[CmdletBinding()]
param(
    [switch]$RunMigrations,
    [switch]$BootstrapAdmin
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

function Write-Step {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Message
    )

    Write-Host ""
    Write-Host "==> $Message" -ForegroundColor Cyan
}

function Invoke-ExternalCommand {
    param(
        [Parameter(Mandatory = $true)]
        [string]$FilePath,
        [string[]]$Arguments = @(),
        [string]$Description = $FilePath
    )

    Write-Host "-> $Description" -ForegroundColor DarkGray
    & $FilePath @Arguments
    if ($LASTEXITCODE -ne 0) {
        throw "Command failed with exit code ${LASTEXITCODE}: $FilePath $($Arguments -join ' ')"
    }
}

function Find-PythonLauncher {
    $pyLauncher = Get-Command py -ErrorAction SilentlyContinue
    if ($pyLauncher) {
        return @{
            FilePath = $pyLauncher.Source
            Arguments = @("-3.12")
            Label = "py -3.12"
        }
    }

    $python = Get-Command python -ErrorAction SilentlyContinue
    if ($python) {
        return @{
            FilePath = $python.Source
            Arguments = @()
            Label = "python"
        }
    }

    throw "Could not find 'py' or 'python' in PATH."
}

function Get-DotEnvValues {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    $values = @{}

    foreach ($line in Get-Content -Path $Path) {
        $trimmed = $line.Trim()
        if (-not $trimmed -or $trimmed.StartsWith("#")) {
            continue
        }

        $content = $trimmed
        if ($content.StartsWith("export ")) {
            $content = $content.Substring(7).Trim()
        }

        $separatorIndex = $content.IndexOf("=")
        if ($separatorIndex -lt 1) {
            continue
        }

        $name = $content.Substring(0, $separatorIndex).Trim()
        $value = $content.Substring($separatorIndex + 1).Trim()

        if (
            ($value.StartsWith('"') -and $value.EndsWith('"')) -or
            ($value.StartsWith("'") -and $value.EndsWith("'"))
        ) {
            $value = $value.Substring(1, $value.Length - 2)
        }

        $values[$name] = $value
    }

    return $values
}

function Get-EffectiveEnvValue {
    param(
        [Parameter(Mandatory = $true)]
        [hashtable]$Values,
        [Parameter(Mandatory = $true)]
        [string]$Name,
        [string]$Default = ""
    )

    if ($Values.ContainsKey($Name) -and -not [string]::IsNullOrWhiteSpace([string]$Values[$Name])) {
        return [string]$Values[$Name]
    }

    $processValue = [Environment]::GetEnvironmentVariable($Name)
    if (-not [string]::IsNullOrWhiteSpace($processValue)) {
        return [string]$processValue
    }

    return $Default
}

function Resolve-MongoUri {
    param(
        [Parameter(Mandatory = $true)]
        [hashtable]$Values,
        [Parameter(Mandatory = $true)]
        [string]$DatabaseName
    )

    $explicitMongoUri = Get-EffectiveEnvValue -Values $Values -Name "MONGO_URI"
    if (-not [string]::IsNullOrWhiteSpace($explicitMongoUri)) {
        return @{
            Value = $explicitMongoUri
            Source = "MONGO_URI"
        }
    }

    $mongoPassword = Get-EffectiveEnvValue -Values $Values -Name "MONGO_APP_PASSWORD"
    if (-not [string]::IsNullOrWhiteSpace($mongoPassword)) {
        $mongoUsername = Get-EffectiveEnvValue -Values $Values -Name "MONGO_APP_USERNAME" -Default "epicrisis_app"
        $mongoDatabase = Get-EffectiveEnvValue -Values $Values -Name "MONGO_APP_DATABASE" -Default $DatabaseName
        return @{
            Value = "mongodb://${mongoUsername}:${mongoPassword}@127.0.0.1:27017/${DatabaseName}?authSource=${mongoDatabase}"
            Source = "derived from MONGO_APP_*"
        }
    }

    return @{
        Value = "mongodb://localhost:27017"
        Source = "default localhost"
    }
}

$scriptDirectory = Split-Path -Parent $MyInvocation.MyCommand.Path
$repoRoot = Split-Path -Parent $scriptDirectory
Set-Location $repoRoot

$uvCommand = Get-Command uv -ErrorAction SilentlyContinue
$executionBackend = if ($uvCommand) { "uv" } else { "python" }
$venvPath = Join-Path $repoRoot ".venv"
$venvPython = Join-Path $venvPath "Scripts\python.exe"
$envPath = Join-Path $repoRoot ".env"
$soatPdfPath = Join-Path $repoRoot "Manual-SOAT-2025.pdf"

Write-Step "Resolved repository root"
Write-Host $repoRoot

Write-Step "Detecting execution backend"
if ($uvCommand) {
    Write-Host "Using uv from PATH." -ForegroundColor Green
} else {
    Write-Host "uv not found. Falling back to Python virtual environment commands." -ForegroundColor Yellow
}

if (-not (Test-Path $venvPython)) {
    Write-Step "Creating .venv"

    if ($uvCommand) {
        Invoke-ExternalCommand -FilePath $uvCommand.Source -Arguments @("venv", "--python", "3.12", ".venv") -Description "uv venv --python 3.12 .venv"
    } else {
        $pythonLauncher = Find-PythonLauncher
        $venvArguments = @()
        if ($pythonLauncher.Arguments.Count -gt 0) {
            $venvArguments += $pythonLauncher.Arguments
        }
        $venvArguments += @("-m", "venv", ".venv")
        Invoke-ExternalCommand -FilePath $pythonLauncher.FilePath -Arguments $venvArguments -Description "$($pythonLauncher.Label) -m venv .venv"
    }
} else {
    Write-Step "Using existing .venv"
    Write-Host $venvPath -ForegroundColor Green
}

if (-not (Test-Path $venvPython)) {
    throw "Virtual environment was not created correctly. Missing file: $venvPython"
}

Write-Step "Step 4/10 - Installing dependencies"
if ($uvCommand) {
    Invoke-ExternalCommand -FilePath $uvCommand.Source -Arguments @("sync", "--dev") -Description "uv sync --dev"
} else {
    Invoke-ExternalCommand -FilePath $venvPython -Arguments @("-m", "pip", "install", "--upgrade", "pip") -Description "python -m pip install --upgrade pip"
    Invoke-ExternalCommand -FilePath $venvPython -Arguments @("-m", "pip", "install", "-r", "requirements.txt", "-r", "requirements-dev.txt") -Description "pip install -r requirements.txt -r requirements-dev.txt"
}

Write-Step "Step 5/10 - Validating .env"
if (-not (Test-Path $envPath)) {
    throw "Missing .env file at $envPath"
}

$envValues = Get-DotEnvValues -Path $envPath
$dbName = Get-EffectiveEnvValue -Values $envValues -Name "DB_NAME" -Default "Epicrisis"
$mongoUriResolution = Resolve-MongoUri -Values $envValues -DatabaseName $dbName
$mongoUri = [string]$mongoUriResolution.Value
$mongoUriSource = [string]$mongoUriResolution.Source

if ($mongoUriSource -ne "MONGO_URI") {
    Write-Host "MongoDB connection will use $mongoUriSource." -ForegroundColor DarkGray
}

Write-Host ".env file found. Continuing with effective runtime defaults where needed." -ForegroundColor Green

Write-Step "Step 6/10 - Verifying MongoDB connectivity"
$mongoPingCode = @'
from pymongo import MongoClient
import sys

mongo_uri = sys.argv[1].strip()
db_name = sys.argv[2].strip()

if not mongo_uri:
    raise SystemExit("MONGO_URI is empty.")
if not db_name:
    raise SystemExit("DB_NAME is empty.")

client = MongoClient(mongo_uri, serverSelectionTimeoutMS=5000)
try:
    client.admin.command("ping")
    client[db_name].command("ping")
finally:
    client.close()

print(f"MongoDB connection OK for database '{db_name}'.")
'@
$mongoPingScriptPath = [System.IO.Path]::ChangeExtension([System.IO.Path]::GetTempFileName(), ".py")
try {
    Set-Content -Path $mongoPingScriptPath -Value $mongoPingCode -Encoding UTF8
    Invoke-ExternalCommand -FilePath $venvPython -Arguments @($mongoPingScriptPath, $mongoUri, $dbName) -Description "MongoDB ping"
} finally {
    if (Test-Path $mongoPingScriptPath) {
        Remove-Item -Path $mongoPingScriptPath -Force
    }
}

Write-Step "Step 7/10 - Building FAISS indexes"
Invoke-ExternalCommand -FilePath $venvPython -Arguments @("-m", "modules.processing.cie10.Carga_estructura") -Description "Build CIE-10 index"
Invoke-ExternalCommand -FilePath $venvPython -Arguments @("-m", "modules.processing.cie10.carga_cups") -Description "Build CUPS index"

if (-not (Test-Path $soatPdfPath)) {
    throw "Missing SOAT source PDF: $soatPdfPath"
}

Invoke-ExternalCommand -FilePath $venvPython -Arguments @("-m", "modules.processing.soat.soat_indexer", "--pdf", $soatPdfPath, "--out", "soat_faiss") -Description "Build SOAT index"

if ($RunMigrations) {
    Write-Step "Step 8/10 - Running MongoDB migrations"
    Invoke-ExternalCommand -FilePath $venvPython -Arguments @("-m", "app.migrations.run") -Description "Run MongoDB migrations"
} else {
    Write-Step "Step 8/10 - Skipped MongoDB migrations"
    Write-Host "Pass -RunMigrations to execute this step." -ForegroundColor Yellow
}

if ($BootstrapAdmin) {
    Write-Step "Step 9/10 - Bootstrapping admin user"
    Invoke-ExternalCommand -FilePath $venvPython -Arguments @("-m", "app.scripts.bootstrap_admin") -Description "Bootstrap admin user"
} else {
    Write-Step "Step 9/10 - Skipped admin bootstrap"
    Write-Host "Pass -BootstrapAdmin to execute this step." -ForegroundColor Yellow
}

Write-Step "Step 10/10 - Warming up heavy resources"
Invoke-ExternalCommand -FilePath $venvPython -Arguments @("-m", "app.scripts.warmup_resources", "--resource", "cie10", "--resource", "cups", "--resource", "soat") -Description "Warm up CIE-10, CUPS and SOAT resources"

$productionCommand = if ($uvCommand) {
    "uv run uvicorn app.main:app --host 0.0.0.0 --port 7000"
} else {
    ".venv\Scripts\python.exe -m uvicorn app.main:app --host 0.0.0.0 --port 7000"
}

Write-Step "Installation summary"
Write-Host "Execution backend: $executionBackend"
Write-Host "Run migrations: $($RunMigrations.IsPresent)"
Write-Host "Bootstrap admin: $($BootstrapAdmin.IsPresent)"
Write-Host ""
Write-Host "Production startup command:" -ForegroundColor Green
Write-Host $productionCommand
