# =====================================================================
# SMF2290 Pensum — server.ps1
# Lett statisk HTTP-server som fallback når Python/Node ikke er installert.
# Krever bare PowerShell 5+ (innebygd i Windows 10/11).
# =====================================================================

param(
  [int]$Port = 8765,
  [string]$Root = "."
)

# Krasj ikke stille: vis full feil og stopp pent.
$ErrorActionPreference = "Stop"

# Merk: vi bruker [System.Uri]::UnescapeDataString for URL-dekoding i stedet
# for System.Web.HttpUtility. System.Web er ikke tilgjengelig i PowerShell 7
# (.NET Core) og kan faile pa enkelte oppsett — det var en kilde til at
# vinduet "krasjet umiddelbart". System.Uri finnes i bade PS 5.1 og PS 7.

try {
  $RootAbs = (Resolve-Path -LiteralPath $Root -ErrorAction Stop).Path
} catch {
  Write-Host "Fant ikke app-mappen: $Root" -ForegroundColor Red
  exit 1
}

if (-not ([System.Net.HttpListener]::IsSupported)) {
  Write-Host "HttpListener stottes ikke pa denne maskinen." -ForegroundColor Red
  exit 1
}

$listener = New-Object System.Net.HttpListener
$listener.Prefixes.Add("http://127.0.0.1:$Port/")

try {
  $listener.Start()
} catch {
  Write-Host ""
  Write-Host "  Klarte ikke starte server pa port ${Port}:" -ForegroundColor Red
  Write-Host "  $($_.Exception.Message)" -ForegroundColor Red
  Write-Host "  Tips: porten kan vaere opptatt, eller en brannmur/policy blokkerer." -ForegroundColor DarkGray
  exit 1
}

$mimeMap = @{
  '.html' = 'text/html; charset=utf-8'
  '.htm'  = 'text/html; charset=utf-8'
  '.css'  = 'text/css; charset=utf-8'
  '.js'   = 'application/javascript; charset=utf-8'
  '.mjs'  = 'application/javascript; charset=utf-8'
  '.json' = 'application/json; charset=utf-8'
  '.md'   = 'text/markdown; charset=utf-8'
  '.txt'  = 'text/plain; charset=utf-8'
  '.svg'  = 'image/svg+xml; charset=utf-8'
  '.png'  = 'image/png'
  '.jpg'  = 'image/jpeg'
  '.jpeg' = 'image/jpeg'
  '.gif'  = 'image/gif'
  '.ico'  = 'image/x-icon'
  '.webp' = 'image/webp'
  '.woff' = 'font/woff'
  '.woff2'= 'font/woff2'
  '.ttf'  = 'font/ttf'
  '.otf'  = 'font/otf'
  '.pdf'  = 'application/pdf'
}

Write-Host ""
Write-Host "  Server kjorer pa http://localhost:$Port"  -ForegroundColor Green
Write-Host "  Trykk Ctrl+C for a stoppe"                 -ForegroundColor DarkGray
Write-Host ""

try {
  while ($listener.IsListening) {
    $context = $listener.GetContext()
    $req = $context.Request
    $res = $context.Response

    try {
      $path = [System.Uri]::UnescapeDataString($req.Url.LocalPath)
      if ([string]::IsNullOrEmpty($path) -or $path -eq '/') { $path = '/index.html' }

      # Strip leading slash and normalize
      $relative = $path.TrimStart('/').Replace('/', [System.IO.Path]::DirectorySeparatorChar)
      $filePath = Join-Path $RootAbs $relative

      # Resolve to prevent directory traversal
      try {
        $resolved = [System.IO.Path]::GetFullPath($filePath)
      } catch {
        $resolved = $filePath
      }

      if (-not $resolved.StartsWith($RootAbs, [StringComparison]::OrdinalIgnoreCase)) {
        $res.StatusCode = 403
        $body = [System.Text.Encoding]::UTF8.GetBytes("403 Forbidden")
        $res.OutputStream.Write($body, 0, $body.Length)
      } elseif (Test-Path -LiteralPath $resolved -PathType Container) {
        # Directory: try index.html
        $idx = Join-Path $resolved 'index.html'
        if (Test-Path -LiteralPath $idx -PathType Leaf) {
          $bytes = [System.IO.File]::ReadAllBytes($idx)
          $res.ContentType = 'text/html; charset=utf-8'
          $res.ContentLength64 = $bytes.Length
          $res.OutputStream.Write($bytes, 0, $bytes.Length)
        } else {
          $res.StatusCode = 404
          $body = [System.Text.Encoding]::UTF8.GetBytes("404 Not Found")
          $res.OutputStream.Write($body, 0, $body.Length)
        }
      } elseif (Test-Path -LiteralPath $resolved -PathType Leaf) {
        $bytes = [System.IO.File]::ReadAllBytes($resolved)
        $ext = [System.IO.Path]::GetExtension($resolved).ToLower()
        $mime = if ($mimeMap.ContainsKey($ext)) { $mimeMap[$ext] } else { 'application/octet-stream' }
        $res.ContentType = $mime
        $res.ContentLength64 = $bytes.Length
        $res.OutputStream.Write($bytes, 0, $bytes.Length)
      } else {
        $res.StatusCode = 404
        $body = [System.Text.Encoding]::UTF8.GetBytes("404 Not Found: $path")
        $res.OutputStream.Write($body, 0, $body.Length)
      }
    } catch {
      try {
        $res.StatusCode = 500
        $body = [System.Text.Encoding]::UTF8.GetBytes("500 Internal Server Error: " + $_.Exception.Message)
        $res.OutputStream.Write($body, 0, $body.Length)
      } catch {}
    } finally {
      try { $res.Close() } catch {}
    }
  }
} finally {
  try { $listener.Stop() } catch {}
  try { $listener.Close() } catch {}
}
