#Requires -RunAsAdministrator

$ErrorActionPreference = "Stop"

# Verify running as Administrator
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]$identity
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Write-Error "This script must be run as a machine Administrator."
    exit 1
}

$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
$InstallDir = "C:\Program Files\Nebula"
$ConfigPath = "$InstallDir\config.yml"
$ExpectedBinPath = "`"$InstallDir\nebula.exe`" -service run -config `"$ConfigPath`""

# --- Validate all required files before making any changes ---

$missing = @()

# Per-host files expected in the working directory
$HostFiles = @("config.yml", "host.crt", "host.key")
foreach ($file in $HostFiles) {
    if (-not (Test-Path (Join-Path $PWD $file))) {
        $missing += "$file (expected in working directory: $PWD)"
    }
}

# Package files expected alongside the script
$PackageFiles = @("ca.crt", "nebula.exe")
foreach ($file in $PackageFiles) {
    if (-not (Test-Path (Join-Path $ScriptDir $file))) {
        $missing += "$file (expected in script directory: $ScriptDir)"
    }
}

if (-not (Test-Path (Join-Path $ScriptDir "dist"))) {
    $missing += "dist\ (expected in script directory: $ScriptDir)"
}

if ($missing.Count -gt 0) {
    Write-Error "Missing required files:`n  - $($missing -join "`n  - ")"
    exit 1
}

# --- Check if already installed and running correctly ---

$existing = Get-Service -Name "nebula" -ErrorAction SilentlyContinue
if ($existing) {
    $svcConfig = sc.exe qc nebula 2>&1 | Out-String
    $correctBinary = $svcConfig -match [regex]::Escape($ExpectedBinPath)

    if ($existing.Status -eq "Running" -and $correctBinary) {
        # Compare all source files against installed files by hash
        $allMatch = $true
        foreach ($file in $PackageFiles) {
            $src = (Get-FileHash (Join-Path $ScriptDir $file)).Hash
            $dst = (Get-FileHash (Join-Path $InstallDir $file) -ErrorAction SilentlyContinue).Hash
            if ($src -ne $dst) { $allMatch = $false; break }
        }
        if ($allMatch) {
            foreach ($file in $HostFiles) {
                $src = (Get-FileHash (Join-Path $PWD $file)).Hash
                $dst = (Get-FileHash (Join-Path $InstallDir $file) -ErrorAction SilentlyContinue).Hash
                if ($src -ne $dst) { $allMatch = $false; break }
            }
        }

        if ($allMatch) {
            Write-Host "Nebula is already installed and running with matching files. No changes needed."
            exit 0
        }

        Write-Host "Nebula is running but files have changed. Reinstalling..."
    }

    Write-Host "Stopping existing Nebula service..."
    if ($existing.Status -eq "Running") {
        sc.exe stop nebula | Out-Null
        # Wait for the service to fully stop
        $timeout = 10
        while ($timeout -gt 0) {
            $svc = Get-Service -Name "nebula" -ErrorAction SilentlyContinue
            if ($svc.Status -eq "Stopped") { break }
            Start-Sleep -Seconds 1
            $timeout--
        }
        if ($timeout -eq 0) {
            Write-Error "Timed out waiting for Nebula service to stop."
            exit 1
        }
    }

    sc.exe delete nebula | Out-Null
    if ($LASTEXITCODE -ne 0) {
        Write-Error "Failed to remove existing Nebula service. A reboot may be required if it is marked for deletion."
        exit 1
    }
    Start-Sleep -Seconds 1
}

# --- Create install directory ---

if (-not (Test-Path $InstallDir)) {
    try {
        New-Item -ItemType Directory -Path $InstallDir | Out-Null
    } catch {
        Write-Error "Failed to create install directory: $_"
        exit 1
    }
}

# --- Copy files ---

try {
    Write-Host "Copying package files..."
    Copy-Item (Join-Path $ScriptDir "ca.crt") $InstallDir -Force
    Copy-Item (Join-Path $ScriptDir "nebula.exe") $InstallDir -Force
    Copy-Item (Join-Path $ScriptDir "dist") $InstallDir -Recurse -Force

    Write-Host "Copying host files..."
    foreach ($file in $HostFiles) {
        Copy-Item (Join-Path $PWD $file) $InstallDir -Force
    }
} catch {
    Write-Error "Failed to copy files: $_"
    exit 1
}

# --- Verify files landed in install directory ---

$AllFiles = $PackageFiles + $HostFiles
foreach ($file in $AllFiles) {
    if (-not (Test-Path (Join-Path $InstallDir $file))) {
        Write-Error "File copy verification failed: $file not found in $InstallDir"
        exit 1
    }
}

if (-not (Test-Path (Join-Path $InstallDir "dist"))) {
    Write-Error "File copy verification failed: dist\ not found in $InstallDir"
    exit 1
}

# --- Install and configure service ---

Write-Host "Installing Nebula service..."
& "$InstallDir\nebula.exe" -service install -config "$ConfigPath"
if ($LASTEXITCODE -ne 0) {
    Write-Error "Failed to install Nebula service."
    exit 1
}

# Wait for real network connectivity before starting, not just the TCP/IP stack
sc.exe config nebula depend= Tcpip/NlaSvc | Out-Null
if ($LASTEXITCODE -ne 0) {
    Write-Error "Failed to set service dependencies."
    exit 1
}

# Delayed start gives Wi-Fi/Ethernet time to fully associate and get an IP
sc.exe config nebula start= delayed-auto | Out-Null
if ($LASTEXITCODE -ne 0) {
    Write-Error "Failed to set delayed-auto start."
    exit 1
}

# --- Start and verify ---

Write-Host "Starting Nebula service..."
sc.exe start nebula | Out-Null
if ($LASTEXITCODE -ne 0) {
    Write-Error "Failed to start Nebula service. Check Event Viewer (Application log, source 'nebula')."
    exit 1
}

Start-Sleep -Seconds 3

$svc = Get-Service -Name "nebula"
if ($svc.Status -eq "Running") {
    Write-Host "Nebula service is running."
} else {
    Write-Error "Nebula service failed to start. Check Event Viewer (Application log, source 'nebula')."
    exit 1
}
