param(
    [string]$GamePath = "",
    [switch]$NoPause
)

$ErrorActionPreference = "Stop"

function Test-GamePath([string]$Candidate) {
    if ([string]::IsNullOrWhiteSpace($Candidate)) {
        return $false
    }
    return (Test-Path -LiteralPath (Join-Path $Candidate "StellaSora.exe")) -and
        (Test-Path -LiteralPath (Join-Path $Candidate "game-launcher-config.json"))
}

function Get-HashOrEmpty([string]$FilePath) {
    if (-not (Test-Path -LiteralPath $FilePath -PathType Leaf)) {
        return ""
    }
    return (Get-FileHash -LiteralPath $FilePath -Algorithm SHA256).Hash.ToUpperInvariant()
}

function Move-ToStash([string]$Source, [string]$RelativeName) {
    if (-not (Test-Path -LiteralPath $Source)) {
        return $false
    }
    $destination = Join-Path $script:StashRoot $RelativeName
    New-Item -ItemType Directory -Path (Split-Path -Parent $destination) -Force | Out-Null
    if (Test-Path -LiteralPath $destination) {
        $destination = "$destination.$([guid]::NewGuid().ToString('N'))"
    }
    Move-Item -LiteralPath $Source -Destination $destination
    return $true
}

if (
    $PSBoundParameters.ContainsKey("GamePath") -and
    -not [string]::IsNullOrWhiteSpace($GamePath) -and
    -not (Test-GamePath $GamePath)
) {
    throw "The supplied GamePath is not a StellaSora_EN installation: $GamePath"
}

if (-not (Test-GamePath $GamePath)) {
    $candidates = @(
        "C:\Program Files\YostarGames\StellaSora_EN",
        "C:\YostarGames\StellaSora_EN"
    )
    foreach ($drive in [System.IO.DriveInfo]::GetDrives()) {
        if ($drive.IsReady -and $drive.DriveType -eq "Fixed") {
            $candidates += (Join-Path $drive.RootDirectory.FullName "YostarGames\StellaSora_EN")
            $candidates += (Join-Path $drive.RootDirectory.FullName "Games\YostarGames\StellaSora_EN")
            $candidates += (Join-Path $drive.RootDirectory.FullName "Program Files\YostarGames\StellaSora_EN")
        }
    }
    $GamePath = $candidates | Where-Object { Test-GamePath $_ } | Select-Object -First 1
}

if (-not (Test-GamePath $GamePath)) {
    throw "Game installation was not found. Run again with -GamePath `"D:\...\StellaSora_EN`"."
}

$GamePath = [System.IO.Path]::GetFullPath($GamePath)
if (Get-Process -Name "StellaSora" -ErrorAction SilentlyContinue) {
    throw "Close StellaSora before restoring files."
}

$stateRoot = Join-Path $GamePath ".starcn"
$statePath = Join-Path $stateRoot "installed.json"
if (-not (Test-Path -LiteralPath $statePath)) {
    throw "No Star_CN installed state was found at $statePath"
}

$state = Get-Content -LiteralPath $statePath -Raw -Encoding UTF8 | ConvertFrom-Json
$script:StashRoot = Join-Path $stateRoot ("restored\" + (Get-Date -Format "yyyyMMdd-HHmmss"))
$originalRoot = Join-Path $stateRoot "originals"
$patchedRoot = Join-Path $stateRoot "patched"
$rootPrefix = $GamePath.TrimEnd('\') + '\'
$movedFiles = 0
$restoredOriginals = 0

foreach ($item in $state.files) {
    $relative = ([string]$item.path).Replace('/', '\')
    $isStory = $relative.StartsWith(
        "lua\Game\UI\Avg\_cn\",
        [System.StringComparison]::OrdinalIgnoreCase
    )
    $isManaged = $relative.Equals(
        "StellaSora_Data\data.unity3d",
        [System.StringComparison]::OrdinalIgnoreCase
    ) -or $relative.Equals(
        "Persistent_Store\Tables\data.arcx",
        [System.StringComparison]::OrdinalIgnoreCase
    )
    if (-not $isStory -and -not $isManaged) {
        throw "Unsafe path in installed state: $relative"
    }

    $target = [System.IO.Path]::GetFullPath((Join-Path $GamePath $relative))
    if (-not $target.StartsWith($rootPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
        throw "Path escapes game directory: $relative"
    }

    $patchedHash = ([string]$item.sha256).ToUpperInvariant()
    if ($isStory) {
        if ((Get-HashOrEmpty $target) -eq $patchedHash) {
            if (Move-ToStash $target ("overlay\" + $relative)) {
                $movedFiles++
            }
        } elseif (Test-Path -LiteralPath $target) {
            Write-Warning "Kept modified story file: $relative"
        }
        continue
    }

    $originalHash = ([string]$item.original_sha256).ToUpperInvariant()
    if ([string]::IsNullOrWhiteSpace($originalHash)) {
        throw "Official hash is missing for managed file: $relative"
    }

    $runtime = "$target.starcn-runtime-original"
    $canonicalOriginal = Join-Path $originalRoot $relative
    $cache = Join-Path $patchedRoot $relative
    $targetHash = Get-HashOrEmpty $target
    $runtimeHash = Get-HashOrEmpty $runtime
    $canonicalHash = Get-HashOrEmpty $canonicalOriginal

    if ($targetHash -eq $patchedHash) {
        if (Move-ToStash $target ("active-patch\" + $relative)) {
            $movedFiles++
        }
        $targetHash = ""
    } elseif ($targetHash -ne "" -and $targetHash -ne $originalHash) {
        Write-Warning "Kept unknown file and skipped automatic replacement: $relative"
        continue
    }

    if ($targetHash -ne $originalHash) {
        New-Item -ItemType Directory -Path (Split-Path -Parent $target) -Force | Out-Null
        if ($runtimeHash -eq $originalHash) {
            Move-Item -LiteralPath $runtime -Destination $target
            $restoredOriginals++
        } elseif ($canonicalHash -eq $originalHash) {
            Copy-Item -LiteralPath $canonicalOriginal -Destination $target
            $restoredOriginals++
        } else {
            throw "Verified official backup is missing: $relative"
        }
    } elseif ($runtimeHash -eq $originalHash) {
        if (Move-ToStash $runtime ("duplicate-original\" + $relative + ".starcn-runtime-original")) {
            $movedFiles++
        }
    } elseif ($runtimeHash -ne "") {
        Write-Warning "Kept unknown runtime sidecar: $runtime"
    }

    if ((Get-HashOrEmpty $target) -ne $originalHash) {
        throw "Official restore verification failed: $relative"
    }
    if (Test-Path -LiteralPath $cache) {
        if (Move-ToStash $cache ("patch-cache\" + $relative)) {
            $movedFiles++
        }
    }
}

foreach ($metadata in @("installed.json", "runtime.json")) {
    $metadataPath = Join-Path $stateRoot $metadata
    if (Move-ToStash $metadataPath ("metadata\" + $metadata)) {
        $movedFiles++
    }
}

Write-Host "Official English resources restored successfully." -ForegroundColor Green
Write-Host "Official files restored: $restoredOriginals"
Write-Host "Community files moved to recoverable storage: $movedFiles"
Write-Host "Recovery storage: $script:StashRoot"
Write-Host "Game path: $GamePath"
if (-not $NoPause) {
    Read-Host "Press Enter to close"
}
