http.server.psm1

function Start-HttpServer {
    param(
        [int]$Port = 8000,
        [string]$Root = (Get-Location),
        [switch]$Public,

        [Alias('if')][string]$IndexFile
    )

    if (-not (Get-Command Start-ThreadJob -ErrorAction SilentlyContinue)) {
        Write-Error "Start-ThreadJob is not available. Run: Install-Module ThreadJob"
        return
    }

    if ($Global:HttpServerJob -and ($Global:HttpServerJob.State -eq 'Running')) {
        Write-Host "[-] Server already running (Job ID: $($Global:HttpServerJob.Id)). Use Stop-HttpServer to stop it."
        return
    }

    $bind = if ($Public) { "*" } else { "localhost" }
    $prefix = "http://$bind`:$Port/"
    $rootFull = (Resolve-Path $Root).Path

    # Only serve a file if explicitly provided, no default
    $ServeFile = if ($IndexFile) { (Resolve-Path (Join-Path $rootFull $IndexFile)).Path } else { $null }

    # MIME type lookup table
    $mimeMap = @{
        '.html' = 'text/html'
        '.htm'  = 'text/html'
        '.png'  = 'image/png'
        '.jpg'  = 'image/jpeg'
        '.jpeg' = 'image/jpeg'
        '.svg'  = 'image/svg+xml'
        '.js'   = 'application/javascript'
        '.css'  = 'text/css'
        '.json' = 'application/json'
    }

    $job = Start-ThreadJob -Name 'PowerShellHttpServer' -ScriptBlock {
        param($prefix, $rootFull, $ServeFile, $mimeMap)
        try {
            $listener = [System.Net.HttpListener]::new()
            $listener.Prefixes.Add($prefix)
            $listener.Start()
            Write-Output "[+] Serving at $prefix"

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

                # Serve specified file at root
                if ($ServeFile -and $req.Url.AbsolutePath -eq '/') {
                    if (Test-Path $ServeFile) {
                        $bytes = [System.IO.File]::ReadAllBytes($ServeFile)
                        $ext = [IO.Path]::GetExtension($ServeFile).ToLower()
                        $type = $mimeMap[$ext] ? $mimeMap[$ext] : 'application/octet-stream'
                        $res.ContentType     = $type
                        $res.ContentLength64 = $bytes.Length
                        $res.OutputStream.Write($bytes, 0, $bytes.Length)
                        $res.OutputStream.Close()
                        continue
                    }
                }

                # Fallback plain-text response
                $message = 'Hello from PowerShell HTTP server!'
                $buf = [System.Text.Encoding]::UTF8.GetBytes($message)
                $res.ContentType     = 'text/plain'
                $res.ContentLength64 = $buf.Length
                $res.OutputStream.Write($buf, 0, $buf.Length)
                $res.OutputStream.Close()
            }
        } catch {
            Write-Output "[-] Error: $($_.Exception.Message)"
        } finally {
            $listener?.Stop()
        }
    } -ArgumentList $prefix, $rootFull, $ServeFile, $mimeMap

    $Global:HttpServerJob = $job

    Start-Sleep -Milliseconds 500
    Receive-Job -Id $job.Id | Write-Host
    Write-Host "[*] Server running. Use Stop-HttpServer to stop it."
}

function Stop-HttpServer {
    $job = $Global:HttpServerJob
    if (-not $job -or $job.State -ne 'Running') {
        $job = Get-Job -Name 'PowerShellHttpServer' -ErrorAction SilentlyContinue | Where-Object { $_.State -eq 'Running' }
    }
    if ($job) {
        Stop-Job $job.Id -ErrorAction SilentlyContinue
        while ((Get-Job -Id $job.Id).State -ne 'Stopped') { Start-Sleep -Milliseconds 100 }
        Remove-Job $job.Id -ErrorAction SilentlyContinue
        Remove-Variable -Name HttpServerJob -Scope Global -ErrorAction SilentlyContinue
        Write-Host "[*] HTTP server stopped and cleaned up."
    } else {
        Write-Host "[-] No running HTTP server job found."
    }
}

Export-ModuleMember -Function Start-HttpServer, Stop-HttpServer


function Stop-HttpServer {
    $job = $Global:HttpServerJob

    if (-not $job -or $job.State -ne 'Running') {
        # Fallback: try to find the job by name
        $job = Get-Job -Name 'PowerShellHttpServer' -ErrorAction SilentlyContinue | Where-Object { $_.State -eq 'Running' }
    }

    if ($job) {
        try {
            Stop-Job $job.Id -ErrorAction SilentlyContinue
            while ((Get-Job -Id $job.Id).State -ne 'Stopped') {
                Start-Sleep -Milliseconds 100
            }
            Remove-Job $job.Id -ErrorAction SilentlyContinue
            Remove-Variable -Name HttpServerJob -Scope Global -ErrorAction SilentlyContinue
            Write-Host "[*] HTTP server stopped and cleaned up."
        } catch {
            Write-Host "[-] Error stopping job: $_"
        }
    } else {
        Write-Host "[-] No running HTTP server job found."
    }
}


Export-ModuleMember -Function Start-HttpServer, Stop-HttpServer