<#
.SYNOPSIS
    ROA2WEB Unified Console - Ultrathin Monolith Management

.DESCRIPTION
    Unified deployment and management console for ROA2WEB Ultrathin Monolith.
    Single Windows service with multiple modules (Reports, Data Entry, Telegram).

    Features:
    - Deploy backend/frontend updates
    - Manage unified service (start/stop/restart)
    - View logs and service status
    - Backup before deployment
    - Module control via .env flags

.PARAMETER NonInteractive
    Run in non-interactive mode with specific action

.PARAMETER Action
    Action to perform:
    - DeployBackend: Deploy backend files only
    - DeployFrontend: Deploy frontend files only
    - DeployAll: Deploy both backend and frontend
    - StartService: Start ROA2WEB-Backend service
    - StopService: Stop ROA2WEB-Backend service
    - RestartService: Restart ROA2WEB-Backend service
    - Status: Show service status and health
    - ViewLogs: Display recent log entries

.PARAMETER PackagePath
    Path to deployment package (for Deploy actions)

.EXAMPLE
    .\ROA2WEB-Console.ps1
    Launch interactive menu

.EXAMPLE
    .\ROA2WEB-Console.ps1 -NonInteractive -Action DeployAll -PackagePath "C:\Temp\deploy-20250129-120000"
    Deploy full package non-interactively

.EXAMPLE
    .\ROA2WEB-Console.ps1 -NonInteractive -Action RestartService
    Restart the unified backend service

.NOTES
    Author: ROA2WEB Team
    Version: 2.0 (Ultrathin Monolith)
    Requires: Administrator privileges, PowerShell 5.1+
#>

[CmdletBinding()]
param(
    [switch]$NonInteractive,

    [ValidateSet("DeployBackend", "DeployFrontend", "DeployAll",
                 "StartService", "StopService", "RestartService",
                 "Status", "ViewLogs", "CheckOCR", "InstallOCR")]
    [string]$Action = "",

    [string]$PackagePath = ""
)

$ErrorActionPreference = "Stop"

# Require Administrator
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Write-Host "[ERROR] This script requires Administrator privileges" -ForegroundColor Red
    Write-Host "Please run PowerShell as Administrator and try again." -ForegroundColor Yellow
    exit 1
}

# =============================================================================
# CONFIGURATION
# =============================================================================

$script:Config = @{
    # Service Configuration
    ServiceName = "ROA2WEB-Backend"
    ServiceDisplayName = "ROA2WEB Unified Backend Service"
    ServicePort = 8000
    HealthUrl = "http://localhost:8000/health"
    HealthTimeout = 10

    # Installation Paths
    InstallRoot = "C:\inetpub\wwwroot\roa2web"
    BackendPath = "C:\inetpub\wwwroot\roa2web\backend"
    # IMPORTANT: venv is OUTSIDE roa2web to survive deployments!
    VenvPath = "C:\inetpub\wwwroot\roa2web-venv"
    FrontendPath = "C:\inetpub\wwwroot\roa2web\frontend"
    SharedPath = "C:\inetpub\wwwroot\roa2web\shared"
    ConfigPath = "C:\inetpub\wwwroot\roa2web\config"
    DataPath = "C:\inetpub\wwwroot\roa2web\data"

    # Logs
    LogsPath = "C:\inetpub\wwwroot\roa2web\logs"
    BackendStdoutLog = "C:\inetpub\wwwroot\roa2web\logs\backend-stdout.log"
    BackendStderrLog = "C:\inetpub\wwwroot\roa2web\logs\backend-stderr.log"

    # Backups
    BackupPath = "C:\inetpub\wwwroot\roa2web\backups"
    MaxBackups = 5

    # IIS Configuration
    IISSiteName = "Default Web Site"
    IISAppName = "roa2web"
}

# =============================================================================
# HELPER FUNCTIONS
# =============================================================================

function Write-Step {
    param([string]$Message)
    Write-Host "`n[*] $Message" -ForegroundColor Cyan
}

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

function Write-Error {
    param([string]$Message)
    Write-Host "    [ERROR] $Message" -ForegroundColor Red
}

function Write-Warning {
    param([string]$Message)
    Write-Host "    [WARN] $Message" -ForegroundColor Yellow
}

function Write-Info {
    param([string]$Message)
    Write-Host "    [*] $Message" -ForegroundColor Gray
}

function Get-PythonPaths {
    <#
    .SYNOPSIS
        Returns Python and pip paths, preferring venv if available
    #>
    $venvPython = Join-Path $Config.VenvPath "Scripts\python.exe"
    $venvPip = Join-Path $Config.VenvPath "Scripts\pip.exe"

    if (Test-Path $venvPython) {
        return @{
            Python = $venvPython
            Pip = $venvPip
            IsVenv = $true
        }
    } else {
        # Fallback to global Python
        $globalPython = (Get-Command python -ErrorAction SilentlyContinue).Source
        $globalPip = (Get-Command pip -ErrorAction SilentlyContinue).Source
        return @{
            Python = $globalPython
            Pip = $globalPip
            IsVenv = $false
        }
    }
}

function Initialize-Venv {
    <#
    .SYNOPSIS
        Creates virtual environment at external location if it doesn't exist.
        External location (C:\inetpub\wwwroot\roa2web-venv) survives deployments.
    #>
    $venvPath = $Config.VenvPath
    $venvPython = Join-Path $venvPath "Scripts\python.exe"
    $venvPip = Join-Path $venvPath "Scripts\pip.exe"

    # If venv exists and is valid (pip works), we're good
    if (Test-Path $venvPython) {
        # Verify pip is functional (not broken by move)
        try {
            $pipTest = & $venvPip --version 2>&1
            if ($LASTEXITCODE -eq 0) {
                Write-Success "Virtual environment already exists at $venvPath"
                return $true
            } else {
                Write-Warning "Venv exists but pip is broken, recreating..."
                Remove-Item -Path $venvPath -Recurse -Force -ErrorAction SilentlyContinue
            }
        } catch {
            Write-Warning "Venv exists but pip test failed, recreating..."
            Remove-Item -Path $venvPath -Recurse -Force -ErrorAction SilentlyContinue
        }
    }

    Write-Step "Creating virtual environment at external location..."
    Write-Info "Path: $venvPath (survives deployments)"
    try {
        $globalPython = (Get-Command python -ErrorAction Stop).Source
        & $globalPython -m venv $venvPath
        if (Test-Path $venvPython) {
            Write-Success "Virtual environment created"

            # Upgrade pip
            Write-Step "Upgrading pip in venv..."
            & $venvPython -m pip install --upgrade pip 2>&1 | Out-Null
            Write-Success "Pip upgraded"

            return $true
        } else {
            Write-Error "Failed to create virtual environment"
            return $false
        }
    } catch {
        Write-Error "Failed to create venv: $_"
        return $false
    }
}

function Update-ServiceToUseVenv {
    <#
    .SYNOPSIS
        Updates NSSM service to use venv Python
    #>
    $venvPython = Join-Path $Config.VenvPath "Scripts\python.exe"

    if (-not (Test-Path $venvPython)) {
        Write-Warning "Venv Python not found: $venvPython"
        return $false
    }

    # Check if nssm is available
    $nssmPath = Get-Command nssm -ErrorAction SilentlyContinue
    if (-not $nssmPath) {
        Write-Warning "NSSM not found in PATH"
        return $false
    }

    try {
        # Get current application path
        $currentApp = & nssm get $Config.ServiceName Application 2>&1

        if ($currentApp -eq $venvPython) {
            Write-Success "Service already configured to use venv Python"
            return $true
        }

        Write-Step "Updating service to use venv Python..."

        # Stop service first
        Stop-ROAService | Out-Null

        # Update service application
        & nssm set $Config.ServiceName Application $venvPython
        Write-Success "Service updated to use: $venvPython"

        return $true
    } catch {
        Write-Error "Failed to update service: $_"
        return $false
    }
}

function Wait-ForKeyPress {
    Write-Host "`nPress any key to continue..." -ForegroundColor Gray
    $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}

# =============================================================================
# SERVICE MANAGEMENT
# =============================================================================

function Get-ServiceSafe {
    param([string]$ServiceName)
    try {
        return Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
    } catch {
        return $null
    }
}

function Test-ServiceInstalled {
    $service = Get-ServiceSafe -ServiceName $Config.ServiceName
    return ($null -ne $service)
}

function Start-ROAService {
    Write-Step "Starting $($Config.ServiceDisplayName)..."

    $service = Get-ServiceSafe -ServiceName $Config.ServiceName

    if (-not $service) {
        Write-Error "Service not found: $($Config.ServiceName)"
        Write-Info "Run Install-ROA2WEB.ps1 first to install the service"
        return $false
    }

    if ($service.Status -eq 'Running') {
        Write-Success "Service is already running"
        return $true
    }

    try {
        Start-Service -Name $Config.ServiceName
        Start-Sleep -Seconds 3

        # Wait for service to start (max 30 seconds)
        $maxWait = 30
        $waited = 0
        while ($waited -lt $maxWait) {
            $service = Get-Service -Name $Config.ServiceName
            if ($service.Status -eq 'Running') {
                Write-Success "Service started successfully"

                # Wait a bit more for backend to initialize
                Write-Info "Waiting for backend initialization..."
                Start-Sleep -Seconds 5

                # Test health endpoint
                Test-ServiceHealth | Out-Null
                return $true
            }
            Start-Sleep -Seconds 2
            $waited += 2
        }

        Write-Warning "Service started but status unclear"
        return $false
    } catch {
        Write-Error "Failed to start service: $_"
        return $false
    }
}

function Stop-ROAService {
    Write-Step "Stopping $($Config.ServiceDisplayName)..."

    $service = Get-ServiceSafe -ServiceName $Config.ServiceName

    if (-not $service) {
        Write-Warning "Service not found: $($Config.ServiceName)"
        return $true
    }

    if ($service.Status -eq 'Stopped') {
        Write-Success "Service is already stopped"
        return $true
    }

    try {
        Stop-Service -Name $Config.ServiceName -Force

        # Wait for service to stop (max 30 seconds)
        $maxWait = 30
        $waited = 0
        while ($waited -lt $maxWait) {
            $service = Get-Service -Name $Config.ServiceName
            if ($service.Status -eq 'Stopped') {
                Write-Success "Service stopped successfully"
                return $true
            }
            Start-Sleep -Seconds 2
            $waited += 2
        }

        Write-Warning "Service stop timeout - may still be running"
        return $false
    } catch {
        Write-Error "Failed to stop service: $_"
        return $false
    }
}

function Restart-ROAService {
    Write-Step "Restarting $($Config.ServiceDisplayName)..."

    if (Stop-ROAService) {
        Start-Sleep -Seconds 2
        return Start-ROAService
    }
    return $false
}

function Test-ServiceHealth {
    Write-Step "Checking service health..."

    $service = Get-ServiceSafe -ServiceName $Config.ServiceName

    if (-not $service) {
        Write-Warning "Service not installed"
        return $false
    }

    Write-Info "Service Status: $($service.Status)"

    if ($service.Status -ne 'Running') {
        Write-Warning "Service is not running"
        return $false
    }

    # Test health endpoint
    try {
        Write-Info "Testing health endpoint: $($Config.HealthUrl)"
        $response = Invoke-WebRequest -Uri $Config.HealthUrl -TimeoutSec $Config.HealthTimeout -UseBasicParsing

        if ($response.StatusCode -eq 200) {
            Write-Success "Health check PASSED (HTTP 200)"

            # Parse response for module status
            try {
                $health = $response.Content | ConvertFrom-Json
                Write-Info "Modules Status:"
                if ($health.modules) {
                    foreach ($module in $health.modules.PSObject.Properties) {
                        $status = if ($module.Value) { "[ON] Enabled" } else { "[OFF] Disabled" }
                        Write-Info "  - $($module.Name): $status"
                    }
                }
            } catch {
                # If can't parse JSON, just show raw response
                Write-Info "Response: $($response.Content)"
            }

            return $true
        } else {
            Write-Warning "Health check returned HTTP $($response.StatusCode)"
            return $false
        }
    } catch {
        Write-Warning "Health check FAILED: $_"
        Write-Info "This may indicate the backend is still initializing or there's a configuration issue"
        return $false
    }
}

# =============================================================================
# OCR DEPENDENCY CHECK
# =============================================================================

function Test-OCRDependencies {
    param(
        [switch]$AutoInstall,
        [switch]$Silent
    )

    if (-not $Silent) {
        Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
        Write-Host "  OCR Dependencies Check$(if ($AutoInstall) { ' (Auto-Install Enabled)' })" -ForegroundColor Cyan
        Write-Host ("=" * 70) -ForegroundColor Cyan
    }

    $allOk = $true

    # Get Python paths (prefer venv)
    $pyPaths = Get-PythonPaths

    # Check Python/venv
    if (-not $Silent) { Write-Step "Checking Python installation..." }

    if ($pyPaths.IsVenv) {
        if (-not $Silent) {
            $venvPythonVersion = & $pyPaths.Python --version 2>&1
            Write-Success "Virtual environment: $($Config.VenvPath)"
            Write-Success "Python (venv): $venvPythonVersion"
        }
        # Ensure service uses venv Python
        if ($AutoInstall) {
            # Install requirements.txt if exists
            $requirementsFile = Join-Path $Config.BackendPath "requirements.txt"
            if (Test-Path $requirementsFile) {
                if (-not $Silent) { Write-Step "Installing base requirements in venv..." }
                try {
                    & $pyPaths.Pip install -r $requirementsFile 2>&1 | Out-Null
                    if (-not $Silent) { Write-Success "Base requirements installed" }
                } catch {
                    if (-not $Silent) { Write-Warning "Failed to install requirements: $_" }
                }
            }

            if (-not $Silent) { Write-Step "Ensuring service uses venv Python..." }
            $serviceUpdated = Update-ServiceToUseVenv
            if ($serviceUpdated -and -not $Silent) {
                Write-Success "Service configured to use venv"
            }
        }
    } else {
        # No venv - create one if AutoInstall
        if ($AutoInstall) {
            if (-not $Silent) { Write-Warning "Virtual environment not found - creating..." }
            $venvCreated = Initialize-Venv
            if ($venvCreated) {
                # Refresh paths
                $pyPaths = Get-PythonPaths

                # Update service to use venv Python
                if (-not $Silent) { Write-Step "Updating service to use virtual environment..." }
                Update-ServiceToUseVenv | Out-Null
            } else {
                if (-not $Silent) { Write-Error "Could not create virtual environment" }
                return $false
            }
        } else {
            # Check global Python
            if ($pyPaths.Python) {
                $pythonVersion = cmd /c "python --version 2>&1"
                if (-not $Silent) {
                    Write-Warning "Using global Python (venv recommended)"
                    Write-Success "Python: $pythonVersion"
                }
            } else {
                if (-not $Silent) { Write-Error "Python not found" }
                return $false
            }
        }
    }

    # Determine pip executable to use
    $pipExe = if ($pyPaths.Pip -and (Test-Path $pyPaths.Pip)) { "`"$($pyPaths.Pip)`"" } else { "pip" }
    if (-not $Silent -and $pyPaths.IsVenv) { Write-Info "Using pip from venv: $pipExe" }

    # Check and optionally install Python packages
    if (-not $Silent) { Write-Step "Checking Python OCR packages..." }

    $packages = @(
        @{ Name = "torch"; Package = "torch"; Required = $true; Description = "PyTorch (for docTR)" },
        @{ Name = "torchvision"; Package = "torchvision"; Required = $true; Description = "TorchVision (for docTR)" },
        @{ Name = "python-doctr"; Package = "python-doctr"; Required = $true; Description = "docTR OCR engine" },
        @{ Name = "pytesseract"; Package = "pytesseract"; Required = $true; Description = "Tesseract Python wrapper" },
        @{ Name = "paddleocr"; Package = "paddleocr"; Required = $true; Description = "PaddleOCR engine" }
    )

    foreach ($pkg in $packages) {
        # Use venv pip to check packages
        $pipOutput = cmd /c "$pipExe show $($pkg.Package) 2>&1"
        $isInstalled = $pipOutput -match "Version:"

        if ($isInstalled) {
            $versionLine = $pipOutput | Where-Object { $_ -match "^Version:" }
            $version = if ($versionLine) { ($versionLine -split ":")[1].Trim() } else { "unknown" }
            if (-not $Silent) { Write-Success "$($pkg.Package): $version" }
        } else {
            if ($pkg.Required) {
                if ($AutoInstall) {
                    if (-not $Silent) { Write-Warning "$($pkg.Package): NOT INSTALLED - Installing..." }
                    try {
                        # Use venv pip to install
                        $installCmd = "$pipExe install `"$($pkg.Name)`""
                        if (-not $Silent) { Write-Info "  Running: $installCmd" }
                        $installResult = Invoke-Expression "cmd /c $installCmd 2>&1"
                        # Verify installation
                        $verifyOutput = cmd /c "$pipExe show $($pkg.Package) 2>&1"
                        if ($verifyOutput -match "Version:") {
                            if (-not $Silent) { Write-Success "$($pkg.Package): Installed successfully" }
                        } else {
                            if (-not $Silent) {
                                Write-Error "$($pkg.Package): Installation FAILED"
                                # Show last few lines of pip output for debugging
                                $errorLines = ($installResult | Select-Object -Last 5) -join "`n"
                                if ($errorLines) {
                                    Write-Host "  Pip output:" -ForegroundColor Gray
                                    Write-Host "  $errorLines" -ForegroundColor Gray
                                }
                                Write-Info "  Try manually: $pipExe install `"$($pkg.Name)`""
                            }
                            $allOk = $false
                        }
                    } catch {
                        if (-not $Silent) { Write-Error "$($pkg.Package): Installation error - $_" }
                        $allOk = $false
                    }
                } else {
                    if (-not $Silent) {
                        Write-Error "$($pkg.Package): NOT INSTALLED - $($pkg.Description)"
                        Write-Info "  Install with: pip install $($pkg.Name)"
                    }
                    $allOk = $false
                }
            } else {
                if (-not $Silent) { Write-Warning "$($pkg.Package): Not installed (optional)" }
            }
        }
    }

    # Check external tools
    if (-not $Silent) { Write-Step "Checking external OCR tools..." }

    # Check for Chocolatey (used for auto-install)
    $chocoAvailable = $null -ne (Get-Command choco -ErrorAction SilentlyContinue)

    # Install Chocolatey if needed and AutoInstall is enabled
    if ($AutoInstall -and -not $chocoAvailable) {
        if (-not $Silent) { Write-Warning "Chocolatey: NOT FOUND - Installing..." }
        try {
            Set-ExecutionPolicy Bypass -Scope Process -Force
            [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
            Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
            # Refresh environment
            $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
            $chocoAvailable = $null -ne (Get-Command choco -ErrorAction SilentlyContinue)
            if ($chocoAvailable) {
                if (-not $Silent) { Write-Success "Chocolatey: Installed successfully" }
            } else {
                if (-not $Silent) { Write-Warning "Chocolatey: Installed but not in PATH - restart PowerShell" }
            }
        } catch {
            if (-not $Silent) { Write-Error "Chocolatey: Installation failed - $_" }
        }
    }

    # Tesseract
    $tesseractPath = Get-Command tesseract -ErrorAction SilentlyContinue
    if ($tesseractPath) {
        $tessVersion = cmd /c "tesseract --version 2>&1" | Select-Object -First 1
        if (-not $Silent) {
            Write-Success "Tesseract: $tessVersion"
            Write-Info "  Path: $($tesseractPath.Source)"
        }
    } else {
        if ($AutoInstall) {
            if ($chocoAvailable) {
                if (-not $Silent) { Write-Warning "Tesseract: NOT FOUND - Installing via Chocolatey..." }
                try {
                    $result = cmd /c "choco install tesseract -y 2>&1"
                    # Refresh PATH
                    $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
                    $tesseractPath = Get-Command tesseract -ErrorAction SilentlyContinue
                    if ($tesseractPath) {
                        if (-not $Silent) { Write-Success "Tesseract: Installed successfully" }
                    } else {
                        if (-not $Silent) { Write-Error "Tesseract: Installation completed but not in PATH - restart PowerShell" }
                        $allOk = $false
                    }
                } catch {
                    if (-not $Silent) { Write-Error "Tesseract: Chocolatey install failed - $_" }
                    $allOk = $false
                }
            } else {
                if (-not $Silent) {
                    Write-Error "Tesseract: NOT FOUND - Chocolatey not available for auto-install"
                    Write-Info "  Install Chocolatey first: https://chocolatey.org/install"
                    Write-Info "  Then run: choco install tesseract -y"
                }
                $allOk = $false
            }
        } else {
            if (-not $Silent) {
                Write-Error "Tesseract: NOT FOUND in PATH"
                Write-Info "  Install with: choco install tesseract -y"
                Write-Info "  Or download from: https://github.com/UB-Mannheim/tesseract/wiki"
            }
            $allOk = $false
        }
    }

    # Poppler (for PDF support)
    $popplerPath = Get-Command pdftoppm -ErrorAction SilentlyContinue
    if ($popplerPath) {
        if (-not $Silent) {
            Write-Success "Poppler: Found (pdftoppm)"
            Write-Info "  Path: $($popplerPath.Source)"
        }
    } else {
        if ($AutoInstall -and $chocoAvailable) {
            if (-not $Silent) { Write-Warning "Poppler: NOT FOUND - Installing via Chocolatey..." }
            try {
                $result = cmd /c "choco install poppler -y 2>&1"
                # Refresh PATH
                $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
                $popplerPath = Get-Command pdftoppm -ErrorAction SilentlyContinue
                if ($popplerPath) {
                    if (-not $Silent) { Write-Success "Poppler: Installed successfully" }
                } else {
                    if (-not $Silent) { Write-Warning "Poppler: Installation completed but not in PATH - restart PowerShell" }
                }
            } catch {
                if (-not $Silent) { Write-Warning "Poppler: Chocolatey install failed - $_" }
            }
        } else {
            if (-not $Silent) {
                Write-Warning "Poppler: NOT FOUND in PATH (required for PDF OCR)"
                Write-Info "  Install with: choco install poppler -y"
            }
        }
    }

    # Check .env OCR settings
    if (-not $Silent) {
        Write-Step "Checking OCR configuration in .env..."
        $envPath = Join-Path $Config.BackendPath ".env"
        if (Test-Path $envPath) {
            $envContent = Get-Content $envPath -Raw

            $ocrSettings = @(
                "OCR_ENABLE_PADDLEOCR",
                "OCR_ENABLE_TESSERACT",
                "OCR_DEFAULT_ENGINE",
                "OCR_WORKERS"
            )

            foreach ($setting in $ocrSettings) {
                if ($envContent -match "$setting\s*=\s*(.+)") {
                    Write-Info "  $setting = $($Matches[1].Trim())"
                } else {
                    Write-Warning "  ${setting}: NOT CONFIGURED"
                }
            }
        } else {
            Write-Warning ".env file not found at: $envPath"
        }

        Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
        if ($allOk) {
            Write-Host "  Result: All required OCR dependencies are installed" -ForegroundColor Green
        } else {
            Write-Host "  Result: Some required dependencies are MISSING" -ForegroundColor Red
            Write-Host "  Run with -AutoInstall to install missing packages" -ForegroundColor Yellow
        }
        Write-Host ("=" * 70) -ForegroundColor Cyan
    }

    return $allOk
}

# =============================================================================
# BACKUP FUNCTIONS
# =============================================================================

function New-Backup {
    param([string]$Component)

    Write-Step "Creating backup before deployment..."

    if (-not (Test-Path $Config.BackupPath)) {
        New-Item -ItemType Directory -Path $Config.BackupPath -Force | Out-Null
    }

    $timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
    $backupName = "backup-${Component}-${timestamp}"
    $backupDest = Join-Path $Config.BackupPath $backupName

    try {
        New-Item -ItemType Directory -Path $backupDest -Force | Out-Null

        if ($Component -eq "Backend" -or $Component -eq "All") {
            if (Test-Path $Config.BackendPath) {
                Write-Info "Backing up backend..."
                Copy-Item -Path $Config.BackendPath -Destination (Join-Path $backupDest "backend") -Recurse -Force
            }
            if (Test-Path $Config.SharedPath) {
                Write-Info "Backing up shared modules..."
                Copy-Item -Path $Config.SharedPath -Destination (Join-Path $backupDest "shared") -Recurse -Force
            }
        }

        if ($Component -eq "Frontend" -or $Component -eq "All") {
            if (Test-Path $Config.FrontendPath) {
                Write-Info "Backing up frontend..."
                Copy-Item -Path $Config.FrontendPath -Destination (Join-Path $backupDest "frontend") -Recurse -Force
            }
        }

        Write-Success "Backup created: $backupName"

        # Clean old backups (keep last N)
        $allBackups = Get-ChildItem -Path $Config.BackupPath -Directory | Sort-Object CreationTime -Descending
        if ($allBackups.Count -gt $Config.MaxBackups) {
            $toDelete = $allBackups | Select-Object -Skip $Config.MaxBackups
            foreach ($old in $toDelete) {
                Write-Info "Removing old backup: $($old.Name)"
                Remove-Item -Path $old.FullName -Recurse -Force
            }
        }

        return $true
    } catch {
        Write-Error "Backup failed: $_"
        return $false
    }
}

# =============================================================================
# DEPLOYMENT FUNCTIONS
# =============================================================================

function Deploy-Backend {
    param([string]$SourcePath)

    Write-Step "Deploying backend..."

    $sourceBe = Join-Path $SourcePath "backend"
    $sourceShared = Join-Path $SourcePath "shared"

    if (-not (Test-Path $sourceBe)) {
        Write-Error "Backend not found in package: $sourceBe"
        return $false
    }

    try {
        # Stop service
        if (-not (Stop-ROAService)) {
            Write-Warning "Failed to stop service, continuing anyway..."
        }

        # Backup
        if (-not (New-Backup -Component "Backend")) {
            Write-Warning "Backup failed, but continuing with deployment"
        }

        # Deploy backend
        Write-Info "Copying backend files..."
        if (Test-Path $Config.BackendPath) {
            # Preserve .env file
            $envFile = Join-Path $Config.BackendPath ".env"
            $envBackup = $null
            if (Test-Path $envFile) {
                $envBackup = Get-Content $envFile -Raw
                Write-Info "Preserving .env file"
            }

            # Preserve data directory (contains SQLite databases with production data!)
            $dataDir = Join-Path $Config.BackendPath "data"
            $dataTempPath = Join-Path $env:TEMP "roa2web-data-backup-$(Get-Date -Format 'yyyyMMddHHmmss')"
            $dataBackup = $null
            if (Test-Path $dataDir) {
                Write-Info "Preserving data directory (SQLite databases, uploads, cache)"
                Copy-Item -Path $dataDir -Destination $dataTempPath -Recurse -Force
                $dataBackup = $dataTempPath
            }

            # Delete old venv inside backend if it exists (it has hardcoded paths and can't be moved)
            $oldVenvPath = Join-Path $Config.BackendPath "venv"
            if (Test-Path $oldVenvPath) {
                Write-Info "Removing old venv from backend directory (will use external venv)"
                Remove-Item -Path $oldVenvPath -Recurse -Force -ErrorAction SilentlyContinue
            }

            Remove-Item -Path $Config.BackendPath -Recurse -Force
            Copy-Item -Path $sourceBe -Destination $Config.BackendPath -Recurse -Force

            # Restore .env
            if ($envBackup) {
                Set-Content -Path $envFile -Value $envBackup -Force
                Write-Success ".env file restored"
            }

            # Restore data directory
            if ($dataBackup -and (Test-Path $dataBackup)) {
                # Remove the empty data dir from package and restore the preserved one
                $newDataDir = Join-Path $Config.BackendPath "data"
                if (Test-Path $newDataDir) {
                    Remove-Item -Path $newDataDir -Recurse -Force
                }
                Copy-Item -Path $dataBackup -Destination $newDataDir -Recurse -Force
                Remove-Item -Path $dataBackup -Recurse -Force -ErrorAction SilentlyContinue
                Write-Success "Data directory restored (SQLite databases preserved)"
            }
        } else {
            Copy-Item -Path $sourceBe -Destination $Config.BackendPath -Recurse -Force
        }
        Write-Success "Backend files deployed"

        # Deploy shared modules if present
        if (Test-Path $sourceShared) {
            Write-Info "Copying shared modules..."
            if (Test-Path $Config.SharedPath) {
                Remove-Item -Path $Config.SharedPath -Recurse -Force
            }
            Copy-Item -Path $sourceShared -Destination $Config.SharedPath -Recurse -Force
            Write-Success "Shared modules deployed"
        }

        # Setup virtual environment
        Write-Step "Setting up Python virtual environment..."
        $venvCreated = Initialize-Venv
        if (-not $venvCreated) {
            Write-Warning "Could not create/verify virtual environment"
        }

        # Install requirements.txt
        $requirementsFile = Join-Path $Config.BackendPath "requirements.txt"
        if (Test-Path $requirementsFile) {
            $pyPaths = Get-PythonPaths
            if ($pyPaths.IsVenv) {
                Write-Step "Installing Python dependencies in venv..."
                $pipExe = $pyPaths.Pip

                # Verify pip is functional before installing
                $pipVersion = & $pipExe --version 2>&1
                if ($LASTEXITCODE -ne 0) {
                    Write-Error "Pip is not functional: $pipVersion"
                    Write-Info "Try: Remove-Item -Recurse $($Config.VenvPath); then redeploy"
                } else {
                    Write-Info "Using pip: $pipVersion"

                    # Install requirements (ignore warnings, check exit code only)
                    $oldErrorAction = $ErrorActionPreference
                    $ErrorActionPreference = "Continue"
                    $pipOutput = & $pipExe install -r $requirementsFile 2>&1
                    $pipExitCode = $LASTEXITCODE
                    $ErrorActionPreference = $oldErrorAction

                    # Log warnings but don't fail on them
                    $pipOutput | ForEach-Object {
                        if ($_ -match "WARNING:") {
                            Write-Warning $_
                        }
                    }

                    if ($pipExitCode -eq 0) {
                        # Verify uvicorn installed (critical dependency)
                        $uvicornCheck = & $pipExe show uvicorn 2>&1
                        if ($LASTEXITCODE -eq 0) {
                            Write-Success "Python dependencies installed"
                        } else {
                            Write-Error "Dependencies install failed - uvicorn not found"
                            Write-Info "Manual fix: $pipExe install -r $requirementsFile"
                        }
                    } else {
                        Write-Error "Pip install failed with exit code $pipExitCode"
                        Write-Info "Manual fix: $pipExe install -r $requirementsFile"
                    }
                }
            } else {
                Write-Warning "No venv found - skipping requirements.txt installation"
            }
        }

        # Update service to use venv Python
        $pyPaths = Get-PythonPaths
        if ($pyPaths.IsVenv) {
            Update-ServiceToUseVenv | Out-Null
        }

        # Start service
        Start-Sleep -Seconds 2
        if (Start-ROAService) {
            Write-Success "Backend deployment completed successfully"

            # Check and auto-install OCR dependencies after deployment
            Write-Step "Checking and installing OCR dependencies..."
            $ocrOk = Test-OCRDependencies -AutoInstall
            if (-not $ocrOk) {
                Write-Warning "Some OCR dependencies could not be installed automatically"
                Write-Info "Manual installation may be required for external tools (Tesseract, Poppler)"
            }

            return $true
        } else {
            Write-Warning "Backend deployed but service start failed"
            Write-Info "Check logs: $($Config.BackendStderrLog)"
            return $false
        }
    } catch {
        Write-Error "Backend deployment failed: $_"
        return $false
    }
}

function Deploy-Frontend {
    param([string]$SourcePath)

    Write-Step "Deploying frontend..."

    $sourceFe = Join-Path $SourcePath "frontend"

    if (-not (Test-Path $sourceFe)) {
        Write-Error "Frontend not found in package: $sourceFe"
        return $false
    }

    try {
        # Backup
        if (-not (New-Backup -Component "Frontend")) {
            Write-Warning "Backup failed, but continuing with deployment"
        }

        # Deploy frontend
        Write-Info "Copying frontend files to IIS..."
        if (Test-Path $Config.FrontendPath) {
            Remove-Item -Path $Config.FrontendPath -Recurse -Force
        }
        Copy-Item -Path $sourceFe -Destination $Config.FrontendPath -Recurse -Force
        Write-Success "Frontend files deployed"

        # Verify web.config was deployed (should be in frontend/ from dist/)
        $deployedWebConfig = Join-Path $Config.FrontendPath "web.config"
        if (Test-Path $deployedWebConfig) {
            Write-Success "web.config deployed successfully"
            # Verify it has correct configuration for /roa2web/ path
            $configContent = Get-Content $deployedWebConfig -Raw
            if ($configContent -match 'url="[^"]*roa2web/api') {
                Write-Success "web.config contains correct /roa2web/api proxy rules"
            } else {
                Write-Warning "web.config may not have correct /roa2web/api proxy configuration"
            }
        } else {
            Write-Warning "web.config not found in deployed frontend: $deployedWebConfig"
            Write-Warning "IIS reverse proxy will not work without web.config"
            Write-Warning "Ensure 'public/web.config' exists in source and rebuild frontend"
        }

        Write-Success "Frontend deployment completed successfully"
        return $true
    } catch {
        Write-Error "Frontend deployment failed: $_"
        return $false
    }
}

function Deploy-All {
    param([string]$SourcePath)

    Write-Step "Deploying complete package (Backend + Frontend)..."

    Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
    Write-Host "  FULL DEPLOYMENT" -ForegroundColor Cyan
    Write-Host ("=" * 70) -ForegroundColor Cyan

    # Backup everything
    if (-not (New-Backup -Component "All")) {
        Write-Warning "Backup failed, but continuing with deployment"
    }

    # Deploy backend (includes service restart)
    $backendOk = Deploy-Backend -SourcePath $SourcePath

    # Deploy frontend
    $frontendOk = Deploy-Frontend -SourcePath $SourcePath

    Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
    Write-Host "  DEPLOYMENT SUMMARY" -ForegroundColor Cyan
    Write-Host ("=" * 70) -ForegroundColor Cyan
    Write-Host "  Backend:  " -NoNewline
    if ($backendOk) {
        Write-Host "[OK] Success" -ForegroundColor Green
    } else {
        Write-Host "[X] Failed" -ForegroundColor Red
    }
    Write-Host "  Frontend: " -NoNewline
    if ($frontendOk) {
        Write-Host "[OK] Success" -ForegroundColor Green
    } else {
        Write-Host "[X] Failed" -ForegroundColor Red
    }
    Write-Host ("=" * 70) -ForegroundColor Cyan

    return ($backendOk -and $frontendOk)
}

# =============================================================================
# LOG VIEWING
# =============================================================================

function Show-Logs {
    Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
    Write-Host "  Service Logs" -ForegroundColor Cyan
    Write-Host ("=" * 70) -ForegroundColor Cyan

    # Backend stdout
    if (Test-Path $Config.BackendStdoutLog) {
        Write-Host "`n--- Backend STDOUT (last 30 lines) ---" -ForegroundColor Yellow
        Get-Content $Config.BackendStdoutLog -Tail 30 | ForEach-Object {
            Write-Host $_ -ForegroundColor Gray
        }
    } else {
        Write-Warning "Backend stdout log not found: $($Config.BackendStdoutLog)"
    }

    # Backend stderr
    if (Test-Path $Config.BackendStderrLog) {
        Write-Host "`n--- Backend STDERR (last 20 lines) ---" -ForegroundColor Yellow
        Get-Content $Config.BackendStderrLog -Tail 20 | ForEach-Object {
            Write-Host $_ -ForegroundColor Red
        }
    } else {
        Write-Info "Backend stderr log not found (this is OK if no errors)"
    }

    Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
}

# =============================================================================
# STATUS DISPLAY
# =============================================================================

function Show-Status {
    Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
    Write-Host "  ROA2WEB Ultrathin Monolith - System Status" -ForegroundColor Cyan
    Write-Host ("=" * 70) -ForegroundColor Cyan

    # Service Status
    $service = Get-ServiceSafe -ServiceName $Config.ServiceName
    Write-Host "`n  Service: $($Config.ServiceDisplayName)" -ForegroundColor Yellow
    if ($service) {
        Write-Host "    Status:     " -NoNewline
        if ($service.Status -eq 'Running') {
            Write-Host "$($service.Status)" -ForegroundColor Green
        } else {
            Write-Host "$($service.Status)" -ForegroundColor Red
        }
        Write-Host "    Name:       $($Config.ServiceName)" -ForegroundColor Gray
        Write-Host "    Port:       $($Config.ServicePort)" -ForegroundColor Gray
    } else {
        Write-Host "    Status:     " -NoNewline
        Write-Host "Not Installed" -ForegroundColor Red
        Write-Host "    Run Install-ROA2WEB.ps1 to install the service" -ForegroundColor Gray
    }

    # Health Check
    if ($service -and $service.Status -eq 'Running') {
        Write-Host ""
        Test-ServiceHealth | Out-Null
    }

    # Paths
    Write-Host "`n  Installation Paths:" -ForegroundColor Yellow
    Write-Host "    Root:       $($Config.InstallRoot)" -ForegroundColor Gray
    Write-Host "    Backend:    $($Config.BackendPath)" -ForegroundColor Gray
    Write-Host "    Frontend:   $($Config.FrontendPath)" -ForegroundColor Gray
    Write-Host "    Shared:     $($Config.SharedPath)" -ForegroundColor Gray
    Write-Host "    Logs:       $($Config.LogsPath)" -ForegroundColor Gray

    # Module Configuration
    $envFile = Join-Path $Config.BackendPath ".env"
    if (Test-Path $envFile) {
        Write-Host "`n  Module Configuration (.env):" -ForegroundColor Yellow
        $envContent = Get-Content $envFile
        $modules = $envContent | Where-Object { $_ -match "^MODULE_.*_ENABLED=" }
        foreach ($line in $modules) {
            if ($line -match "MODULE_(.+)_ENABLED=(.+)") {
                $moduleName = $matches[1]
                $enabled = $matches[2] -eq "true"
                Write-Host "    $moduleName`: " -NoNewline -ForegroundColor Gray
                if ($enabled) {
                    Write-Host "Enabled" -ForegroundColor Green
                } else {
                    Write-Host "Disabled" -ForegroundColor Red
                }
            }
        }
    }

    # Endpoints
    Write-Host "`n  API Endpoints:" -ForegroundColor Yellow
    Write-Host "    Health:     http://localhost:$($Config.ServicePort)/health" -ForegroundColor Gray
    Write-Host "    Docs:       http://localhost:$($Config.ServicePort)/docs" -ForegroundColor Gray
    Write-Host "    Frontend:   http://localhost/ (via IIS)" -ForegroundColor Gray

    Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
}

# =============================================================================
# INTERACTIVE MENU
# =============================================================================

function Show-MainMenu {
    Clear-Host
    Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
    Write-Host "  ROA2WEB Unified Console - Ultrathin Monolith" -ForegroundColor Cyan
    Write-Host ("=" * 70) -ForegroundColor Cyan

    $service = Get-ServiceSafe -ServiceName $Config.ServiceName
    if ($service) {
        Write-Host "`n  Service Status: " -NoNewline
        if ($service.Status -eq 'Running') {
            Write-Host "RUNNING" -ForegroundColor Green
        } else {
            Write-Host "$($service.Status)" -ForegroundColor Yellow
        }
    } else {
        Write-Host "`n  Service Status: " -NoNewline
        Write-Host "NOT INSTALLED" -ForegroundColor Red
    }

    Write-Host "`n  Main Menu:" -ForegroundColor Yellow
    Write-Host ""
    Write-Host "  === Deployment ===" -ForegroundColor Cyan
    Write-Host "  [1] Deploy Backend" -ForegroundColor White
    Write-Host "  [2] Deploy Frontend" -ForegroundColor White
    Write-Host "  [3] Deploy All (Backend + Frontend)" -ForegroundColor White
    Write-Host ""
    Write-Host "  === Service Management ===" -ForegroundColor Cyan
    Write-Host "  [4] Start Service" -ForegroundColor White
    Write-Host "  [5] Stop Service" -ForegroundColor White
    Write-Host "  [6] Restart Service" -ForegroundColor White
    Write-Host ""
    Write-Host "  === Monitoring ===" -ForegroundColor Cyan
    Write-Host "  [7] View Status" -ForegroundColor White
    Write-Host "  [8] View Logs" -ForegroundColor White
    Write-Host "  [9] Check/Install OCR Dependencies" -ForegroundColor White
    Write-Host ""
    Write-Host "  [Q] Quit" -ForegroundColor Red
    Write-Host ""
    Write-Host ("=" * 70) -ForegroundColor Cyan

    do {
        Write-Host "`nYour choice: " -ForegroundColor Yellow -NoNewline
        $choice = Read-Host

        switch ($choice.ToUpper()) {
            "1" {
                Write-Host "`nEnter deployment package path: " -NoNewline
                $pkgPath = Read-Host
                if (Test-Path $pkgPath) {
                    Deploy-Backend -SourcePath $pkgPath | Out-Null
                } else {
                    Write-Error "Package path not found: $pkgPath"
                }
                Wait-ForKeyPress
                return "Continue"
            }
            "2" {
                Write-Host "`nEnter deployment package path: " -NoNewline
                $pkgPath = Read-Host
                if (Test-Path $pkgPath) {
                    Deploy-Frontend -SourcePath $pkgPath | Out-Null
                } else {
                    Write-Error "Package path not found: $pkgPath"
                }
                Wait-ForKeyPress
                return "Continue"
            }
            "3" {
                Write-Host "`nEnter deployment package path: " -NoNewline
                $pkgPath = Read-Host
                if (Test-Path $pkgPath) {
                    Deploy-All -SourcePath $pkgPath | Out-Null
                } else {
                    Write-Error "Package path not found: $pkgPath"
                }
                Wait-ForKeyPress
                return "Continue"
            }
            "4" {
                Start-ROAService | Out-Null
                Wait-ForKeyPress
                return "Continue"
            }
            "5" {
                Stop-ROAService | Out-Null
                Wait-ForKeyPress
                return "Continue"
            }
            "6" {
                Restart-ROAService | Out-Null
                Wait-ForKeyPress
                return "Continue"
            }
            "7" {
                Show-Status
                Wait-ForKeyPress
                return "Continue"
            }
            "8" {
                Show-Logs
                Wait-ForKeyPress
                return "Continue"
            }
            "9" {
                $ocrOk = Test-OCRDependencies
                if (-not $ocrOk) {
                    Write-Host ""
                    Write-Host "  Install missing dependencies? (Y/N): " -ForegroundColor Yellow -NoNewline
                    $installChoice = Read-Host
                    if ($installChoice -eq "Y" -or $installChoice -eq "y") {
                        Write-Host ""
                        Test-OCRDependencies -AutoInstall | Out-Null
                    }
                }
                Wait-ForKeyPress
                return "Continue"
            }
            "Q" { return "Quit" }
            default {
                Write-Host "Invalid choice. Please select 1-9 or Q." -ForegroundColor Red
            }
        }
    } while ($true)
}

# =============================================================================
# MAIN EXECUTION
# =============================================================================

function Main {
    # Check if service installed
    if (-not (Test-ServiceInstalled) -and -not $NonInteractive) {
        Write-Warning "ROA2WEB service not found"
        Write-Info "Run Install-ROA2WEB.ps1 first to install the service"
        Write-Host ""
        Wait-ForKeyPress
    }

    # Non-interactive mode
    if ($NonInteractive -and $Action) {
        switch ($Action) {
            "DeployBackend" {
                if (-not $PackagePath) {
                    Write-Error "PackagePath parameter required for DeployBackend"
                    exit 1
                }
                $success = Deploy-Backend -SourcePath $PackagePath
                exit $(if ($success) { 0 } else { 1 })
            }
            "DeployFrontend" {
                if (-not $PackagePath) {
                    Write-Error "PackagePath parameter required for DeployFrontend"
                    exit 1
                }
                $success = Deploy-Frontend -SourcePath $PackagePath
                exit $(if ($success) { 0 } else { 1 })
            }
            "DeployAll" {
                if (-not $PackagePath) {
                    Write-Error "PackagePath parameter required for DeployAll"
                    exit 1
                }
                $success = Deploy-All -SourcePath $PackagePath
                exit $(if ($success) { 0 } else { 1 })
            }
            "StartService" {
                $success = Start-ROAService
                exit $(if ($success) { 0 } else { 1 })
            }
            "StopService" {
                $success = Stop-ROAService
                exit $(if ($success) { 0 } else { 1 })
            }
            "RestartService" {
                $success = Restart-ROAService
                exit $(if ($success) { 0 } else { 1 })
            }
            "Status" {
                Show-Status
                exit 0
            }
            "ViewLogs" {
                Show-Logs
                exit 0
            }
            "CheckOCR" {
                $success = Test-OCRDependencies
                exit $(if ($success) { 0 } else { 1 })
            }
            "InstallOCR" {
                $success = Test-OCRDependencies -AutoInstall
                exit $(if ($success) { 0 } else { 1 })
            }
        }
    }

    # Interactive mode
    do {
        $result = Show-MainMenu
    } while ($result -eq "Continue")

    Write-Host "`nGoodbye!`n" -ForegroundColor Cyan
}

# Run main
Main
