#Requires -RunAsAdministrator
<#
.SYNOPSIS
    ServerPanel Installer
.DESCRIPTION
    Installs ServerPanel as a Windows Service on port 4080
.NOTES
    Must be run as Administrator
#>

$ErrorActionPreference = 'Stop'
$Host.UI.RawUI.WindowTitle = 'ServerPanel Installer'

# ── Colors ────────────────────────────────────────────────
function Write-Header  { param($msg) Write-Host "`n$msg" -ForegroundColor Cyan }
function Write-Success { param($msg) Write-Host "  ✓ $msg" -ForegroundColor Green }
function Write-Info    { param($msg) Write-Host "  → $msg" -ForegroundColor White }
function Write-Warn    { param($msg) Write-Host "  ⚠ $msg" -ForegroundColor Yellow }
function Write-Fail    { param($msg) Write-Host "  ✗ $msg" -ForegroundColor Red }

# ── Banner ─────────────────────────────────────────────────
Clear-Host
Write-Host @"
╔══════════════════════════════════════════════╗
║          ServerPanel Installer v1.0          ║
║      Windows Server Management Panel        ║
╚══════════════════════════════════════════════╝
"@ -ForegroundColor Cyan

# ── Configuration ──────────────────────────────────────────
$INSTALL_DIR = 'D:\panel'
$PORT        = 4080
$SERVICE_NAME= 'ServerPanel'

# ── Step 1: Check if running as Admin ──────────────────────
Write-Header "Step 1 — Checking prerequisites"
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
    Write-Fail "Please run this script as Administrator"
    exit 1
}
Write-Success "Running as Administrator"

# Check IIS is installed (panel manages IIS — it must be present)
$iisFeature = Get-WindowsOptionalFeature -Online -FeatureName IIS-WebServerRole -ErrorAction SilentlyContinue
if (-not $iisFeature -or $iisFeature.State -ne 'Enabled') {
    Write-Warn "IIS is not installed. This panel requires IIS to manage sites and app pools."
    $installIIS = Read-Host "  Install IIS now? (Y/n)"
    if ($installIIS -ne 'n') {
        Write-Info "Installing IIS..."
        Enable-WindowsOptionalFeature -Online -FeatureName IIS-WebServerRole,IIS-WebServer,IIS-CommonHttpFeatures,IIS-ManagementConsole -All -NoRestart | Out-Null
        Write-Success "IIS installed"
    } else {
        Write-Warn "Continuing without IIS — some panel features will not work"
    }
} else {
    Write-Success "IIS is installed"
}

# ── Step 2: Check / Install Node.js ────────────────────────
Write-Header "Step 2 — Checking Node.js"
$nodeInstalled = $false
try {
    $nodeVer = & node --version 2>$null
    if ($nodeVer) {
        Write-Success "Node.js found: $nodeVer"
        $nodeInstalled = $true
    }
} catch {}

if (-not $nodeInstalled) {
    Write-Info "Node.js not found. Installing via Chocolatey..."

    # Install Chocolatey if not present
    if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
        Write-Info "Installing Chocolatey..."
        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'))
        Write-Success "Chocolatey installed"
    }

    Write-Info "Installing Node.js LTS..."
    choco install nodejs-lts -y --no-progress
    # Refresh environment
    $env:Path = [System.Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path','User')
    Write-Success "Node.js installed"
}

# ── Step 3: Set install directory ──────────────────────────
Write-Header "Step 3 — Setting up installation directory"
$customDir = Read-Host "  Install directory [$INSTALL_DIR]"
if ($customDir -and $customDir.Trim()) { $INSTALL_DIR = $customDir.Trim() }

if (Test-Path $INSTALL_DIR) {
    $overwrite = Read-Host "  Directory exists. Overwrite? (y/N)"
    if ($overwrite -ne 'y') {
        Write-Info "Installation cancelled."
        exit 0
    }
    Write-Info "Removing existing installation..."
    # Stop service if running
    if (Get-Service -Name $SERVICE_NAME -ErrorAction SilentlyContinue) {
        Stop-Service -Name $SERVICE_NAME -Force -ErrorAction SilentlyContinue
        sc.exe delete $SERVICE_NAME | Out-Null
        Start-Sleep -Seconds 2
    }
}

New-Item -ItemType Directory -Path $INSTALL_DIR -Force | Out-Null
New-Item -ItemType Directory -Path "$INSTALL_DIR\data" -Force | Out-Null
New-Item -ItemType Directory -Path "$INSTALL_DIR\data\backups" -Force | Out-Null
Write-Success "Directory created: $INSTALL_DIR"

# ── Step 4: Copy panel files ────────────────────────────────
Write-Header "Step 4 — Copying panel files"
$SOURCE_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path

# Check if source files exist
if (-not (Test-Path "$SOURCE_DIR\server.js")) {
    Write-Fail "server.js not found in $SOURCE_DIR"
    Write-Info "Make sure install-panel.ps1 is in the same folder as server.js"
    exit 1
}

$filesToCopy = @(
    'server.js', 'crashMonitor.js', 'package.json'
)
foreach ($f in $filesToCopy) {
    if (Test-Path "$SOURCE_DIR\$f") {
        Copy-Item "$SOURCE_DIR\$f" "$INSTALL_DIR\$f" -Force
        Write-Success "Copied $f"
    }
}

# Copy folders
foreach ($folder in @('routes', 'scripts', 'middleware', 'client')) {
    if (Test-Path "$SOURCE_DIR\$folder") {
        Copy-Item "$SOURCE_DIR\$folder" "$INSTALL_DIR\$folder" -Recurse -Force
        Write-Success "Copied $folder\"
    }
}
Write-Success "All files copied"

# ── Step 5: npm install ─────────────────────────────────────
Write-Header "Step 5 — Installing Node.js packages"
Write-Info "Running npm install (this may take a minute)..."
Set-Location $INSTALL_DIR
& npm install 2>&1 | Out-Null
if ($LASTEXITCODE -ne 0) {
    Write-Fail "npm install failed. Check your internet connection and try again."
    exit 1
}
Write-Success "Packages installed"

# ── Step 6: Configure admin account ────────────────────────
Write-Header "Step 6 — Configure admin account"
$adminUser = Read-Host "  Admin username [admin]"
if (-not $adminUser) { $adminUser = 'admin' }

$adminPass = ''
while ($adminPass.Length -lt 6) {
    $secPass   = Read-Host "  Admin password (min 6 chars)" -AsSecureString
    $adminPass = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
                 [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secPass))
    if ($adminPass.Length -lt 6) { Write-Warn "Password must be at least 6 characters" }
}

Write-Info "Hashing password..."
$hashScript = @"
const b = require('bcryptjs');
process.stdout.write(b.hashSync('$adminPass', 10));
"@
$hash = & node -e $hashScript
Write-Success "Password hashed"

# ── Step 7: Generate .env file ──────────────────────────────
Write-Header "Step 7 — Generating configuration"

# Custom port
$customPort = Read-Host "  Port [$PORT]"
if ($customPort -and [int]::TryParse($customPort, [ref]$null)) { $PORT = [int]$customPort }

# Generate JWT secret
$jwtSecret = -join ((1..64) | ForEach-Object { '{0:x}' -f (Get-Random -Maximum 16) })

$envContent = @"
PORT=$PORT
JWT_SECRET=$jwtSecret
ADMIN_USER=$adminUser
ADMIN_PASS_HASH=$hash
"@
Set-Content -Path "$INSTALL_DIR\.env" -Value $envContent -Encoding UTF8
Write-Success ".env file created"

# ── Step 8: Install as Windows Service ─────────────────────
Write-Header "Step 8 — Installing Windows Service"

$installAsService = Read-Host "  Install as Windows Service (auto-start on boot)? (Y/n)"
if ($installAsService -ne 'n') {
    # Find node.exe path
    $nodePath = (Get-Command node).Source

    # Create service using sc.exe
    $svcArgs = '"' + $nodePath + '" "' + $INSTALL_DIR + '\server.js"'

    sc.exe create $SERVICE_NAME binPath= $svcArgs start= auto DisplayName= "ServerPanel" | Out-Null
    sc.exe description $SERVICE_NAME "Windows Server Management Panel" | Out-Null
    # Auto-restart on failure: restart after 5s, reset failure count after 1 hour
    sc.exe failure $SERVICE_NAME reset= 3600 actions= restart/5000/restart/5000/restart/5000 | Out-Null

    # Set working directory via registry
    $regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$SERVICE_NAME"
    if (Test-Path $regPath) {
        Set-ItemProperty -Path $regPath -Name "AppDirectory" -Value $INSTALL_DIR -ErrorAction SilentlyContinue
    }

    # Start the service
    Start-Service -Name $SERVICE_NAME -ErrorAction SilentlyContinue
    Start-Sleep -Seconds 3

    $svc = Get-Service -Name $SERVICE_NAME -ErrorAction SilentlyContinue
    if ($svc -and $svc.Status -eq 'Running') {
        Write-Success "Service installed and running"
    } else {
        Write-Warn "Service installed but not running. Start manually: node $INSTALL_DIR\server.js"
    }
} else {
    Write-Info "Skipped. Start manually: node $INSTALL_DIR\server.js"
}

# ── Step 9: Open firewall port ──────────────────────────────
Write-Header "Step 9 — Configuring firewall"
try {
    $existing = Get-NetFirewallRule -DisplayName "ServerPanel-Port-$PORT" -ErrorAction SilentlyContinue
    if (-not $existing) {
        New-NetFirewallRule -DisplayName "ServerPanel-Port-$PORT" `
            -Direction Inbound -Action Allow -Protocol TCP -LocalPort $PORT | Out-Null
        Write-Success "Firewall rule added for port $PORT"
    } else {
        Write-Success "Firewall rule already exists"
    }
} catch {
    Write-Warn "Could not add firewall rule: $_"
    Write-Info "Add manually: netsh advfirewall firewall add rule name=ServerPanel dir=in action=allow protocol=TCP localport=$PORT"
}

# ── Step 10: Seed users.json ────────────────────────────────
Write-Header "Step 10 — Creating admin user"
$ALL_PERMS = '["dashboard","processes","logs","sites","apppools","domains","ssl","services","database","files","smtp","scheduler","security","traffic","docker","dotnet","users","settings"]'
$usersJson = @"
[
  {
    "id": 1,
    "username": "$adminUser",
    "password": "$hash",
    "role": "admin",
    "permissions": $ALL_PERMS,
    "createdAt": "$(Get-Date -Format 'yyyy-MM-ddTHH:mm:ss.fffZ')"
  }
]
"@
Set-Content -Path "$INSTALL_DIR\data\users.json" -Value $usersJson -Encoding UTF8
Write-Success "Admin user created: $adminUser"

# ── Done ───────────────────────────────────────────────────
Write-Host "`n" -NoNewline
Write-Host "╔══════════════════════════════════════════════╗" -ForegroundColor Green
Write-Host "║         Installation Complete!  ✓           ║" -ForegroundColor Green
Write-Host "╚══════════════════════════════════════════════╝" -ForegroundColor Green

Write-Host "`n  Panel URL:  " -NoNewline
Write-Host "http://localhost:$PORT" -ForegroundColor Cyan
Write-Host "  Username:   " -NoNewline
Write-Host $adminUser -ForegroundColor Cyan
Write-Host "  Password:   " -NoNewline
Write-Host "(the password you entered)" -ForegroundColor Cyan
Write-Host "  Install dir:" -NoNewline
Write-Host " $INSTALL_DIR" -ForegroundColor Cyan

$openBrowser = Read-Host "`n  Open panel in browser? (Y/n)"
if ($openBrowser -ne 'n') {
    Start-Sleep -Seconds 2
    Start-Process "http://localhost:$PORT"
}

Write-Host "`n  To uninstall: sc.exe delete ServerPanel" -ForegroundColor DarkGray
Write-Host "  Logs: $INSTALL_DIR\data\crash.log" -ForegroundColor DarkGray

Write-Host "`n  ── GitHub Updates (optional) ──────────────────────────" -ForegroundColor DarkGray
Write-Host "  To enable the Update button, add these two lines to:" -ForegroundColor DarkGray
Write-Host "  $INSTALL_DIR\.env" -ForegroundColor Yellow
Write-Host ""
Write-Host "    GITHUB_REPO=your-username/your-repo-name" -ForegroundColor White
Write-Host "    GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx" -ForegroundColor White
Write-Host ""
Write-Host "  Then restart the service: Restart-Service ServerPanel`n" -ForegroundColor DarkGray