# test-reconnect.ps1
# Windows reconnection tests for agnt daemon

param(
    [string]$AgntPath = "agnt.exe",
    [string]$SocketPath = "$env:TEMP\agnt-test-reconnect.sock"
)

$ErrorActionPreference = "Stop"

# Colors for output
function Write-TestHeader($message) {
    Write-Host "`n=== $message ===" -ForegroundColor Cyan
}

function Write-Success($message) {
    Write-Host "[OK] $message" -ForegroundColor Green
}

function Write-Failure($message) {
    Write-Host "[FAIL] $message" -ForegroundColor Red
}

function Write-Info($message) {
    Write-Host "[INFO] $message" -ForegroundColor Yellow
}

function Convert-AgntInfo($output) {
    $text = ($output | Out-String).Trim()
    if ($text -notmatch "Daemon v(?<version>\S+)") {
        throw "agnt command did not produce daemon info: $text"
    }

    $version = $Matches.version
    $uptime = "unknown"
    if ($text -match "(?m)^Uptime:\s*(?<uptime>.+)$") {
        $uptime = $Matches.uptime.Trim()
    }

    return [pscustomobject]@{
        version = $version
        uptime = $uptime
        raw = $text
    }
}

function Invoke-AgntInfo {
    $output = & $AgntPath daemon info --socket $SocketPath 2>&1
    if ($LASTEXITCODE -ne 0) {
        throw "daemon info failed: $($output | Out-String)"
    }
    return Convert-AgntInfo $output
}

# Helper: Wait for daemon to be ready
function Wait-DaemonReady {
    param([int]$TimeoutSeconds = 5)

    $deadline = (Get-Date).AddSeconds($TimeoutSeconds)
    while ((Get-Date) -lt $deadline) {
        try {
            $result = & $AgntPath daemon status --socket $SocketPath 2>$null
            if ($LASTEXITCODE -eq 0) {
                return $true
            }
        } catch {}
        Start-Sleep -Milliseconds 100
    }
    return $false
}

# Helper: Wait for daemon to stop
function Wait-DaemonStopped {
    param([int]$TimeoutSeconds = 5)

    $deadline = (Get-Date).AddSeconds($TimeoutSeconds)
    while ((Get-Date) -lt $deadline) {
        try {
            & $AgntPath daemon status --socket $SocketPath 2>$null
            if ($LASTEXITCODE -ne 0) {
                return $true
            }
        } catch {
            return $true
        }
        Start-Sleep -Milliseconds 100
    }
    return $false
}

# Helper: Start daemon
function Start-TestDaemon {
    Write-Info "Starting daemon on socket: $SocketPath"

    $job = Start-Job -ScriptBlock {
        param($agnt, $sock)
        & $agnt daemon start --socket $sock 2>&1
    } -ArgumentList $AgntPath, $SocketPath

    if (-not (Wait-DaemonReady -TimeoutSeconds 5)) {
        Stop-Job $job -ErrorAction SilentlyContinue
        Remove-Job $job -ErrorAction SilentlyContinue
        throw "Daemon failed to start"
    }

    return $job
}

# Helper: Stop daemon
function Stop-TestDaemon {
    param($job)

    try {
        & $AgntPath daemon stop --socket $SocketPath 2>$null
    } catch {}

    Start-Sleep -Seconds 1

    if ($job) {
        Stop-Job $job -ErrorAction SilentlyContinue
        Remove-Job $job -ErrorAction SilentlyContinue
    }
}

# Helper: Kill daemon process
function Kill-Daemon {
    $processes = Get-Process -Name "agnt*" -ErrorAction SilentlyContinue
    foreach ($proc in $processes) {
        Write-Info "Killing daemon process: $($proc.Id)"
        Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
    }

    # Wait for processes to be gone
    Start-Sleep -Milliseconds 500
}

# Cleanup before tests
function Cleanup {
    Write-Info "Cleaning up any existing daemon..."

    # Try graceful stop first
    try {
        & $AgntPath daemon stop --socket $SocketPath 2>$null
    } catch {}

    # Kill any remaining processes
    Kill-Daemon

    # Remove socket file
    if (Test-Path $SocketPath) {
        Remove-Item $SocketPath -Force -ErrorAction SilentlyContinue
    }

    Start-Sleep -Seconds 1
}

# Test 1: Kill daemon and verify client reconnects
function Test-KillDaemonReconnect {
    Write-TestHeader "Test 1: Kill daemon and verify client reconnects"

    try {
        # Start daemon
        $daemonJob = Start-TestDaemon
        Write-Success "Daemon started"

        # Get daemon info to verify connection
        $info = Invoke-AgntInfo
        Write-Success "Connected to daemon v$($info.version)"

        # Kill the daemon process (simulate crash)
        Write-Info "Killing daemon process to simulate crash..."
        Kill-Daemon

        if (-not (Wait-DaemonStopped -TimeoutSeconds 3)) {
            throw "Daemon did not stop after kill"
        }
        Write-Success "Daemon process killed"

        # Restart daemon and verify the client can reconnect.
        Write-Info "Restarting daemon and reconnecting..."
        $daemonJob = Start-TestDaemon
        $info2 = Invoke-AgntInfo

        Write-Success "Client reconnected to restarted daemon"
        Write-Success "New daemon v$($info2.version) running"

        return $true
    }
    catch {
        Write-Failure $_.Exception.Message
        return $false
    }
    finally {
        Cleanup
    }
}

# Test 2: Graceful daemon restart with client recovery
function Test-GracefulRestart {
    Write-TestHeader "Test 2: Graceful daemon restart with client recovery"

    try {
        # Start daemon
        $daemonJob = Start-TestDaemon
        Write-Success "Daemon started"

        # Get initial info
        $info = Invoke-AgntInfo
        Write-Success "Connected to daemon v$($info.version), uptime: $($info.uptime)"

        # Graceful stop
        Write-Info "Stopping daemon gracefully..."
        & $AgntPath daemon stop --socket $SocketPath 2>&1

        if (-not (Wait-DaemonStopped -TimeoutSeconds 5)) {
            throw "Daemon did not stop gracefully"
        }
        Write-Success "Daemon stopped gracefully"

        # Restart daemon and verify the client can reconnect.
        Write-Info "Restarting daemon and reconnecting..."
        $daemonJob = Start-TestDaemon
        $info2 = Invoke-AgntInfo

        Write-Success "Client reconnected to restarted daemon"
        Write-Success "New daemon v$($info2.version) running"

        return $true
    }
    catch {
        Write-Failure $_.Exception.Message
        return $false
    }
    finally {
        Cleanup
    }
}

# Test 3: Socket file deletion with recovery
function Test-SocketDeletion {
    Write-TestHeader "Test 3: Socket file deletion with recovery"

    try {
        # Start daemon
        $daemonJob = Start-TestDaemon
        Write-Success "Daemon started"

        # Verify connection
        $info = Invoke-AgntInfo
        Write-Success "Connected to daemon v$($info.version)"

        # Delete socket file while daemon running
        Write-Info "Deleting socket file while daemon is running..."
        if (Test-Path $SocketPath) {
            Remove-Item $SocketPath -Force
            Write-Success "Socket file deleted"
        }

        # Restart daemon and verify the client can reconnect after losing the socket.
        Write-Info "Restarting daemon after socket deletion..."
        Stop-TestDaemon $daemonJob
        $daemonJob = Start-TestDaemon
        $info2 = Invoke-AgntInfo

        Write-Success "Client reconnected after socket recovery"
        Write-Success "New daemon v$($info2.version) running"

        return $true
    }
    catch {
        Write-Failure $_.Exception.Message
        return $false
    }
    finally {
        Cleanup
    }
}

# Test 4: Multiple rapid reconnections
function Test-RapidReconnections {
    Write-TestHeader "Test 4: Multiple rapid reconnections"

    try {
        $successCount = 0
        $iterations = 5

        for ($i = 1; $i -le $iterations; $i++) {
            Write-Info "Iteration $i of $iterations"

            # Start daemon
            $daemonJob = Start-TestDaemon

            # Use client
            $info = Invoke-AgntInfo
            Write-Success "Connected on iteration $i"
            $successCount++

            # Stop daemon
            Stop-TestDaemon $daemonJob

            Start-Sleep -Milliseconds 200
        }

        if ($successCount -eq $iterations) {
            Write-Success "All $iterations iterations succeeded"
            return $true
        }
        else {
            Write-Failure "Only $successCount of $iterations iterations succeeded"
            return $false
        }
    }
    catch {
        Write-Failure $_.Exception.Message
        return $false
    }
    finally {
        Cleanup
    }
}

# Test 5: Client operations survive daemon restart
function Test-ClientOperationsDuringRestart {
    Write-TestHeader "Test 5: Client operations survive daemon restart"

    try {
        # Start daemon
        $daemonJob = Start-TestDaemon
        Write-Success "Daemon started"

        # Start a long-running process via client
        Write-Info "Starting a background process..."
        & $AgntPath --socket $SocketPath 2>&1 | Out-Null

        # Kill daemon
        Write-Info "Killing daemon while process running..."
        Kill-Daemon

        if (-not (Wait-DaemonStopped -TimeoutSeconds 3)) {
            throw "Daemon did not stop"
        }

        # Restart daemon and verify the client can reconnect.
        Write-Info "Restarting daemon and querying daemon info..."
        $daemonJob = Start-TestDaemon
        $result = Invoke-AgntInfo

        Write-Success "Client reconnected after daemon restart"
        Write-Success "Daemon v$($result.version) running"

        return $true
    }
    catch {
        Write-Failure $_.Exception.Message
        return $false
    }
    finally {
        Cleanup
    }
}

# Main execution
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "   agnt Windows Reconnection Tests" -ForegroundColor Cyan
Write-Host "========================================`n" -ForegroundColor Cyan

Write-Info "Using agnt binary: $AgntPath"
Write-Info "Test socket path: $SocketPath"

# Verify agnt exists
if (-not (Test-Path $AgntPath)) {
    Write-Failure "agnt binary not found at: $AgntPath"
    Write-Info "Build it with: go build -o agnt.exe ./cmd/agnt/"
    exit 1
}

# Initial cleanup
Cleanup

# Run tests
$results = @()
$results += @{ Name = "Kill daemon reconnect"; Passed = (Test-KillDaemonReconnect) }
$results += @{ Name = "Graceful restart"; Passed = (Test-GracefulRestart) }
$results += @{ Name = "Socket deletion recovery"; Passed = (Test-SocketDeletion) }
$results += @{ Name = "Rapid reconnections"; Passed = (Test-RapidReconnections) }
$results += @{ Name = "Operations during restart"; Passed = (Test-ClientOperationsDuringRestart) }

# Final cleanup
Cleanup

# Summary
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "           Test Summary" -ForegroundColor Cyan
Write-Host "========================================`n" -ForegroundColor Cyan

$passCount = 0
$failCount = 0

foreach ($result in $results) {
    if ($result.Passed) {
        Write-Success $result.Name
        $passCount++
    }
    else {
        Write-Failure $result.Name
        $failCount++
    }
}

Write-Host "`nTotal: $($results.Count) tests" -ForegroundColor White
Write-Host "Passed: $passCount" -ForegroundColor Green
Write-Host "Failed: $failCount" -ForegroundColor $(if ($failCount -gt 0) { "Red" } else { "Green" })

if ($failCount -gt 0) {
    exit 1
}
else {
    Write-Host "`nAll tests passed!" -ForegroundColor Green
    exit 0
}
