param(
    [string]$ProjectRoot = $PSScriptRoot,
    [switch]$SkipModelPull
)

$ErrorActionPreference = 'Stop'
Set-Location $ProjectRoot

# ── Helper: coloured step banners ────────────────────────────────────────────

$_stepNum = 0
$_totalSteps = 9

function Write-Banner($msg) {
    $script:_stepNum++
    $pct = [int](($script:_stepNum / $script:_totalSteps) * 100)
    Write-Host ""
    Write-Host "  [$script:_stepNum/$script:_totalSteps]  $msg" -ForegroundColor Cyan
    Write-Host ("  " + ([string]::new([char]0x2588, [math]::Floor($pct / 5))) + ([string]::new([char]0x2591, (20 - [math]::Floor($pct / 5)))) + "  ${pct}%") -ForegroundColor DarkCyan
    Write-Host ""
}

function Write-Ok($msg)   { Write-Host "  [OK]  $msg" -ForegroundColor Green }
function Write-Skip($msg) { Write-Host "  [SKIP]  $msg" -ForegroundColor DarkYellow }
function Write-Warn($msg) { Write-Host "  [WARN]  $msg" -ForegroundColor Yellow }
function Write-Err($msg)  { Write-Host "  [FAIL]  $msg" -ForegroundColor Red }

# ── Pre-flight checks ────────────────────────────────────────────────────────

Write-Host ""
Write-Host "========================================================" -ForegroundColor Cyan
Write-Host "       AgentOrchestrator -- Windows Installer            " -ForegroundColor Cyan
Write-Host "========================================================" -ForegroundColor Cyan
Write-Host ""

# Check PowerShell version
if ($PSVersionTable.PSVersion.Major -lt 5) {
    Write-Err "PowerShell 5.0 or newer is required. Current: $($PSVersionTable.PSVersion)"
    Write-Host "  Update via: https://aka.ms/powershell" -ForegroundColor Yellow
    exit 1
}

# Check disk space (need at least 2 GB free)
try {
    $drive = (Get-Item $ProjectRoot).PSDrive
    if ($drive -and $drive.Free) {
        $freeGB = [math]::Round($drive.Free / 1GB, 1)
        if ($freeGB -lt 2) {
            Write-Err "Low disk space: only ${freeGB} GB free. At least 2 GB recommended."
            exit 1
        }
    }
} catch { }

# Check port availability early
function Assert-PortAvailable([int]$Port) {
    if (Get-Command Get-NetTCPConnection -ErrorAction SilentlyContinue) {
        try {
            $conn = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction Stop | Select-Object -First 1
            if ($conn) {
                $proc = Get-Process -Id $conn.OwningProcess -ErrorAction SilentlyContinue
                $pname = if ($proc) { $proc.ProcessName } else { 'unknown' }
                return @{ available = $false; process = $pname; pid = $conn.OwningProcess }
            }
        } catch { }
    }
    return @{ available = $true }
}

$port8001 = Assert-PortAvailable -Port 8001
if (-not $port8001.available) {
    Write-Warn "Port 8001 is in use by $($port8001.process) (PID $($port8001.pid))."
    Write-Host "  This may be a previous AgentOrchestrator instance." -ForegroundColor Yellow
    Write-Host "  The installer will continue -- the app may need a restart after install." -ForegroundColor Yellow
}

Write-Host "  Install location : $ProjectRoot" -ForegroundColor DarkGray
Write-Host "  PowerShell       : $($PSVersionTable.PSVersion)" -ForegroundColor DarkGray
Write-Host ""

# ── Utility functions ────────────────────────────────────────────────────────

function Test-Cmd($name) {
    return [bool](Get-Command $name -ErrorAction SilentlyContinue)
}

function Get-PythonVersionTuple([string]$PythonExe) {
    $out = & $PythonExe -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"
    $parts = ([string]$out).Trim().Split('.')
    if ($parts.Count -lt 2) { return @{ major = 0; minor = 0; text = '0.0' } }
    return @{
        major = [int]$parts[0]
        minor = [int]$parts[1]
        text  = "{0}.{1}" -f [int]$parts[0], [int]$parts[1]
    }
}

function Assert-DirectoryWritable([string]$PathToTest) {
    $probe = Join-Path $PathToTest (".write_test_" + [guid]::NewGuid().ToString('N') + ".tmp")
    try {
        Set-Content -Path $probe -Value "ok" -Encoding UTF8 -ErrorAction Stop
        Remove-Item -Path $probe -Force -ErrorAction SilentlyContinue
    } catch {
        throw "No write permission in '$PathToTest'. Run PowerShell as a user with write access or choose another install location."
    }
}

function Apply-BackendCompatibilityHotfix([string]$BackendFile) {
    if (-not (Test-Path $BackendFile)) { return $false }
    $raw = Get-Content -Path $BackendFile -Raw
    $updated = $raw

    # Python 3.11 compatibility: avoid backslash escapes inside f-string expressions.
    $updated = $updated.Replace("item.Location or '\u2014'", "item.Location or '-'")
    $updated = $updated.Replace("item.Location or '$([char]0x2014)'", "item.Location or '-'")
    $updated = [regex]::Replace(
        $updated,
        'f"Location:\s*\{item\.Location\s+or\s+[^}]+\}\\n"',
        'f"Location: {item.Location or ''-''}\\n"'
    )

    if ($updated -ne $raw) {
        Set-Content -Path $BackendFile -Value $updated -Encoding UTF8
        return $true
    }
    return $false
}

function Test-BackendSyntax([string]$PythonExe, [string]$RootPath) {
    $old = Get-Location
    try {
        Set-Location $RootPath
        & $PythonExe -c "import py_compile; py_compile.compile('backend/main.py', doraise=True)"
        return ($LASTEXITCODE -eq 0)
    } catch {
        return $false
    } finally {
        Set-Location $old
    }
}

# ── Step 1: Python ───────────────────────────────────────────────────────────

Write-Banner "Checking Python installation"

if (-not (Test-Cmd python)) {
    if (Test-Cmd winget) {
        Write-Host "  Python not found. Installing Python 3.11 via winget..." -ForegroundColor Yellow
        Write-Host "  (this may take a minute)" -ForegroundColor DarkGray
        winget install -e --id Python.Python.3.11 --accept-package-agreements --accept-source-agreements
        if ($LASTEXITCODE -ne 0) {
            Write-Err "Python installation failed."
            Write-Host "  Install Python 3.11+ manually from https://python.org/downloads" -ForegroundColor Yellow
            exit 1
        }
        # Refresh PATH
        $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
        Write-Ok "Python installed via winget."
    } else {
        Write-Err "Python is not installed and winget is unavailable."
        Write-Host "  Install Python 3.11+ from https://python.org/downloads" -ForegroundColor Yellow
        exit 1
    }
} else {
    $sysVer = Get-PythonVersionTuple -PythonExe "python"
    Write-Ok "Python $($sysVer.text) found."
}

# ── Step 2: Virtual environment ──────────────────────────────────────────────

Write-Banner "Setting up virtual environment"

if (-not (Test-Path ".venv\Scripts\python.exe")) {
    Write-Host "  Creating virtual environment..." -ForegroundColor DarkGray
    python -m venv .venv
    if (-not (Test-Path ".venv\Scripts\python.exe")) {
        Write-Err "Failed to create virtual environment."
        Write-Host "  Try: python -m venv .venv" -ForegroundColor Yellow
        exit 1
    }
    Write-Ok "Virtual environment created."
} else {
    Write-Ok "Virtual environment already exists."
}

$Py = Join-Path $ProjectRoot ".venv\Scripts\python.exe"

$pyVer = Get-PythonVersionTuple -PythonExe $Py
if ($pyVer.major -lt 3 -or ($pyVer.major -eq 3 -and $pyVer.minor -lt 11)) {
    Write-Err "Python 3.11+ is required, but venv uses $($pyVer.text)."
    Write-Host "  Delete .venv and install Python 3.11+, then re-run the installer." -ForegroundColor Yellow
    exit 1
}

Assert-DirectoryWritable -PathToTest $ProjectRoot

# ── Step 3: Python packages ─────────────────────────────────────────────────

Write-Banner "Installing Python packages"

Write-Host "  Upgrading pip..." -ForegroundColor DarkGray
& $Py -m pip install --upgrade pip --quiet 2>&1 | Out-Null

Write-Host "  Installing requirements (this may take a few minutes)..." -ForegroundColor DarkGray
& $Py -m pip install -r requirements.txt --quiet 2>&1 | ForEach-Object {
    if ($_ -match 'Successfully installed') { Write-Host "  $_" -ForegroundColor DarkGray }
}
if ($LASTEXITCODE -ne 0) {
    Write-Err "Failed to install Python packages."
    Write-Host "  Check your internet connection and try again." -ForegroundColor Yellow
    # Rollback: remove broken venv so next run retries cleanly
    Write-Host "  Removing incomplete virtual environment for clean retry..." -ForegroundColor DarkGray
    Remove-Item -Path ".venv" -Recurse -Force -ErrorAction SilentlyContinue
    exit 1
}
Write-Ok "Python packages installed."

# ── Step 4: Playwright browser ───────────────────────────────────────────────

Write-Banner "Installing browser engine (Playwright Chromium)"

Write-Host "  Downloading Chromium (this may take 1-2 minutes)..." -ForegroundColor DarkGray
& $Py -m playwright install chromium 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
    Write-Warn "Playwright Chromium install failed. Browser skills will be unavailable."
    Write-Host "  You can retry later: .venv\Scripts\python.exe -m playwright install chromium" -ForegroundColor Yellow
} else {
    # Verify browser launch
    Write-Host "  Verifying browser launch..." -ForegroundColor DarkGray
    & $Py -c "from playwright.sync_api import sync_playwright; p = sync_playwright().start(); b = p.chromium.launch(headless=True); b.close(); p.stop()" 2>&1 | Out-Null
    if ($LASTEXITCODE -ne 0) {
        Write-Warn "Playwright Chromium verification failed. Browser skills may not work."
    } else {
        Write-Ok "Playwright Chromium installed and verified."
    }
}

# ── Step 5: Node.js / npx for MCP servers ───────────────────────────────────

Write-Banner "Checking Node.js runtime for MCP servers"

$hasNode = (Test-Cmd node) -or (Test-Cmd node.exe)
$hasNpm = (Test-Cmd npm) -or (Test-Cmd npm.cmd)
$hasNpx = (Test-Cmd npx) -or (Test-Cmd npx.cmd)

if (-not ($hasNode -and $hasNpm -and $hasNpx)) {
    if (Test-Cmd winget) {
        Write-Host "  Node.js / npx not found. Installing Node.js LTS via winget..." -ForegroundColor Yellow
        Write-Host "  (needed for MCP servers such as Filesystem, GitHub, GitLab, SQLite and PostgreSQL)" -ForegroundColor DarkGray
        winget install -e --id OpenJS.NodeJS.LTS --accept-package-agreements --accept-source-agreements
        if ($LASTEXITCODE -ne 0) {
            Write-Warn "Node.js installation failed. npx-based MCP servers will stay unavailable until Node.js LTS is installed manually."
            Write-Host "  Manual install: winget install OpenJS.NodeJS.LTS -e" -ForegroundColor Yellow
        } else {
            $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
            $hasNode = (Test-Cmd node) -or (Test-Cmd node.exe)
            $hasNpm = (Test-Cmd npm) -or (Test-Cmd npm.cmd)
            $hasNpx = (Test-Cmd npx) -or (Test-Cmd npx.cmd)
            if ($hasNode -and $hasNpm -and $hasNpx) {
                Write-Ok "Node.js LTS installed. npx-based MCP servers are available."
            } else {
                Write-Warn "Node.js was installed, but the current shell cannot see node/npm/npx yet. Restart the app or open a new shell if MCP presets still show unavailable."
            }
        }
    } else {
        Write-Warn "winget not available. Install Node.js LTS manually if you want npx-based MCP servers."
        Write-Host "  Download: https://nodejs.org" -ForegroundColor Yellow
    }
} else {
    Write-Ok "Node.js, npm and npx already installed."
}

# ── Step 6: Backend validation ───────────────────────────────────────────────

Write-Banner "Validating backend code"

$backendFile = Join-Path $ProjectRoot "backend\main.py"
$patched = Apply-BackendCompatibilityHotfix -BackendFile $backendFile
if ($patched) {
    Write-Host "  Applied compatibility hotfixes." -ForegroundColor DarkGray
}

if (-not (Test-BackendSyntax -PythonExe $Py -RootPath $ProjectRoot)) {
    $null = Apply-BackendCompatibilityHotfix -BackendFile $backendFile
    if (-not (Test-BackendSyntax -PythonExe $Py -RootPath $ProjectRoot)) {
        Write-Err "Backend syntax validation failed after auto-repair."
        Write-Host "  Re-run the installer with the latest package." -ForegroundColor Yellow
        exit 1
    }
}
Write-Ok "Backend code validated."

# ── Step 7: Ollama (optional local AI) ───────────────────────────────────────

Write-Banner "Setting up Ollama (local AI models)"

if (-not (Test-Cmd ollama)) {
    if (Test-Cmd winget) {
        Write-Host "  Installing Ollama via winget..." -ForegroundColor DarkGray
        winget install -e --id Ollama.Ollama --accept-package-agreements --accept-source-agreements 2>&1 | Out-Null
        if ($LASTEXITCODE -ne 0) {
            Write-Warn "Ollama installation failed. You can still use cloud models."
            Write-Host "  Manual install: https://ollama.com/download/windows" -ForegroundColor Yellow
        } else {
            # Refresh PATH
            $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
            Write-Ok "Ollama installed."
        }
    } else {
        Write-Warn "winget not available. Install Ollama manually if you want local models."
        Write-Host "  Download: https://ollama.com/download/windows" -ForegroundColor Yellow
    }
} else {
    Write-Ok "Ollama already installed."
}

if (Test-Cmd ollama) {
    if (-not $SkipModelPull) {
        # Check which models are already present
        $existing = @()
        try {
            $ollamaList = & ollama list 2>&1
            $existing = ($ollamaList | ForEach-Object { ($_ -split '\s+')[0] }) | Where-Object { $_ -and $_ -ne 'NAME' }
        } catch { }

        $modelsToCheck = @("qwen3.5:9b")
        $modelsToPull = @()
        foreach ($m in $modelsToCheck) {
            $found = $false
            foreach ($e in $existing) {
                if ($e -eq $m -or $e.StartsWith("$m-")) { $found = $true; break }
            }
            if (-not $found) { $modelsToPull += $m }
        }

        if ($modelsToPull.Count -eq 0) {
            Write-Skip "Default models already present."
        } else {
            Write-Host "  Pulling $($modelsToPull.Count) model(s) -- this can take 5-15 minutes..." -ForegroundColor DarkGray
            $mi = 0
            foreach ($model in $modelsToPull) {
                $mi++
                Write-Host "  [$mi/$($modelsToPull.Count)] Pulling $model ..." -ForegroundColor Yellow
                & ollama pull $model
                if ($LASTEXITCODE -eq 0) {
                    Write-Ok "$model downloaded."
                } else {
                    Write-Warn "Failed to pull $model. You can retry later: ollama pull $model"
                }
            }
        }
    } else {
        Write-Skip "Model pull skipped (-SkipModelPull)."
    }
} else {
    Write-Skip "Ollama not available -- skipping model pull."
}

# ── Step 8: Configuration ───────────────────────────────────────────────────

Write-Banner "Creating configuration"

$settingsFile = Join-Path $ProjectRoot "settings.json"
$templateFile = Join-Path $ProjectRoot "settings.template.json"
if (-not (Test-Path $settingsFile) -and (Test-Path $templateFile)) {
    Copy-Item -Path $templateFile -Destination $settingsFile
    Write-Ok "Created settings.json from template."
    Write-Host "  Configure your API keys and integrations via the app Settings panel." -ForegroundColor DarkGray
} else {
    Write-Ok "settings.json already exists."
}

# ── Step 9: Shortcuts ────────────────────────────────────────────────────────

Write-Banner "Creating shortcuts"

function New-AppShortcut([string]$ShortcutPath, [string]$TargetPath, [string]$Arguments, [string]$WorkingDir, [string]$IconPath, [string]$Description) {
    try {
        $shell = New-Object -ComObject WScript.Shell
        $lnk = $shell.CreateShortcut($ShortcutPath)
        $lnk.TargetPath = $TargetPath
        if ($Arguments) { $lnk.Arguments = $Arguments }
        $lnk.WorkingDirectory = $WorkingDir
        if ($IconPath -and (Test-Path $IconPath)) { $lnk.IconLocation = $IconPath }
        $lnk.Description = $Description
        $lnk.Save()
        [System.Runtime.InteropServices.Marshal]::ReleaseComObject($shell) | Out-Null
        return $true
    } catch {
        Write-Warn "Could not create shortcut at $ShortcutPath : $_"
        return $false
    }
}

$batLauncher = Join-Path $ProjectRoot "launch_agent.bat"
$shortcutName = "AgentOrchestrator.lnk"
$description = "Start AgentOrchestrator server and open the app"

$iconFile = Join-Path $ProjectRoot "app.ico"
if (-not (Test-Path $iconFile)) {
    $iconFile = Join-Path $ProjectRoot "frontend" "app.ico"
}
if (-not (Test-Path $iconFile)) { $iconFile = "" }

# Desktop shortcut
$desktopPath = [Environment]::GetFolderPath('Desktop')
if ($desktopPath) {
    $desktopLnk = Join-Path $desktopPath $shortcutName
    if (New-AppShortcut -ShortcutPath $desktopLnk -TargetPath $batLauncher -Arguments "" -WorkingDir $ProjectRoot -IconPath $iconFile -Description $description) {
        Write-Ok "Desktop shortcut created."
    }
}

# Start Menu shortcut
$startMenuPath = Join-Path ([Environment]::GetFolderPath('Programs')) "AgentOrchestrator"
if (-not (Test-Path $startMenuPath)) { New-Item -ItemType Directory -Path $startMenuPath -Force | Out-Null }
$startMenuLnk = Join-Path $startMenuPath $shortcutName
if (New-AppShortcut -ShortcutPath $startMenuLnk -TargetPath $batLauncher -Arguments "" -WorkingDir $ProjectRoot -IconPath $iconFile -Description $description) {
    Write-Ok "Start Menu shortcut created."
}

# ── Done ─────────────────────────────────────────────────────────────────────

Write-Host ""
Write-Host "========================================================" -ForegroundColor Green
Write-Host "           Installation complete!                        " -ForegroundColor Green
Write-Host "========================================================" -ForegroundColor Green
Write-Host ""
Write-Host "  Start the app:" -ForegroundColor Cyan
Write-Host "    - Desktop shortcut: AgentOrchestrator" -ForegroundColor White
Write-Host "    - Start Menu: AgentOrchestrator" -ForegroundColor White
Write-Host "    - Or run: .\.venv\Scripts\python.exe .\start.py" -ForegroundColor DarkGray
Write-Host ""
Write-Host "  First time? The app will guide you through setup in the browser." -ForegroundColor DarkGray
Write-Host ""
