#Requires -RunAsAdministrator
param([string]$InstallDir = "C:\DeployPro")

$ErrorActionPreference = "Stop"
$ProgressPreference    = "SilentlyContinue"

$AppName       = "DeployPro"
$PoolName      = "DeployPro"
$SiteName      = "DeployPro"
$DefaultPort   = 7070
$NodeMinVer    = 18
$NodeLtsUrl    = "https://nodejs.org/dist/v20.11.1/node-v20.11.1-x64.msi"
$IisnodeUrl    = "https://github.com/Azure/iisnode/releases/download/v0.2.26/iisnode-full-v0.2.26-x64.msi"
$UrlRewriteUrl = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_en-US.msi"

function Write-Step($n, $msg) { Write-Host ""; Write-Host "  [$n] $msg" -ForegroundColor Cyan }
function Write-OK($msg)   { Write-Host "      OK  $msg" -ForegroundColor Green }
function Write-Info($msg) { Write-Host "      >>  $msg" -ForegroundColor Gray }
function Write-Warn($msg) { Write-Host "      !!  $msg" -ForegroundColor Yellow }
function Write-Fail($msg) { Write-Host "      ERR $msg" -ForegroundColor Red ; exit 1 }

function Install-Msi {
    param([string]$Url, [string]$Label)
    $tmp = Join-Path $env:TEMP "$Label.msi"
    Write-Info "Downloading $Label..."
    Invoke-WebRequest -Uri $Url -OutFile $tmp -UseBasicParsing
    Write-Info "Installing $Label (silent)..."
    $p = Start-Process msiexec.exe -ArgumentList "/i `"$tmp`" /qn /norestart" -Wait -PassThru -NoNewWindow
    Remove-Item $tmp -Force -ErrorAction SilentlyContinue
    if ($p.ExitCode -ne 0 -and $p.ExitCode -ne 3010) {
        Write-Warn "$Label installer exit code $($p.ExitCode) -- may already be installed or need reboot."
    } else {
        Write-OK "$Label installed"
    }
}

Write-Host ""
Write-Host "  +=============================================+" -ForegroundColor DarkCyan
Write-Host "  |   DeployPro IIS Installer                  |" -ForegroundColor DarkCyan
Write-Host "  +=============================================+" -ForegroundColor DarkCyan
Write-Host ""
Write-Host "  Install directory : $InstallDir"
Write-Host "  IIS site port     : $DefaultPort"
Write-Host ""

# Step 1: Node.js
Write-Step 1 "Checking Node.js..."
$nodeExePath = $null
$nodeOk = $false
try {
    $nodeVer = & node --version 2>$null
    if ($nodeVer -match "v(\d+)") {
        if ([int]$Matches[1] -ge $NodeMinVer) {
            $nodeExePath = (Get-Command node -ErrorAction SilentlyContinue).Source
            Write-OK "Node.js $nodeVer — $nodeExePath"
            $nodeOk = $true
        } else {
            Write-Warn "Node.js $nodeVer too old (need v$NodeMinVer+). Will upgrade."
        }
    }
} catch {}

if (-not $nodeOk) {
    Install-Msi -Url $NodeLtsUrl -Label "nodejs-lts"
    $env:Path = [System.Environment]::GetEnvironmentVariable("Path",[System.EnvironmentVariableTarget]::Machine) + ";" +
                [System.Environment]::GetEnvironmentVariable("Path",[System.EnvironmentVariableTarget]::User)
    $nodeExePath = (Get-Command node -ErrorAction SilentlyContinue).Source
    Write-OK "Node.js installed — $nodeExePath"
}
if (-not $nodeExePath) { $nodeExePath = "C:\Program Files\nodejs\node.exe" }  # last resort fallback

# Step 2: IIS Features
Write-Step 2 "Enabling IIS features..."
$requiredFeatures = @(
    "IIS-WebServer",
    "IIS-WebServerManagementTools",
    "IIS-ManagementConsole",
    "IIS-DefaultDocument",
    "IIS-StaticContent",
    "IIS-ISAPIExtensions",
    "IIS-ISAPIFilter",
    "IIS-HttpCompressionStatic",
    "IIS-HttpLogging",
    "IIS-RequestFiltering",
    "IIS-WebSockets"
)
foreach ($f in $requiredFeatures) {
    $feat = Get-WindowsOptionalFeature -Online -FeatureName $f -ErrorAction SilentlyContinue
    if (-not ($feat -and $feat.State -eq "Enabled")) {
        Enable-WindowsOptionalFeature -Online -FeatureName $f -NoRestart -ErrorAction SilentlyContinue | Out-Null
    }
}
Write-OK "IIS features enabled"

# Step 3: URL Rewrite
Write-Step 3 "Checking URL Rewrite module..."
if (Test-Path "HKLM:\SOFTWARE\Microsoft\IIS Extensions\URL Rewrite") {
    Write-OK "URL Rewrite already installed"
} else {
    Install-Msi -Url $UrlRewriteUrl -Label "url-rewrite"
}

# Step 4: iisnode
Write-Step 4 "Checking iisnode..."
if ((Test-Path "HKLM:\SOFTWARE\Microsoft\IISNode") -or (Test-Path "$env:ProgramFiles\iisnode\iisnode.dll")) {
    Write-OK "iisnode already installed"
} else {
    Install-Msi -Url $IisnodeUrl -Label "iisnode"
}

# Step 5: Copy files
Write-Step 5 "Installing DeployPro to $InstallDir..."
# Script lives in installer\ subfolder — source root is one level up
$ScriptDir   = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
$excludeDirs = @("installer","node_modules","data","uploads","backups","logs","dist","daemon")

New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null

Get-ChildItem -Path $ScriptDir -Recurse | Where-Object {
    $rel      = $_.FullName.Substring($ScriptDir.Length).TrimStart([System.IO.Path]::DirectorySeparatorChar)
    $topLevel = $rel.Split([System.IO.Path]::DirectorySeparatorChar)[0]
    $topLevel -notin $excludeDirs
} | ForEach-Object {
    $rel  = $_.FullName.Substring($ScriptDir.Length).TrimStart([System.IO.Path]::DirectorySeparatorChar)
    $dest = Join-Path $InstallDir $rel
    if ($_.PSIsContainer) {
        New-Item -ItemType Directory -Path $dest -Force | Out-Null
    } else {
        $destDir = Split-Path $dest -Parent
        New-Item -ItemType Directory -Path $destDir -Force | Out-Null
        Copy-Item $_.FullName -Destination $dest -Force
    }
}

foreach ($d in @("data","uploads","backups","logs","logs\iisnode","dist")) {
    New-Item -ItemType Directory -Path (Join-Path $InstallDir $d) -Force | Out-Null
}

# Copy the dist ZIP into the installed app so the landing-page download button works
$srcZip = Join-Path $ScriptDir "dist\deploypro-setup.zip"
if (Test-Path $srcZip) {
    Copy-Item $srcZip -Destination (Join-Path $InstallDir "dist\deploypro-setup.zip") -Force
    Write-OK "dist\deploypro-setup.zip deployed to $InstallDir"
}

# Patch web.config — use 8.3 short path to avoid spaces-in-path issues with iisnode
$shortNodePath = $nodeExePath
try {
    $fso           = New-Object -ComObject Scripting.FileSystemObject
    $shortNodePath = $fso.GetFile($nodeExePath).ShortPath
} catch {}
# Manual fallback: replace known long name segment
if ($shortNodePath -eq $nodeExePath) {
    $shortNodePath = $nodeExePath -replace [regex]::Escape("C:\Program Files\"), "C:\PROGRA~1\"
    $shortNodePath = $shortNodePath -replace [regex]::Escape("C:\Program Files (x86)\"), "C:\PROGRA~2\"
}
$webConfigPath = Join-Path $InstallDir "web.config"
$webContent    = Get-Content $webConfigPath -Raw
$webContent    = $webContent -replace 'nodeProcessCommandLine="[^"]*"', "nodeProcessCommandLine=`"$shortNodePath`""
[System.IO.File]::WriteAllText($webConfigPath, $webContent, [System.Text.Encoding]::UTF8)
Write-OK "Files copied to $InstallDir"
Write-OK "web.config patched  ->  node: $shortNodePath"

# Step 6: npm install
Write-Step 6 "Installing npm dependencies..."
Push-Location $InstallDir
$prevEAP = $ErrorActionPreference
$ErrorActionPreference = "Continue"
& npm install --omit=dev --no-audit --no-fund 2>&1 | Where-Object { $_ -notmatch '^npm (warn|notice)' } | ForEach-Object { Write-Info $_ }
$ErrorActionPreference = $prevEAP
Pop-Location
Write-OK "npm install complete"

# Step 7: JWT secret
Write-Step 7 "Generating JWT secret..."
$chars  = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
$secret = -join (1..64 | ForEach-Object { $chars[(Get-Random -Max $chars.Length)] })
[System.Environment]::SetEnvironmentVariable("JWT_SECRET", $secret, [System.EnvironmentVariableTarget]::Machine)
Write-OK "JWT_SECRET set in system environment"

# Step 8: App Pool
Write-Step 8 "Configuring IIS Application Pool..."
Import-Module WebAdministration -ErrorAction Stop

$poolPath = "IIS:\AppPools\$PoolName"
if (Test-Path $poolPath) {
    Remove-WebAppPool -Name $PoolName -ErrorAction SilentlyContinue
    Start-Sleep 2
}
New-WebAppPool -Name $PoolName | Out-Null
Set-ItemProperty $poolPath managedRuntimeVersion ""
Set-ItemProperty $poolPath enable32BitAppOnWin64 $false
Set-ItemProperty $poolPath processModel.idleTimeout "00:00:00"
Write-OK "Application pool '$PoolName' created (No Managed Code)"

# Step 9: IIS Site
Write-Step 9 "Creating IIS Site '$SiteName' on port $DefaultPort..."
$existingSite = Get-Website -Name $SiteName -ErrorAction SilentlyContinue
if ($existingSite) { Remove-Website -Name $SiteName }
New-Website -Name $SiteName -Port $DefaultPort -PhysicalPath $InstallDir -ApplicationPool $PoolName | Out-Null
Write-OK "Site '$SiteName' created -> http://localhost:$DefaultPort"

# Step 10: Permissions
Write-Step 10 "Setting folder permissions..."
$appPoolIdentity = "IIS AppPool\$PoolName"
& icacls $InstallDir /grant "${appPoolIdentity}:(OI)(CI)M" /T /Q
Write-OK "App directory permissions granted for '$appPoolIdentity'"

# Grant Read+Execute on Node.js dir so App Pool identity can run node.exe
$nodeDir = Split-Path $nodeExePath -Parent
if (Test-Path $nodeDir) {
    & icacls $nodeDir /grant "${appPoolIdentity}:(OI)(CI)RX" /T /Q
    Write-OK "Node.js directory RX permissions granted"
}

# Step 11: Firewall
Write-Step 11 "Configuring firewall..."
if (Get-NetFirewallRule -DisplayName $AppName -ErrorAction SilentlyContinue) {
    Write-OK "Firewall rule already exists"
} else {
    New-NetFirewallRule -DisplayName $AppName -Description "Allow DeployPro on port $DefaultPort" `
        -Direction Inbound -Protocol TCP -LocalPort $DefaultPort -Action Allow | Out-Null
    Write-OK "Firewall rule created for port $DefaultPort"
}

# Step 12: Start
Write-Step 12 "Starting IIS site..."
Start-Website -Name $SiteName -ErrorAction SilentlyContinue
Start-Sleep 3
Restart-WebAppPool -Name $PoolName -ErrorAction SilentlyContinue
Start-Sleep 2

$site = Get-Website -Name $SiteName
if ($site.State -eq "Started") { Write-OK "Site is running" }
else { Write-Warn "Site state: $($site.State). Check IIS Manager." }

Write-Host ""
Write-Host "  +=============================================+" -ForegroundColor Green
Write-Host "  |   Installation Complete!                   |" -ForegroundColor Green
Write-Host "  +=============================================+" -ForegroundColor Green
Write-Host ""
Write-Host "  URL           : http://localhost:$DefaultPort" -ForegroundColor White
Write-Host "  Default login : admin / Admin@123"            -ForegroundColor Yellow
Write-Host "  Install path  : $InstallDir"                  -ForegroundColor White
Write-Host ""
Write-Host "  IMPORTANT: Change the default admin password immediately!" -ForegroundColor Red
Write-Host ""

Start-Process "http://localhost:$DefaultPort"