param(
    [Parameter(Mandatory = $true)]
    [string]$Archive,

    [Parameter(Mandatory = $true)]
    [string]$Slug,

    [string]$RootDirectory = "C:\codex\catalog-screenshots",

    [string]$InitialKeys = "",

    [ValidateRange(1, 60)]
    [int]$WaitSeconds = 8
)

$ErrorActionPreference = "Stop"
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
$workDirectory = Join-Path $RootDirectory "work\$Slug"
$outputDirectory = Join-Path $RootDirectory "results"
$runner = Join-Path $PSScriptRoot "runScreenshotTask.ps1"
$javaExecutable = "C:\codex\jre\bin\javaw.exe"

function Get-NormalizedName {
    param([string]$Value)
    return ($Value.ToLowerInvariant() -replace '[^a-z0-9]', '')
}

function Get-CandidateScore {
    param(
        [System.IO.FileInfo]$File,
        [string]$ProgramSlug
    )

    $stem = Get-NormalizedName $File.BaseName
    $slugName = Get-NormalizedName $ProgramSlug
    $score = [Math]::Log([Math]::Max($File.Length, 1), 2)

    if ($stem -eq $slugName) {
        $score += 100
    }
    foreach ($token in ($ProgramSlug -split '[-_]')) {
        if ($token.Length -ge 3 -and $stem.Contains((Get-NormalizedName $token))) {
            $score += 15
        }
    }

    $relativePath = $File.FullName.Substring($workDirectory.Length).TrimStart('\')
    $score -= ([regex]::Matches($relativePath, '\\').Count * 2)

    if ($File.BaseName -match '^(unins|uninstall|unwise|remove|register|regsvr)') {
        $score -= 200
    }
    if ($File.BaseName -match '(setup|install|autorun|update|updater)') {
        $score -= 50
    }

    return $score
}

function Stop-WorkDirectoryProcesses {
    $directoryPrefix = $workDirectory.TrimEnd('\') + '\'

    for ($attempt = 0; $attempt -lt 3; $attempt++) {
        foreach ($item in Get-CimInstance Win32_Process -ErrorAction SilentlyContinue) {
            if ($item.ExecutablePath -and $item.ExecutablePath.StartsWith($directoryPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
                taskkill.exe /PID $item.ProcessId /T /F 2>$null | Out-Null
            }
        }
        Start-Sleep -Milliseconds 300
    }
}

New-Item -ItemType Directory -Force -Path $workDirectory, $outputDirectory | Out-Null

try {
    Expand-Archive -LiteralPath $Archive -DestinationPath $workDirectory -Force

    $candidates = @(
        Get-ChildItem -LiteralPath $workDirectory -Recurse -File -Filter "*.exe" |
            ForEach-Object {
                [pscustomobject]@{
                    File = $_
                    Score = Get-CandidateScore -File $_ -ProgramSlug $Slug
                }
            } |
            Sort-Object @{ Expression = "Score"; Descending = $true }, @{ Expression = { $_.File.Length }; Descending = $true }
    )

    $arguments = ""
    $executable = $null
    $workingDirectory = $null
    if ($candidates.Count -gt 0) {
        $selected = $candidates[0].File
        $executable = $selected.FullName
        $workingDirectory = $selected.DirectoryName
    }
    else {
        $candidates = @(
            Get-ChildItem -LiteralPath $workDirectory -Recurse -File -Filter "*.jar" |
                Where-Object { $_.DirectoryName -notmatch '\\lib($|\\)' } |
                ForEach-Object {
                    [pscustomobject]@{
                        File = $_
                        Score = Get-CandidateScore -File $_ -ProgramSlug $Slug
                    }
                } |
                Sort-Object @{ Expression = "Score"; Descending = $true }, @{ Expression = { $_.File.Length }; Descending = $true }
        )
        if ($candidates.Count -eq 0 -or -not (Test-Path -LiteralPath $javaExecutable)) {
            [pscustomobject]@{
                success = $false
                reason = "no-executable"
                candidates = @()
            } | ConvertTo-Json -Depth 5 -Compress
            exit 1
        }
        $selected = $candidates[0].File
        $executable = $javaExecutable
        $workingDirectory = $selected.DirectoryName
        $arguments = "-jar `"$($selected.FullName)`""
    }

    $installer = $selected.Extension -ieq ".exe" -and $selected.BaseName -match '(setup|install|autorun)'
    if ($installer) {
        [pscustomobject]@{
            success = $false
            reason = "installer-only"
            selected = $selected.FullName.Substring($workDirectory.Length).TrimStart('\')
            installer = $true
            candidates = @($candidates | Select-Object -First 10 | ForEach-Object {
                [pscustomobject]@{
                    path = $_.File.FullName.Substring($workDirectory.Length).TrimStart('\')
                    size = $_.File.Length
                    score = [Math]::Round($_.Score, 2)
                }
            })
        } | ConvertTo-Json -Depth 5 -Compress
        exit 1
    }
    $winapp = "C:\codex\winapp\winapp.exe"
    $screenshotTool = if (Test-Path -LiteralPath $winapp) {
        $winapp
    }
    else {
        Join-Path $RootDirectory "nircmd.exe"
    }
    $runnerParameters = @{
        Executable = $executable
        Name = $Slug
        OutputDirectory = $outputDirectory
        WaitSeconds = $WaitSeconds
        Arguments = $arguments
        WorkingDirectory = $workingDirectory
        InitialKeys = $InitialKeys
        ScreenshotTool = $screenshotTool
    }
    $rawResult = & $runner @runnerParameters
    $result = $rawResult | ConvertFrom-Json

    [pscustomobject]@{
        success = [bool]$result.success
        reason = $result.reason
        title = $result.title
        error = $result.error
        selected = $selected.FullName.Substring($workDirectory.Length).TrimStart('\')
        installer = $installer
        width = $result.width
        height = $result.height
        windows = $result.windows
        candidates = @($candidates | Select-Object -First 10 | ForEach-Object {
            [pscustomobject]@{
                path = $_.File.FullName.Substring($workDirectory.Length).TrimStart('\')
                size = $_.File.Length
                score = [Math]::Round($_.Score, 2)
            }
        })
    } | ConvertTo-Json -Depth 7 -Compress
}
catch {
    [pscustomobject]@{
        success = $false
        reason = "error"
        error = $_.Exception.Message
        candidates = @()
    } | ConvertTo-Json -Depth 5 -Compress
    exit 1
}
finally {
    Stop-WorkDirectoryProcesses
    Remove-Item -LiteralPath $workDirectory -Recurse -Force -ErrorAction SilentlyContinue
    Remove-Item -LiteralPath $Archive -Force -ErrorAction SilentlyContinue
}
