<#
.SYNOPSIS
    SiteKings Agent Watchdog for Windows 7 / Server 2008 R2 era hosts.

    This helper avoids the newer task module and newer hashing cmdlets so
    legacy endpoints can still self-correct using inbox sc.exe, schtasks.exe,
    and .NET crypto primitives.
#>

$ErrorActionPreference = "SilentlyContinue"
try {
    [Net.ServicePointManager]::SecurityProtocol = [Enum]::ToObject([Net.SecurityProtocolType], 3072)
} catch { }

$DefaultLogFile = "C:\ProgramData\SiteKings\logs\watchdog.log"

function Get-InstallDir {
    $candidates = @("$env:ProgramData\SiteKings", "$env:ProgramData\LCL")
    foreach ($candidate in $candidates) {
        if (Test-Path (Join-Path $candidate "config.json")) {
            return $candidate
        }
    }
    return "$env:ProgramData\SiteKings"
}

$InstallDir = Get-InstallDir
$ConfigFile = Join-Path $InstallDir "config.json"
$AgentPath = Join-Path $InstallDir "sk-agent.exe"
$AgentScript = Join-Path $InstallDir "sk-agent-legacy.ps1"
$RecoverPath = Join-Path $InstallDir "sk-recover.exe"
$TrayPath = Join-Path $InstallDir "sk-tray.exe"
$LegacyTrayPath = Join-Path $InstallDir "sk-tray-legacy.ps1"
$StatusFile = Join-Path $InstallDir "status.json"
$RestartFlag = Join-Path $InstallDir "restart-agent.flag"
$SyncFlag = Join-Path $InstallDir "sync-now.flag"
$LogDir = Join-Path $InstallDir "logs"
$LogFile = Join-Path $LogDir "watchdog.log"

if (-not (Test-Path $InstallDir)) { New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null }
if (-not (Test-Path $LogDir)) { New-Item -ItemType Directory -Path $LogDir -Force | Out-Null }

function Write-Log {
    param([string]$Message)
    $line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') $Message"
    $line | Out-File -FilePath $LogFile -Append -Encoding UTF8 -Force
}

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

function Get-FileSha256 {
    param([string]$Path)
    if (-not (Test-Path $Path)) { return "" }
    $stream = [IO.File]::OpenRead($Path)
    try {
        $sha = New-Object System.Security.Cryptography.SHA256Managed
        $hash = $sha.ComputeHash($stream)
        return ([BitConverter]::ToString($hash) -replace "-", "").ToLowerInvariant()
    } finally {
        $stream.Close()
    }
}

function Invoke-Download {
    param([string]$Url, [string]$OutFile)
    $client = New-Object System.Net.WebClient
    $client.Headers.Add("User-Agent", "SK-Watchdog-Legacy/1.0")
    $apiKey = Get-ConfigValue "api_key"
    if ($apiKey) { $client.Headers.Add("X-API-Key", $apiKey) }
    $client.DownloadFile($Url, $OutFile)
}

function Ensure-RecoverDaemon {
    if (-not (Test-Path $RecoverPath)) {
        $serverUrl = (Get-ConfigValue "server_url").TrimEnd("/")
        if ($serverUrl) {
            try {
                Write-Log "SKRecoverDaemon binary missing; downloading legacy recover artifact"
                Invoke-Download "$serverUrl/api/client/download/sk-recover-legacy.exe" "$InstallDir\sk-recover-new.exe"
                if ((Test-Path "$InstallDir\sk-recover-new.exe") -and ((Get-Item "$InstallDir\sk-recover-new.exe").Length -gt 50000)) {
                    Move-Item "$InstallDir\sk-recover-new.exe" $RecoverPath -Force
                    Write-Log "Downloaded legacy SKRecoverDaemon binary"
                }
            } catch {
                Write-Log "SKRecoverDaemon download failed: $($_.Exception.Message)"
                Remove-Item "$InstallDir\sk-recover-new.exe" -Force
            }
        }
    }

    if (-not (Test-Path $RecoverPath)) {
        Write-Log "SKRecoverDaemon unavailable"
        return
    }

    $svc = Get-Service -Name "SKRecoverDaemon"
    if (-not $svc) {
        Write-Log "Installing SKRecoverDaemon service"
        sc.exe stop SKRecoverDaemon | Out-Null
        sc.exe delete SKRecoverDaemon | Out-Null
        sc.exe create SKRecoverDaemon binPath= "`"$RecoverPath`"" start= auto DisplayName= "SiteKings Recover Daemon" | Out-Null
        sc.exe description SKRecoverDaemon "SiteKings RMM peer recovery daemon" | Out-Null
        sc.exe failure SKRecoverDaemon reset= 86400 actions= restart/5000/restart/30000/restart/60000 | Out-Null
    }
    netsh advfirewall firewall delete rule name="SiteKings Recover Daemon" 2>$null | Out-Null
    netsh advfirewall firewall add rule name="SiteKings Recover Daemon" dir=in action=allow protocol=TCP localport=47778 program="$RecoverPath" enable=yes | Out-Null

    $svc = Get-Service -Name "SKRecoverDaemon"
    if ($svc -and $svc.Status -ne "Running") {
        Write-Log "Starting SKRecoverDaemon"
        sc.exe start SKRecoverDaemon | Out-Null
    }
}

function Ensure-AgentTask {
    if (Test-Path $AgentScript) {
        $taskAction = "powershell.exe -NoProfile -ExecutionPolicy Bypass -File `"$AgentScript`""
    } elseif (Test-Path $AgentPath) {
        $taskAction = "`"$AgentPath`" debug"
    } else {
        return
    }
    schtasks.exe /Create /TN "SK Agent" /SC ONSTART /RU SYSTEM /RL HIGHEST /TR $taskAction /F | Out-Null
}

function Ensure-WatchdogTask {
    $thisScript = $MyInvocation.MyCommand.Path
    if (-not $thisScript) { $thisScript = Join-Path $InstallDir "sk-watchdog-legacy.ps1" }
    $taskAction = "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File `"$thisScript`""
    schtasks.exe /Create /TN "SK Watchdog" /SC MINUTE /MO 5 /RU SYSTEM /RL HIGHEST /TR $taskAction /F | Out-Null
}

function Ensure-TrayTask {
    $taskAction = ""
    if (Test-Path $TrayPath) {
        $taskAction = "`"$TrayPath`""
    } elseif (Test-Path $LegacyTrayPath) {
        $taskAction = "powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$LegacyTrayPath`""
    }
    if ($taskAction) {
        schtasks.exe /Create /TN "SK Tray" /SC ONLOGON /TR $taskAction /F | Out-Null
    }
}

function Start-Agent {
    param([string]$Reason)
    Write-Log "Starting agent: $Reason"
    Ensure-RecoverDaemon
    Ensure-AgentTask

    if (Test-Path $AgentScript) {
        Start-LegacyAgent
        return
    }

    $svc = Get-Service -Name "SKSyncAgent"
    if (-not $svc -and (Test-Path $AgentPath)) {
        & $AgentPath install | Out-Null
        Start-Sleep -Seconds 2
        $svc = Get-Service -Name "SKSyncAgent"
    }
    if ($svc) {
        sc.exe failure SKSyncAgent reset= 86400 actions= restart/5000/restart/30000/restart/60000 | Out-Null
        sc.exe start SKSyncAgent | Out-Null
        Start-Sleep -Seconds 5
    }
    if (-not (Get-Process -Name "sk-agent")) {
        schtasks.exe /Run /TN "SK Agent" | Out-Null
        Start-Sleep -Seconds 5
    }
}

function Test-LegacyAgentRunning {
    $matches = Get-WmiObject Win32_Process -Filter "Name='powershell.exe'" | Where-Object { $_.CommandLine -like "*sk-agent-legacy.ps1*" }
    return (@($matches).Count -gt 0)
}

function Test-LegacyTrayRunning {
    $native = Get-WmiObject Win32_Process -Filter "Name='sk-tray.exe'"
    $legacy = Get-WmiObject Win32_Process -Filter "Name='powershell.exe'" | Where-Object { $_.CommandLine -like "*sk-tray-legacy.ps1*" }
    return ((@($native).Count + @($legacy).Count) -gt 0)
}

function Start-LegacyAgent {
    if (-not (Test-Path $AgentScript)) { return }
    Write-Log "Starting legacy PowerShell agent"
    schtasks.exe /Run /TN "SK Agent" | Out-Null
    Start-Sleep -Seconds 5
    if (-not (Test-LegacyAgentRunning)) {
        Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$AgentScript`"" -WindowStyle Hidden | Out-Null
        Start-Sleep -Seconds 5
    }
}

function Start-LegacyTray {
    if ((-not (Test-Path $TrayPath)) -and (-not (Test-Path $LegacyTrayPath))) { return }
    Write-Log "SK Tray missing; attempting restart"
    Ensure-TrayTask
    schtasks.exe /Run /TN "SK Tray" | Out-Null
    Start-Sleep -Seconds 5
}

function Test-StatusFresh {
    if (-not (Test-Path $StatusFile)) { return $false }
    $age = ((Get-Date) - (Get-Item $StatusFile).LastWriteTime).TotalMinutes
    return ($age -lt 15)
}

try {
    Write-Log "Legacy watchdog check"
    Get-FileSha256 $AgentPath | Out-Null
    Ensure-RecoverDaemon
    Ensure-WatchdogTask
    Ensure-TrayTask
    if (-not (Test-LegacyTrayRunning)) {
        Start-LegacyTray
    }

    $agent = $null
    $legacyAgent = $false
    if (Test-Path $AgentScript) {
        $legacyAgent = Test-LegacyAgentRunning
    } else {
        $agent = Get-Process -Name "sk-agent"
    }
    if ((Test-Path $RestartFlag) -or ((Test-Path $AgentScript) -and (-not $legacyAgent)) -or ((-not (Test-Path $AgentScript)) -and (-not $agent)) -or (-not (Test-StatusFresh))) {
        if (Test-Path $RestartFlag) { Remove-Item $RestartFlag -Force }
        if (Test-Path $SyncFlag) { Write-Log "sync-now.flag present" }
        if ($agent) { $agent | Stop-Process -Force }
        sc.exe stop SKSyncAgent | Out-Null
        Start-Sleep -Seconds 3
        Start-Agent "missing, stale, or requested"
    } else {
        Write-Log "Agent healthy"
    }
} catch {
    Write-Log "Watchdog error: $($_.Exception.Message)"
}
