<#
.SYNOPSIS
    Collect retained lab evidence for the SiteKings legacy Windows lane.

    PowerShell 2/3 friendly. The script writes an evidence folder that can be
    copied off a Windows 7 SP1 or Server 2008 R2 lab machine after install,
    smoke, watchdog, tray, and peer-healing checks.
#>

param(
    [string]$InstallDir = "",
    [string]$OutDir = "",
    [switch]$ExerciseSelfRescue,
    [switch]$ExercisePeerRecover,
    [string]$ApiKey = ""
)

$ErrorActionPreference = "SilentlyContinue"
$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
try {
    [Net.ServicePointManager]::SecurityProtocol = [Enum]::ToObject([Net.SecurityProtocolType], 3072)
} catch { }

function Escape-JsonString {
    param([string]$Value)
    if ($null -eq $Value) { return "" }
    $s = [string]$Value
    $s = $s.Replace("\", "\\")
    $s = $s.Replace('"', '\"')
    $s = $s.Replace("`r", "\r")
    $s = $s.Replace("`n", "\n")
    $s = $s.Replace("`t", "\t")
    return $s
}

function Resolve-InstallDir {
    if (-not [string]::IsNullOrEmpty($InstallDir)) { return $InstallDir }
    foreach ($candidate in @("$env:ProgramData\SiteKings", "$env:ProgramData\LCL")) {
        if (Test-Path $candidate) { return $candidate }
    }
    return "$env:ProgramData\SiteKings"
}

function Resolve-LocalScript {
    param(
        [string]$Name,
        [string]$InstallRoot
    )
    foreach ($candidate in @(
        (Join-Path $ScriptRoot $Name),
        (Join-Path $InstallRoot $Name),
        (Join-Path (Join-Path $ScriptRoot "client") $Name)
    )) {
        if (Test-Path $candidate) { return $candidate }
    }
    return ""
}

function Resolve-PayloadArtifact {
    param(
        [string]$Name,
        [string]$InstallRoot
    )
    $payloadRoot = Split-Path -Parent $ScriptRoot
    foreach ($candidate in @(
        (Join-Path (Join-Path $payloadRoot "bin") $Name),
        (Join-Path $payloadRoot $Name),
        (Join-Path $ScriptRoot $Name),
        (Join-Path (Join-Path $InstallRoot "bin") $Name),
        (Join-Path $InstallRoot $Name)
    )) {
        if (Test-Path $candidate) { return $candidate }
    }
    return ""
}

function Get-Sha256Hex {
    param([string]$Path)
    if ([string]::IsNullOrEmpty($Path) -or -not (Test-Path $Path)) { return "" }
    $stream = $null
    $sha = $null
    try {
        $sha = [Security.Cryptography.SHA256]::Create()
        $stream = [IO.File]::OpenRead($Path)
        $hash = $sha.ComputeHash($stream)
        return -join ($hash | ForEach-Object { $_.ToString("x2") })
    } catch {
        return ""
    } finally {
        if ($stream -ne $null) { $stream.Close() }
        if ($sha -ne $null) { $sha.Clear() }
    }
}

function Add-TextEvidence {
    param(
        [string]$Name,
        [string]$Text
    )
    $target = Join-Path $OutDir $Name
    [IO.File]::WriteAllText($target, [string]$Text)
    return $target
}

function Copy-IfPresent {
    param(
        [string]$Source,
        [string]$Name
    )
    if (Test-Path $Source) {
        $target = Join-Path $OutDir $Name
        Copy-Item -Path $Source -Destination $target -Force
        return $target
    }
    return ""
}

function Query-Task {
    param([string]$TaskName)
    $out = & schtasks.exe /Query /TN $TaskName 2>&1 | Out-String
    [void](Add-TextEvidence ("task-" + ($TaskName -replace " ", "-") + ".txt") $out)
}

function Write-OperatorContext {
    param([string]$Path)
    $username = ""
    $isElevated = $false
    try {
        $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
        $username = $identity.Name
        $principal = New-Object Security.Principal.WindowsPrincipal($identity)
        $isElevated = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    } catch { }

    $elevatedLiteral = if ($isElevated) { "true" } else { "false" }
    $lines = @(
        "{",
        '  "schema": "legacy-windows-lab-operator-context",',
        '  "is_elevated": ' + $elevatedLiteral + ",",
        '  "username": "' + (Escape-JsonString $username) + '",',
        '  "computer_name": "' + (Escape-JsonString $env:COMPUTERNAME) + '",',
        '  "script_root": "' + (Escape-JsonString $ScriptRoot) + '"',
        "}"
    )
    [IO.File]::WriteAllText($Path, (($lines -join "`r`n") + "`r`n"))
}

function Get-SmokeJsonString {
    param(
        [string]$Path,
        [string]$Name
    )
    if (-not (Test-Path $Path)) { return "" }
    try {
        $raw = [IO.File]::ReadAllText($Path)
        $pattern = '"' + [Regex]::Escape($Name) + '"\s*:\s*"([^"]*)"'
        $match = [Regex]::Match($raw, $pattern, "IgnoreCase")
        if ($match.Success) { return $match.Groups[1].Value }
    } catch { }
    return ""
}

$InstallDir = Resolve-InstallDir
$LogDir = Join-Path $InstallDir "logs"
if ([string]::IsNullOrEmpty($OutDir)) {
    $stamp = Get-Date -Format "yyyyMMdd-HHmmss"
    $OutDir = Join-Path $LogDir ("legacy-windows-lab-evidence-" + $stamp)
}
if (-not (Test-Path $OutDir)) {
    New-Item -ItemType Directory -Path $OutDir -Force | Out-Null
}

$operatorContext = Join-Path $OutDir "operator-context.json"
Write-OperatorContext $operatorContext

$started = Get-Date
$CollectorExit = 0
$SmokeExit = 0
$SmokeScript = Resolve-LocalScript "legacy-windows-lab-smoke.ps1" $InstallDir
$SmokeJson = Join-Path $OutDir "legacy-windows-lab-smoke.json"
if (Test-Path $SmokeScript) {
    $smokeArgs = @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", $SmokeScript, "-OutFile", $SmokeJson)
    if ($ExerciseSelfRescue) { $smokeArgs += "-ExerciseSelfRescue" }
    if ($ExercisePeerRecover) { $smokeArgs += "-ExercisePeerRecover" }
    if (-not [string]::IsNullOrEmpty($ApiKey)) {
        $smokeArgs += "-ApiKey"
        $smokeArgs += $ApiKey
    }
    $smokeOut = & powershell.exe $smokeArgs 2>&1 | Out-String
    $SmokeExit = $LASTEXITCODE
    if ($SmokeExit -ne 0) { $CollectorExit = $SmokeExit }
    [void](Add-TextEvidence "legacy-windows-lab-smoke-output.txt" $smokeOut)
    if (-not (Test-Path $SmokeJson)) { $CollectorExit = 1 }
} else {
    [void](Add-TextEvidence "legacy-windows-lab-smoke-output.txt" ("missing smoke script: " + $SmokeScript))
    $CollectorExit = 2
}

Query-Task "SK Agent"
Query-Task "SK Watchdog"
Query-Task "SK Tray"

$serviceOut = & sc.exe query SKRecoverDaemon 2>&1 | Out-String
[void](Add-TextEvidence "service-SKRecoverDaemon.txt" $serviceOut)

$firewallOut = & netsh advfirewall firewall show rule name="SiteKings Recover Daemon" 2>&1 | Out-String
[void](Add-TextEvidence "firewall-SiteKings-Recover-Daemon.txt" $firewallOut)

$netstatOut = & netstat.exe -ano 2>&1 | Out-String
[void](Add-TextEvidence "netstat-ano.txt" $netstatOut)

$tasklistOut = & tasklist.exe 2>&1 | Out-String
[void](Add-TextEvidence "tasklist.txt" $tasklistOut)

[void](Copy-IfPresent (Join-Path $InstallDir "status.json") "status.json")
[void](Copy-IfPresent (Join-Path $InstallDir "restart-agent.flag") "restart-agent.flag")
[void](Copy-IfPresent (Join-Path $InstallDir "update-flag.txt") "update-flag.txt")
[void](Copy-IfPresent (Join-Path $InstallDir "win10-probe.json") "win10-probe.json")
[void](Copy-IfPresent (Join-Path $InstallDir "win10_upgrade.status.json") "win10_upgrade.status.json")
[void](Copy-IfPresent (Join-Path $InstallDir "win10_upgrade.log") "win10_upgrade.log")
[void](Copy-IfPresent (Join-Path $LogDir "watchdog.log") "watchdog.log")
[void](Copy-IfPresent (Join-Path $LogDir "sk-agent.log") "sk-agent.log")

$finished = Get-Date
$SmokeOsKind = Get-SmokeJsonString $SmokeJson "os_kind"
$BuildManifestPath = Resolve-PayloadArtifact "legacy-windows-first-iteration-build-manifest.json" $InstallDir
$BuildManifestSha256 = Get-Sha256Hex $BuildManifestPath
$files = @()
try {
    foreach ($file in (Get-ChildItem -Path $OutDir)) {
        if (-not $file.PSIsContainer) {
            $files += $file.Name
        }
    }
} catch { }

$manifest = New-Object System.Text.StringBuilder
[void]$manifest.AppendLine("{")
[void]$manifest.AppendLine('  "schema": "legacy-windows-lab-collector",')
[void]$manifest.AppendLine('  "hostname": "' + (Escape-JsonString $env:COMPUTERNAME) + '",')
[void]$manifest.AppendLine('  "os_kind": "' + (Escape-JsonString $SmokeOsKind) + '",')
[void]$manifest.AppendLine('  "install_dir": "' + (Escape-JsonString $InstallDir) + '",')
[void]$manifest.AppendLine('  "out_dir": "' + (Escape-JsonString $OutDir) + '",')
[void]$manifest.AppendLine('  "started": "' + (Escape-JsonString $started.ToString("o")) + '",')
[void]$manifest.AppendLine('  "finished": "' + (Escape-JsonString $finished.ToString("o")) + '",')
[void]$manifest.AppendLine('  "build_manifest_sha256": "' + (Escape-JsonString $BuildManifestSha256) + '",')
[void]$manifest.AppendLine('  "build_manifest_path": "' + (Escape-JsonString $BuildManifestPath) + '",')
[void]$manifest.AppendLine('  "collector_exit": ' + $CollectorExit + ',')
[void]$manifest.AppendLine('  "smoke_exit": ' + $SmokeExit + ',')
[void]$manifest.AppendLine('  "files": [')
for ($i = 0; $i -lt $files.Count; $i++) {
    $comma = ","
    if ($i -eq ($files.Count - 1)) { $comma = "" }
    [void]$manifest.AppendLine('    "' + (Escape-JsonString $files[$i]) + '"' + $comma)
}
[void]$manifest.AppendLine("  ]")
[void]$manifest.AppendLine("}")
[IO.File]::WriteAllText((Join-Path $OutDir "legacy-windows-lab-collector-manifest.json"), $manifest.ToString())

Write-Host ("Legacy Windows lab evidence written to " + $OutDir)
if ($CollectorExit -ne 0) {
    Write-Host ("Collector exit code: " + $CollectorExit)
}
exit $CollectorExit
