param(
  [int]$Port = 8788,
  [switch]$ForceRestart
)

$ErrorActionPreference = 'Stop'

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

[System.Windows.Forms.Application]::EnableVisualStyles()

$root = Split-Path -Parent $PSScriptRoot
$url = "http://localhost:$Port"
$env:PORT = [string]$Port
Set-Location $root

function Get-PortListenerProcessIds {
  param([int]$LocalPort)

  $connections = Get-NetTCPConnection -LocalPort $LocalPort -State Listen -ErrorAction SilentlyContinue
  if (-not $connections) { return @() }

  return @($connections | Select-Object -ExpandProperty OwningProcess -Unique)
}

function Stop-PortListeners {
  param([int]$LocalPort)

  $processIds = Get-PortListenerProcessIds -LocalPort $LocalPort
  foreach ($processId in $processIds) {
    try { Stop-Process -Id $processId -Force -ErrorAction Stop } catch {}
  }
}

function Assert-Node {
  if (Get-Command node -ErrorAction SilentlyContinue) { return }

  [System.Windows.Forms.MessageBox]::Show(
    'Node.js is not installed. Please install Node.js 20 LTS, then start S-Live again.',
    'S-Live',
    [System.Windows.Forms.MessageBoxButtons]::OK,
    [System.Windows.Forms.MessageBoxIcon]::Error
  ) | Out-Null
  exit 1
}

function Install-Deps {
  if (Test-Path (Join-Path $root 'node_modules')) { return }

  [System.Windows.Forms.MessageBox]::Show(
    'First launch: installing dependencies. Please wait.',
    'S-Live',
    [System.Windows.Forms.MessageBoxButtons]::OK,
    [System.Windows.Forms.MessageBoxIcon]::Information
  ) | Out-Null

  $proc = Start-Process -FilePath 'npm' -ArgumentList 'install' -WorkingDirectory $root -PassThru -WindowStyle Hidden
  $proc.WaitForExit()

  if ($proc.ExitCode -eq 0) { return }

  [System.Windows.Forms.MessageBox]::Show(
    "npm install failed with exit code $($proc.ExitCode).",
    'S-Live',
    [System.Windows.Forms.MessageBoxButtons]::OK,
    [System.Windows.Forms.MessageBoxIcon]::Error
  ) | Out-Null
  exit 1
}

function Get-TrayIcon {
  $iconPath = Join-Path $root 'public\sanglive.ico'
  if (Test-Path $iconPath) {
    return New-Object System.Drawing.Icon($iconPath)
  }

  return [System.Drawing.SystemIcons]::Application
}

function Test-BackendOnline {
  try {
    Invoke-RestMethod -Uri "$url/api/status" -TimeoutSec 2 -ErrorAction Stop | Out-Null
    return $true
  } catch {
    return $false
  }
}

function Open-Frontend {
  Start-Process $url
}

function Start-Backend {
  $stdout = Join-Path $root 'logs\backend.stdout.log'
  $stderr = Join-Path $root 'logs\backend.stderr.log'

  return Start-Process `
    -FilePath 'node' `
    -ArgumentList 'server/index.js' `
    -WorkingDirectory $root `
    -WindowStyle Hidden `
    -RedirectStandardOutput $stdout `
    -RedirectStandardError $stderr `
    -PassThru
}

Assert-Node
Install-Deps

if (Test-BackendOnline) {
  Open-Frontend
  exit 0
}

$listenerProcessIds = Get-PortListenerProcessIds -LocalPort $Port
if ($listenerProcessIds.Count -gt 0) {
  if (-not $ForceRestart) {
    [System.Windows.Forms.MessageBox]::Show(
      "S-Live port $Port is already in use, but the backend did not answer the status check.`n`nTo avoid interrupting a live stream, S-Live will not restart it from the Desktop shortcut.",
      'S-Live',
      [System.Windows.Forms.MessageBoxButtons]::OK,
      [System.Windows.Forms.MessageBoxIcon]::Warning
    ) | Out-Null
    Open-Frontend
    exit 0
  }

  Stop-PortListeners -LocalPort $Port
}

$nodeProc = Start-Backend

$tray = New-Object System.Windows.Forms.NotifyIcon
$tray.Icon = Get-TrayIcon
$tray.Text = "S-Live - $url"
$tray.Visible = $true

$ctxMenu = New-Object System.Windows.Forms.ContextMenuStrip

$itemOpen = New-Object System.Windows.Forms.ToolStripMenuItem
$itemOpen.Text = 'Open S-Live'
$itemOpen.add_Click({ Start-Process $url })

$sep = New-Object System.Windows.Forms.ToolStripSeparator

$itemExit = New-Object System.Windows.Forms.ToolStripMenuItem
$itemExit.Text = 'Exit S-Live'
$itemExit.ForeColor = [System.Drawing.Color]::FromArgb(200, 30, 30)
$itemExit.add_Click({
  $tray.Visible = $false
  try {
    if ($nodeProc -and -not $nodeProc.HasExited) {
      Stop-Process -Id $nodeProc.Id -Force -ErrorAction SilentlyContinue
    }
  } catch {}
  [System.Windows.Forms.Application]::Exit()
})

$ctxMenu.Items.AddRange([System.Windows.Forms.ToolStripItem[]]@($itemOpen, $sep, $itemExit))
$tray.ContextMenuStrip = $ctxMenu
$tray.add_DoubleClick({ Start-Process $url })

for ($i = 0; $i -lt 20; $i++) {
  if ($nodeProc.HasExited -or (Test-BackendOnline)) { break }
  Start-Sleep -Milliseconds 500
}

Open-Frontend

if (-not $nodeProc.HasExited) {
  $tray.ShowBalloonTip(
    4000,
    'S-Live is running',
    "Backend: $url`nDouble-click the tray icon to open.",
    [System.Windows.Forms.ToolTipIcon]::Info
  )
}

$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 5000
$timer.add_Tick({
  if ($nodeProc.HasExited) {
    $tray.Text = 'S-Live - stopped'
    $tray.ShowBalloonTip(
      4000,
      'S-Live stopped',
      'Backend has stopped. Start S-Live again.',
      [System.Windows.Forms.ToolTipIcon]::Warning
    )
    $timer.Stop()
  }
})
$timer.Start()

try {
  [System.Windows.Forms.Application]::Run()
} finally {
  $timer.Dispose()
  if ($tray.Icon) { $tray.Icon.Dispose() }
  $tray.Dispose()
}
