# RepairIQ Sync Agent - Scheduled Task Updater v1.3.5
# Runs as SYSTEM with permission to stop/start services
# Triggered by the service when an update is available
#
# Reads update info from: C:\RepairIQ-EMS\updates\update.json
# Expects ZIP at: C:\RepairIQ-EMS\updates\update.zip

$ErrorActionPreference = "Stop"

# Configuration
$ServiceName = "RepairIQSyncAgent"
$InstallDir = "C:\Program Files\RepairIQ"
$BackupDir = "C:\Program Files\RepairIQ.backup"
$UpdatesDir = "C:\RepairIQ-EMS\updates"
$LogDir = "C:\RepairIQ-EMS\logs"
$LogFile = Join-Path $LogDir "updater-task.log"
$LockFile = Join-Path $UpdatesDir "update.lock"
$UpdateInfoFile = Join-Path $UpdatesDir "update.json"
$UpdateZipFile = Join-Path $UpdatesDir "update.zip"

# Ensure directories exist
if (-not (Test-Path $LogDir)) {
    New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
}
if (-not (Test-Path $UpdatesDir)) {
    New-Item -ItemType Directory -Path $UpdatesDir -Force | Out-Null
}

function Write-Log {
    param([string]$Message, [string]$Level = "INFO")
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logLine = "[$timestamp] [$Level] $Message"
    Add-Content -Path $LogFile -Value $logLine
    Write-Host $logLine
}

function Test-FileHash {
    param([string]$FilePath, [string]$ExpectedHash)
    $actualHash = (Get-FileHash -Path $FilePath -Algorithm SHA256).Hash
    Write-Log "Expected: $ExpectedHash"
    Write-Log "Actual:   $actualHash"
    return $actualHash -eq $ExpectedHash.ToUpper()
}

function Cleanup-UpdateFiles {
    Remove-Item $LockFile -Force -ErrorAction SilentlyContinue
    Remove-Item $UpdateInfoFile -Force -ErrorAction SilentlyContinue
    Remove-Item $UpdateZipFile -Force -ErrorAction SilentlyContinue
}

try {
    Write-Log "====================================="
    Write-Log "REPAIRIQ UPDATER TASK STARTED"
    Write-Log "====================================="
    Write-Log "Running as: $env:USERNAME"
    Write-Log "====================================="

    # Check for update.json
    if (-not (Test-Path $UpdateInfoFile)) {
        Write-Log "No update.json found. Nothing to do." "WARN"
        exit 0
    }

    # Read update info
    $updateInfo = Get-Content $UpdateInfoFile -Raw | ConvertFrom-Json
    $expectedVersion = $updateInfo.version
    $expectedHash = $updateInfo.sha256
    $downloadUrl = $updateInfo.downloadUrl

    Write-Log "Update info:"
    Write-Log "  Version: $expectedVersion"
    Write-Log "  SHA256: $expectedHash"
    Write-Log "  URL: $downloadUrl"

    # Check for ZIP file
    if (-not (Test-Path $UpdateZipFile)) {
        Write-Log "No update.zip found. Nothing to do." "WARN"
        Cleanup-UpdateFiles
        exit 0
    }

    # Create lock file to prevent concurrent updates
    if (Test-Path $LockFile) {
        $lockAge = (Get-Date) - (Get-Item $LockFile).LastWriteTime
        if ($lockAge.TotalMinutes -lt 10) {
            Write-Log "Update already in progress (lock file exists). Exiting." "WARN"
            exit 0
        }
        Write-Log "Stale lock file found (age: $($lockAge.TotalMinutes) min), removing..." "WARN"
        Remove-Item $LockFile -Force
    }
    Set-Content -Path $LockFile -Value (Get-Date).ToString()

    # Step 1: Verify ZIP hash
    Write-Log "[1/8] Verifying ZIP integrity..."
    if (-not (Test-FileHash -FilePath $UpdateZipFile -ExpectedHash $expectedHash)) {
        throw "SHA256 hash mismatch! File may be corrupted or tampered."
    }
    Write-Log "       SHA256 verified successfully"

    # Step 2: Stop the service
    Write-Log "[2/8] Stopping service '$ServiceName'..."
    $service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
    if ($service) {
        if ($service.Status -eq "Running") {
            Stop-Service -Name $ServiceName -Force
            # Wait for service to fully stop (up to 30 seconds)
            $timeout = 30
            while ($timeout -gt 0) {
                $svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
                if ($svc.Status -eq "Stopped") {
                    break
                }
                Start-Sleep -Seconds 1
                $timeout--
            }
            $svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
            if ($svc.Status -ne "Stopped") {
                Write-Log "       Service didn't stop gracefully, forcing process kill..." "WARN"
                # Get the process ID and kill it
                $wmiSvc = Get-WmiObject Win32_Service | Where-Object { $_.Name -eq $ServiceName }
                if ($wmiSvc -and $wmiSvc.ProcessId -and $wmiSvc.ProcessId -ne 0) {
                    Stop-Process -Id $wmiSvc.ProcessId -Force -ErrorAction SilentlyContinue
                }
                Start-Sleep -Seconds 3
            }
        }
        Write-Log "       Service stopped"
    } else {
        Write-Log "       Service not found (fresh install scenario)" "WARN"
    }

    # Step 3: Backup current installation
    Write-Log "[3/8] Backing up current installation..."
    if (Test-Path $BackupDir) {
        Remove-Item $BackupDir -Recurse -Force
    }
    if (Test-Path $InstallDir) {
        # Backup critical files only (for speed)
        New-Item -ItemType Directory -Path $BackupDir -Force | Out-Null
        if (Test-Path "$InstallDir\dist") {
            Copy-Item "$InstallDir\dist" "$BackupDir\dist" -Recurse
        }
        if (Test-Path "$InstallDir\package.json") {
            Copy-Item "$InstallDir\package.json" "$BackupDir\package.json"
        }
        Write-Log "       Backup created at $BackupDir"
    } else {
        Write-Log "       No existing installation to backup"
    }

    # Step 4: Extract new version
    Write-Log "[4/8] Extracting update package..."
    $tempExtract = Join-Path $env:TEMP "RepairIQ-Update-$(Get-Date -Format 'yyyyMMddHHmmss')"
    Expand-Archive -Path $UpdateZipFile -DestinationPath $tempExtract -Force
    Write-Log "       Extracted to $tempExtract"

    # Find the actual content directory (ZIP may have nested folder)
    $sourceDir = $tempExtract
    $distPath = Join-Path $sourceDir "dist"
    if (-not (Test-Path $distPath)) {
        # Check one level deeper (e.g., sync-agent/dist)
        $subdirs = Get-ChildItem -Path $tempExtract -Directory
        foreach ($subdir in $subdirs) {
            $distPath = Join-Path $subdir.FullName "dist"
            if (Test-Path $distPath) {
                $sourceDir = $subdir.FullName
                Write-Log "       Found nested structure: $($subdir.Name)"
                break
            }
        }
    }

    if (-not (Test-Path (Join-Path $sourceDir "dist"))) {
        throw "Invalid ZIP structure: dist folder not found in archive"
    }
    Write-Log "       Source directory: $sourceDir"

    # Step 5: Update files
    Write-Log "[5/8] Copying new files..."

    # Ensure install directory exists
    if (-not (Test-Path $InstallDir)) {
        New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
        Write-Log "       Created install directory"
    }

    # Remove and replace dist folder
    if (Test-Path "$InstallDir\dist") {
        Remove-Item "$InstallDir\dist" -Recurse -Force
    }
    Copy-Item "$sourceDir\dist" "$InstallDir\dist" -Recurse
    Write-Log "       Updated: dist\"

    # Update node_modules if present in package
    if (Test-Path "$sourceDir\node_modules") {
        if (Test-Path "$InstallDir\node_modules") {
            Remove-Item "$InstallDir\node_modules" -Recurse -Force
        }
        Copy-Item "$sourceDir\node_modules" "$InstallDir\node_modules" -Recurse
        Write-Log "       Updated: node_modules\"
    }

    # Update package.json
    if (Test-Path "$sourceDir\package.json") {
        Copy-Item "$sourceDir\package.json" "$InstallDir\package.json" -Force
        Write-Log "       Updated: package.json"
    }

    # Update PowerShell updater script (self-update capability)
    if (Test-Path "$sourceDir\updater-task.ps1") {
        Copy-Item "$sourceDir\updater-task.ps1" "$InstallDir\updater-task.ps1" -Force
        Write-Log "       Updated: updater-task.ps1"
    }

    # Update service scripts
    if (Test-Path "$sourceDir\service-install.js") {
        Copy-Item "$sourceDir\service-install.js" "$InstallDir\service-install.js" -Force
        Write-Log "       Updated: service-install.js"
    }
    if (Test-Path "$sourceDir\service-uninstall.js") {
        Copy-Item "$sourceDir\service-uninstall.js" "$InstallDir\service-uninstall.js" -Force
        Write-Log "       Updated: service-uninstall.js"
    }

    # Cleanup temp extraction
    Remove-Item $tempExtract -Recurse -Force -ErrorAction SilentlyContinue
    Write-Log "       Cleaned up temp files"

    # Step 6: Reinstall Windows Service (required after file update)
    Write-Log "[6/8] Reinstalling Windows Service..."

    # Change to install directory
    Push-Location $InstallDir

    # Uninstall old service
    Write-Log "       Uninstalling old service wrapper..."
    & node service-uninstall.js 2>&1 | ForEach-Object { Write-Log "       $_" }
    Start-Sleep -Seconds 3

    # Delete service if still exists (fallback)
    sc.exe delete $ServiceName 2>&1 | Out-Null
    Start-Sleep -Seconds 2

    # Install new service
    Write-Log "       Installing new service wrapper..."
    & node service-install.js 2>&1 | ForEach-Object { Write-Log "       $_" }
    Start-Sleep -Seconds 3

    Pop-Location
    Write-Log "       Service reinstalled"

    # Step 7: Verify service is running
    Write-Log "[7/8] Verifying service..."
    Start-Sleep -Seconds 5

    $service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
    if ($service -and $service.Status -eq "Running") {
        Write-Log "       Service running successfully"
    } else {
        # Try to start it manually
        Write-Log "       Service not running, attempting manual start..."
        Start-Service -Name $ServiceName -ErrorAction SilentlyContinue
        Start-Sleep -Seconds 5

        $service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
        if ($service -and $service.Status -eq "Running") {
            Write-Log "       Service started successfully"
        } else {
            $status = if ($service) { $service.Status } else { "NOT FOUND" }
            throw "Service failed to start. Status: $status"
        }
    }

    # Step 8: Self-heal scheduled task settings (fix battery restrictions from old installs)
    Write-Log "[8/8] Self-healing scheduled task settings..."
    try {
        $taskName = "RepairIQUpdater"
        $task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue

        if ($task) {
            $settings = $task.Settings
            $needsFix = $false

            # Check if battery restrictions are enabled (they shouldn't be)
            if ($settings.DisallowStartIfOnBatteries -eq $true) {
                $needsFix = $true
                Write-Log "       Found: DisallowStartIfOnBatteries=True (bad)"
            }
            if ($settings.StopIfGoingOnBatteries -eq $true) {
                $needsFix = $true
                Write-Log "       Found: StopIfGoingOnBatteries=True (bad)"
            }

            if ($needsFix) {
                Write-Log "       Fixing battery settings..."
                $settings.DisallowStartIfOnBatteries = $false
                $settings.StopIfGoingOnBatteries = $false
                Set-ScheduledTask -TaskName $taskName -Settings $settings | Out-Null
                Write-Log "       Fixed: Battery restrictions disabled"
            } else {
                Write-Log "       Battery settings OK (no fix needed)"
            }
        } else {
            Write-Log "       Scheduled task not found (will be fixed on next install)" "WARN"
        }
    } catch {
        # Non-fatal - log warning but don't fail the update
        Write-Log "       Could not fix scheduled task: $_" "WARN"
    }

    # Cleanup update files
    Cleanup-UpdateFiles

    Write-Log "====================================="
    Write-Log "UPDATE TO v$expectedVersion COMPLETED SUCCESSFULLY"
    Write-Log "====================================="

    exit 0

} catch {
    Write-Log "=========== UPDATE FAILED ===========" "ERROR"
    Write-Log "Error: $_" "ERROR"
    Write-Log $_.ScriptStackTrace "ERROR"

    # Attempt rollback
    if (Test-Path $BackupDir) {
        Write-Log "Attempting rollback to previous version..." "WARN"
        try {
            if (Test-Path "$BackupDir\dist") {
                if (Test-Path "$InstallDir\dist") {
                    Remove-Item "$InstallDir\dist" -Recurse -Force
                }
                Copy-Item "$BackupDir\dist" "$InstallDir\dist" -Recurse
                Write-Log "       Restored: dist\" "WARN"
            }
            if (Test-Path "$BackupDir\package.json") {
                Copy-Item "$BackupDir\package.json" "$InstallDir\package.json" -Force
                Write-Log "       Restored: package.json" "WARN"
            }
            Write-Log "Rollback completed" "WARN"

            # Try to start service with old version
            Start-Service -Name $ServiceName -ErrorAction SilentlyContinue
            Start-Sleep -Seconds 3
            $svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
            if ($svc -and $svc.Status -eq "Running") {
                Write-Log "Service restarted with previous version" "WARN"
            } else {
                Write-Log "WARNING: Service did not restart after rollback" "ERROR"
            }
        } catch {
            Write-Log "Rollback failed: $_" "ERROR"
        }
    } else {
        Write-Log "No backup available for rollback" "ERROR"
    }

    # Cleanup lock file but keep update files for debugging
    Remove-Item $LockFile -Force -ErrorAction SilentlyContinue

    exit 1
}
