oob.psm1

function Start-OOB {
    [CmdletBinding()]
    param(
        [int]    $Port         = 8000,
        [string] $Name         = 'oob',
        [string] $LogName,                  # ← new optional parameter
        [string] $Path         = '/',
        [int]    $Timeout      = 15,
        [switch] $Persistent,
        [switch] $Tail,

        [ValidateSet('None','Image','File','Redirect','Script')]
        [string] $ResponseType = 'None',
        [string] $ResponseFile,
        [string] $RedirectUrl,
        [string] $ScriptBody,
        [string] $ContentType
    )

    # ── env checks ────────────────────────────────────────────────
    Import-Module Node        -ErrorAction Stop; Assert-NodeInstalled
    Import-Module cloudflared -ErrorAction Stop
    Write-Host "ℹ️ Node : $(Get-NodePath)"

    # ── normalize path ─────────────────────────────────────────────
    if (-not $Path.StartsWith('/')) { $Path = "/$Path" }

    # ── decide logfile name & create file ──────────────────────────
    $logRoot = Join-Path $HOME '.oob\logs'
    if (-not (Test-Path $logRoot)) {
        New-Item -ItemType Directory -Path $logRoot | Out-Null
    }

    if ($LogName) {
        $fileName = $LogName
    }
    else {
        $stamp    = Get-Date -Format "ddMMMyyHHmmss"
        $fileName = "${Name}_$stamp.log"
    }

    $logFile = Join-Path $logRoot "$fileName.log"
    New-Item -ItemType File -Path $logFile -Force | Out-Null
    $Global:OOBLogFile = $logFile

    # ── assemble Node args ────────────────────────────────────────
    $jsPath   = Join-Path $PSScriptRoot 'oob-server.js'
    $nodeArgs = @(
        "`"$jsPath`"",
        '--port'         , $Port,
        '--name'         , $Name,
        '--path'         , $Path,
        '--logfile'      , $logFile,              # bare absolute path
        '--response-type', $ResponseType.ToLower()
    )

    switch ($ResponseType) {
        'Image'   { $nodeArgs += @('--response-file',  $ResponseFile) }
        'File'    { $nodeArgs += @('--response-file',  $ResponseFile) }
        'Redirect'{ $nodeArgs += @('--redirect-url',   $RedirectUrl) }
        'Script'  { $nodeArgs += @('--script-body',    $ScriptBody) }
    }
    if ($ContentType) {
        $nodeArgs += @('--content-type', $ContentType)
    }

    # ── launch Node listener (hidden unless -Tail) ────────────────
    $windowStyle = if ($Tail) { 'Normal' } else { 'Hidden' }
    $script:__oobProc = Start-Process node `
        -ArgumentList $nodeArgs `
        -WorkingDirectory $PSScriptRoot `
        -WindowStyle $windowStyle `
        -PassThru
    Start-Sleep 1

    # ── Cloudflare tunnel handling ────────────────────────────────
    if (-not $Persistent) {
        $rand               = Get-Random -Minimum 1000 -Maximum 9999
        $script:__oobTunnel = Start-TempTunnel `
            -Name "${Name}_$rand" `
            -Url  "http://localhost:$Port" `
            -TimeoutSeconds $Timeout
        $publicUrl = $script:__oobTunnel.Url
        Write-Host "🌐 Temp tunnel : $publicUrl$Path"
    }
    else {
        # 1) Start (or ensure) the persistent tunnel
        Start-PersistentTunnel -Name $Name | Out-Null

        # 2) Query your helper to fetch the assigned URL
        $t = Get-TunnelStatus |
             Where-Object Name -EQ $Name |
             Select-Object -First 1

        # 3) Pull out Url and write it
        $publicBase = $t.Url
        Write-Host "🌐 Persist URL : $publicBase$Path"
    }

    # ── live-tail ────────────────────────────────────────────────
    if ($Tail) {
        if ($script:__logJob -and -not $script:__logJob.HasExited) {
            Stop-Job   $script:__logJob -Force -ErrorAction SilentlyContinue
            Remove-Job $script:__logJob -Force -ErrorAction SilentlyContinue
        }
        $script:__logJob = Start-Job -Name "OOBLog_$Name" -ScriptBlock {
            param($path)
            Get-Content -Path $path -Wait -Tail 0 |
              ForEach-Object { Write-Host "[OOB]" $_ }
        } -ArgumentList $logFile
        Write-Host "📝 Tailing log: $logFile"
    }

    return ($publicUrl + $Path)
}

function Stop-OOB {
<#
.SYNOPSIS
    Tear down listener, tunnel, tail window, and helper processes.
#>

    [CmdletBinding()]
    param()

    ## 1. tunnel teardown ##########################################
    if ($script:__oobTunnel) {
        try {
            switch ($script:__oobTunnel.Type) {
                'Temporary'  { Stop-TempTunnel       -Name $script:__oobTunnel.Name -EA SilentlyContinue }
                'Persistent' { Stop-PersistentTunnel -Name $script:__oobTunnel.Name -EA SilentlyContinue }
                default      {
                    Stop-TempTunnel       -Name $script:__oobTunnel.Name -EA SilentlyContinue
                    Stop-PersistentTunnel -Name $script:__oobTunnel.Name -EA SilentlyContinue
                }
            }
        } catch {}
        $script:__oobTunnel = $null
    }

    ## 2. kill node listener #######################################
    if ($script:__oobProc -and -not $script:__oobProc.HasExited) {
        Stop-Process -Id $script:__oobProc.Id -Force -EA SilentlyContinue
        $script:__oobProc = $null
    }

    ## 3. close tail window ########################################
    if ($script:__tailProc -and -not $script:__tailProc.HasExited) {
        Stop-Process -Id $script:__tailProc.Id -Force -EA SilentlyContinue
        $script:__tailProc = $null
    }

    ## 4. clean up child pwsh wrappers hosting cloudflared #########
    try {
        Get-CimInstance Win32_Process -Filter "Name='pwsh.exe'" |
          Where-Object { $_.ParentProcessId -eq $PID } |
          ForEach-Object { Stop-Process -Id $_.ProcessId -Force -EA SilentlyContinue }
    } catch {}

    ## 5. ensure cloudflared.exe is gone ###########################
    try {
        Get-CimInstance Win32_Process -Filter "Name='cloudflared.exe'" |
          Where-Object { $_.CommandLine -match $script:__oobTunnel.Name } |
          ForEach-Object { Stop-Process -Id $_.ProcessId -Force -EA SilentlyContinue }
    } catch {}

    Write-Host "🛑 OOB listener, tunnel, tail window, and helpers stopped."
}

function Show-OOBLog {
    <#
    .SYNOPSIS
        Spin up a separate window to tail the current OOB logfile.
    #>

    if (-not $Global:OOBLogFile -or -not (Test-Path $Global:OOBLogFile)) {
        Write-Warning "No active OOB logfile found. Run Start-OOB first."
        return
    }

    # close prior tail window
    if ($script:__tailProc -and -not $script:__tailProc.HasExited) {
        Stop-Process -Id $script:__tailProc.Id -Force -EA SilentlyContinue
    }

    $cmd = "Get-Content -Path `"$Global:OOBLogFile`" -Wait -Tail 0"
    $script:__tailProc = Start-Process pwsh -ArgumentList "-NoExit","-Command",$cmd `
                          -WindowStyle Normal -PassThru
}



Export-ModuleMember -Function Start-OOB, Stop-OOB, Show-OOBLog