param(
  [ValidateSet("frontend", "backend", "admin", "mcp")]
  [string]$Only,
  [switch]$Spawn,
  [switch]$WithMcp,
  [switch]$Install,
  [string]$DatabaseUrl = "postgres://postgres:postgres%402025%21@10.0.0.2:5432/termi-api_development",
  [string]$McpApiKey = "termi-mcp-local-dev-key",
  [string]$McpBackendApiBase = "http://127.0.0.1:5150/api",
  [int]$McpPort = 5151,
  [switch]$FrontendOnly,
  [switch]$BackendOnly,
  [switch]$AdminOnly,
  [switch]$McpOnly
)

$ErrorActionPreference = "Stop"

$repoRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$devScriptPath = $MyInvocation.MyCommand.Path
$serviceOrder = @("frontend", "admin", "backend")

function Resolve-TargetService {
  if ($Only) {
    return $Only
  }

  $legacyTargets = @(
    @{ Enabled = $FrontendOnly; Name = "frontend" }
    @{ Enabled = $BackendOnly; Name = "backend" }
    @{ Enabled = $AdminOnly; Name = "admin" }
    @{ Enabled = $McpOnly; Name = "mcp" }
  ) | Where-Object { $_.Enabled }

  if ($legacyTargets.Count -gt 1) {
    throw "Use only one of -Only, -FrontendOnly, -BackendOnly, -AdminOnly, or -McpOnly."
  }

  if ($legacyTargets.Count -eq 1) {
    return $legacyTargets[0].Name
  }

  return $null
}

function Invoke-RepoCommand {
  param(
    [string]$Name,
    [string]$WorkingDirectory,
    [scriptblock]$Run,
    [switch]$UsesNode
  )

  if (-not (Test-Path $WorkingDirectory)) {
    throw "$Name directory not found: $WorkingDirectory"
  }

  Push-Location $WorkingDirectory

  try {
    if ($UsesNode -and ($Install -or -not (Test-Path (Join-Path $WorkingDirectory "node_modules")))) {
      Write-Host "[$Name] Installing dependencies..." -ForegroundColor Cyan
      npm install
      if ($LASTEXITCODE -ne 0) {
        throw "npm install failed for $Name"
      }
    }

    & $Run
    if ($LASTEXITCODE -ne 0) {
      throw "$Name failed to start"
    }
  }
  finally {
    Pop-Location
  }
}

function Start-Frontend {
  Invoke-RepoCommand `
    -Name "frontend" `
    -WorkingDirectory (Join-Path $repoRoot "frontend") `
    -UsesNode `
    -Run {
      Write-Host "[frontend] Starting Astro dev server..." -ForegroundColor Green
      npm run dev
    }
}

function Start-Admin {
  Invoke-RepoCommand `
    -Name "admin" `
    -WorkingDirectory (Join-Path $repoRoot "admin") `
    -UsesNode `
    -Run {
      Write-Host "[admin] Starting Vite admin workspace..." -ForegroundColor Green
      npm run dev
    }
}

function Start-Backend {
  Invoke-RepoCommand `
    -Name "backend" `
    -WorkingDirectory (Join-Path $repoRoot "backend") `
    -Run {
      $env:DATABASE_URL = $DatabaseUrl
      Write-Host "[backend] DATABASE_URL set to $DatabaseUrl" -ForegroundColor Cyan
      Write-Host "[backend] Starting Loco.rs server + worker..." -ForegroundColor Green
      cargo loco start --server-and-worker 2>&1
    }
}

function Start-Mcp {
  Invoke-RepoCommand `
    -Name "mcp" `
    -WorkingDirectory (Join-Path $repoRoot "mcp-server") `
    -UsesNode `
    -Run {
      $env:TERMI_MCP_API_KEY = $McpApiKey
      $env:TERMI_BACKEND_API_BASE = $McpBackendApiBase
      $env:TERMI_MCP_PORT = "$McpPort"

      Write-Host "[mcp] Backend API base set to $McpBackendApiBase" -ForegroundColor Cyan
      Write-Host "[mcp] Starting MCP server on port $McpPort..." -ForegroundColor Green
      npm run start
    }
}

function Invoke-Service {
  param([string]$Name)

  switch ($Name) {
    "frontend" { Start-Frontend; return }
    "admin" { Start-Admin; return }
    "backend" { Start-Backend; return }
    "mcp" { Start-Mcp; return }
    default { throw "Unsupported service: $Name" }
  }
}

function Get-ServiceLaunchArguments {
  param([string]$Name)

  $arguments = @(
    "powershell",
    "-NoExit",
    "-ExecutionPolicy", "Bypass",
    "-File", $devScriptPath,
    "-Only", $Name
  )

  if ($Install -and $Name -ne "backend") {
    $arguments += "-Install"
  }

  if ($Name -eq "backend") {
    $arguments += @("-DatabaseUrl", $DatabaseUrl)
  }

  if ($Name -eq "mcp") {
    $arguments += @(
      "-McpApiKey", $McpApiKey,
      "-McpBackendApiBase", $McpBackendApiBase,
      "-McpPort", $McpPort
    )
  }

  return $arguments
}

function Start-ServiceWindow {
  param([string]$Name)

  $arguments = Get-ServiceLaunchArguments -Name $Name
  Start-Process powershell -ArgumentList $arguments[1..($arguments.Length - 1)]
}

function Start-ServiceHost {
  param([string[]]$Services)

  $wt = Get-Command wt.exe -ErrorAction SilentlyContinue
  if (-not $wt) {
    Write-Warning "[dev] Windows Terminal (wt.exe) not found. Falling back to separate PowerShell windows."
    foreach ($service in $Services) {
      Start-ServiceWindow $service
    }
    return
  }

  $wtArguments = @("-w", "0")
  $isFirst = $true

  foreach ($service in $Services) {
    if (-not $isFirst) {
      $wtArguments += ";"
    }

    $wtArguments += @(
      "new-tab",
      "--title", "termi:$service"
    )
    $wtArguments += Get-ServiceLaunchArguments -Name $service
    $isFirst = $false
  }

  Start-Process -FilePath $wt.Source -ArgumentList $wtArguments
}

$targetService = Resolve-TargetService

if ($targetService -and -not $Spawn) {
  Invoke-Service $targetService
  exit $LASTEXITCODE
}

$servicesToStart = [System.Collections.Generic.List[string]]::new()
if ($targetService) {
  [void]$servicesToStart.Add($targetService)
}
else {
  $serviceOrder | ForEach-Object { [void]$servicesToStart.Add($_) }
  if ($WithMcp) {
    [void]$servicesToStart.Add("mcp")
  }
}

$serviceLabel = ($servicesToStart -join ", ")
Write-Host "[dev] Starting $serviceLabel in one Windows Terminal window..." -ForegroundColor Cyan
Start-ServiceHost -Services $servicesToStart

Write-Host "[dev] Ready. Use .\\stop-services.ps1 to stop everything." -ForegroundColor Green
