src/public/Orchestration/Invoke-AitherSetup.ps1

function Invoke-AitherSetup {
    <#
    .SYNOPSIS
        One command to set up AitherOS — full auto, interactive menu, or targeted modules.
 
    .DESCRIPTION
        Wraps the playbook engine with an interactive setup experience.
 
        Three modes:
          aither setup Interactive menu — pick playbook + modules
          aither setup -Playbook bootstrap Run a specific playbook directly
          aither setup -All Run the full bootstrap playbook
 
        Available playbooks (run 'aither setup -List' to see all):
          bootstrap Fresh system — Docker, K8s, environment, boot AitherOS
          install-aitheros Full partner install — deps, profile, deploy, models
          onboard-remote-site Lightweight — ADK + Node + MCP (no Docker needed)
          dev-environment Dev tools on custom drive paths
          setup-interactive Post-clone — pick: vLLM, Ollama, MCP, secrets, tunnel, etc.
 
    .PARAMETER Playbook
        Run a specific playbook by name.
 
    .PARAMETER All
        Run the 'bootstrap' playbook with all defaults (full auto).
 
    .PARAMETER List
        List all available setup playbooks.
 
    .PARAMETER Modules
        For setup-interactive playbook: comma-separated module keys to run.
        Valid: prereqs, environment, secrets, docker, vllm, ollama, node, mcp, connect, mesh, desktop
 
    .PARAMETER Profile
        Docker Compose profile: minimal, core, full. Default: core.
 
    .PARAMETER DryRun
        Show what would run without executing.
 
    .PARAMETER Variables
        Hashtable of variables to pass to the playbook.
 
    .EXAMPLE
        Invoke-AitherSetup
        # Interactive menu
 
    .EXAMPLE
        Invoke-AitherSetup -All
        # Full bootstrap, no questions
 
    .EXAMPLE
        Invoke-AitherSetup -Playbook onboard-remote-site -Variables @{ TenantSlug = 'acme'; ApiKey = 'sk_...' }
        # Onboard a remote site
 
    .EXAMPLE
        Invoke-AitherSetup -Playbook setup-interactive -Modules vllm,mcp,secrets
        # Just set up vLLM, MCP config, and API keys
 
    .EXAMPLE
        Invoke-AitherSetup -List
        # Show available playbooks
 
    .EXAMPLE
        Invoke-AitherSetup -DryRun
        # Preview what would happen
    #>

    [CmdletBinding(DefaultParameterSetName = 'Interactive')]
    param(
        [Parameter(ParameterSetName = 'Named', Position = 0)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            $setupPlaybooks = @('bootstrap', 'install-aitheros', 'onboard-remote-site', 'dev-environment', 'setup-interactive')
            $setupPlaybooks | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
                [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
            }
        })]
        [string]$Playbook,

        [Parameter(ParameterSetName = 'All')]
        [switch]$All,

        [Parameter(ParameterSetName = 'List')]
        [switch]$List,

        [string[]]$Modules,

        [ValidateSet('minimal', 'core', 'full')]
        [string]$Profile = 'core',

        [switch]$DryRun,

        [hashtable]$Variables = @{}
    )

    # ── Banner ───────────────────────────────────────────────────────────
    function Show-Banner {
        Write-Host ""
        Write-Host " ___ _ _____ _ _ ___ ___ ___ ___" -ForegroundColor Cyan
        Write-Host " / _ \ | |_ _| || | __| _ \/ _ \/ __|" -ForegroundColor Cyan
        Write-Host " | (_) || | | | | __ | _|| / (_) \__ \" -ForegroundColor Cyan
        Write-Host " \___/_|_| |_| |_||_|___|_|_\\___/|___/" -ForegroundColor Cyan
        Write-Host ""
        Write-Host " Setup Wizard" -ForegroundColor White
        Write-Host ""
    }

    # ── List mode ────────────────────────────────────────────────────────
    if ($List) {
        Show-Banner
        Write-Host " Available setup playbooks:" -ForegroundColor White
        Write-Host ""

        $playbooks = Get-AitherPlaybook -List | Where-Object {
            $_.Name -in @('bootstrap', 'install-aitheros', 'onboard-remote-site',
                          'dev-environment', 'setup-interactive',
                          'deploy-lightweight', 'docker-setup')
        }

        # If filter returned nothing, show all with 'setup' category or common ones
        if (-not $playbooks) {
            $playbooks = Get-AitherPlaybook -List
        }

        foreach ($pb in $playbooks) {
            Write-Host " $($pb.Name)" -ForegroundColor Green -NoNewline
            Write-Host " — $($pb.Description)" -ForegroundColor Gray
        }

        Write-Host ""
        Write-Host " Usage:" -ForegroundColor DarkGray
        Write-Host " Invoke-AitherSetup -Playbook <name>" -ForegroundColor White
        Write-Host " Invoke-AitherSetup -Playbook <name> -DryRun" -ForegroundColor White
        Write-Host ""
        return
    }

    # ── Full auto mode ───────────────────────────────────────────────────
    if ($All) {
        Show-Banner
        Write-Host " Running full bootstrap..." -ForegroundColor Yellow
        Write-Host ""

        $vars = @{ AitherOSProfile = $Profile } + $Variables
        return Invoke-AitherPlaybook -Name 'bootstrap' -Variables $vars -DryRun:$DryRun -ShowOutput -ContinueOnError
    }

    # ── Named playbook mode ─────────────────────────────────────────────
    if ($Playbook) {
        Show-Banner
        Write-Host " Playbook: $Playbook" -ForegroundColor Yellow

        $vars = $Variables.Clone()
        $vars['Profile'] = $Profile

        # If modules specified, load playbook, filter sequence, run filtered
        if ($Modules -and $Modules.Count -gt 0) {
            return Invoke-FilteredPlaybook -PlaybookName $Playbook -ModuleKeys $Modules -Variables $vars -DryRun:$DryRun
        }

        Write-Host ""
        return Invoke-AitherPlaybook -Name $Playbook -Variables $vars -DryRun:$DryRun -ShowOutput -ContinueOnError
    }

    # ── Interactive mode (default) ───────────────────────────────────────
    Show-Banner

    # Step 1: Pick a playbook
    $setupPlaybooks = @(
        @{ Key = '1'; Name = 'bootstrap';           Desc = 'Fresh system — Docker, K8s, env, boot everything' }
        @{ Key = '2'; Name = 'install-aitheros';    Desc = 'Full install — deps, partner profile, deploy, models' }
        @{ Key = '3'; Name = 'setup-interactive';   Desc = 'Post-clone — pick modules: vLLM, Ollama, MCP, secrets, tunnel' }
        @{ Key = '4'; Name = 'onboard-remote-site'; Desc = 'Lightweight — ADK + Node + MCP (no Docker)' }
        @{ Key = '5'; Name = 'dev-environment';     Desc = 'Dev tools — Python, Node, Docker, WSL, cloud CLIs' }
    )

    Write-Host " What do you want to set up?" -ForegroundColor White
    Write-Host ""

    foreach ($p in $setupPlaybooks) {
        Write-Host " [$($p.Key)] " -ForegroundColor Cyan -NoNewline
        Write-Host "$($p.Name)" -ForegroundColor Green -NoNewline
        Write-Host " — $($p.Desc)" -ForegroundColor Gray
    }

    Write-Host ""
    Write-Host " [q] Quit" -ForegroundColor DarkGray
    Write-Host ""

    $choice = Read-Host " Select"
    $choice = $choice.Trim()

    if ($choice -eq 'q' -or $choice -eq 'Q' -or -not $choice) {
        Write-Host " Cancelled." -ForegroundColor Yellow
        return
    }

    $selected = $setupPlaybooks | Where-Object { $_.Key -eq $choice }
    if (-not $selected) {
        Write-Host " Invalid choice: $choice" -ForegroundColor Red
        return
    }

    $playbookName = $selected.Name
    $vars = @{ Profile = $Profile } + $Variables

    # Step 2: If setup-interactive, offer module picker
    $selectedModuleKeys = $null
    if ($playbookName -eq 'setup-interactive') {
        $pickerResult = Show-ModulePicker -BaseVariables $vars
        if ($null -eq $pickerResult) {
            Write-Host " Cancelled." -ForegroundColor Yellow
            return
        }
        $vars = $pickerResult
        if ($vars.SetupModules -and $vars.SetupModules -ne 'all') {
            $selectedModuleKeys = $vars.SetupModules -split ','
        }
    }

    # Step 3: Confirm
    Write-Host ""
    Write-Host " Playbook: " -NoNewline -ForegroundColor DarkGray
    Write-Host $playbookName -ForegroundColor Green

    if ($selectedModuleKeys) {
        Write-Host " Modules: " -NoNewline -ForegroundColor DarkGray
        Write-Host ($selectedModuleKeys -join ', ') -ForegroundColor Cyan
    }

    Write-Host " Profile: " -NoNewline -ForegroundColor DarkGray
    Write-Host $Profile -ForegroundColor Cyan
    Write-Host ""

    if (-not $DryRun) {
        $confirm = Read-Host " Run now? (Y/n)"
        if ($confirm -and $confirm -notmatch '^[Yy]') {
            Write-Host " Cancelled." -ForegroundColor Yellow
            return
        }
    }

    Write-Host ""

    # If module filtering, use filtered execution
    if ($selectedModuleKeys) {
        return Invoke-FilteredPlaybook -PlaybookName $playbookName -ModuleKeys $selectedModuleKeys -Variables $vars -DryRun:$DryRun
    }

    return Invoke-AitherPlaybook -Name $playbookName -Variables $vars -DryRun:$DryRun -ShowOutput -ContinueOnError
}


function Show-ModulePicker {
    <#
    .SYNOPSIS
        Interactive module picker for setup-interactive playbook.
    #>

    param([hashtable]$BaseVariables = @{})

    $modules = [ordered]@{
        prereqs    = @{ Num = 1;  Name = 'Prerequisites';     Cat = 'Foundation';     Desc = 'Docker, PowerShell 7, Git, hardware check' }
        environment= @{ Num = 2;  Name = 'Environment';       Cat = 'Foundation';     Desc = 'Master key, hardware detect, .env config' }
        secrets    = @{ Num = 3;  Name = 'API Keys & Secrets'; Cat = 'Foundation';    Desc = 'GitHub, Anthropic, HuggingFace tokens' }
        docker     = @{ Num = 4;  Name = 'Docker Services';   Cat = 'Infrastructure'; Desc = 'Build + start containers' }
        vllm       = @{ Num = 5;  Name = 'vLLM';              Cat = 'AI Inference';   Desc = 'GPU inference server (CUDA)' }
        ollama     = @{ Num = 6;  Name = 'Ollama';             Cat = 'AI Inference';   Desc = 'Local models (CPU/GPU)' }
        node       = @{ Num = 7;  Name = 'ADK + AitherNode';  Cat = 'Tooling';        Desc = 'Agent framework + MCP server' }
        mcp        = @{ Num = 8;  Name = 'MCP Config';        Cat = 'Tooling';        Desc = 'Claude Code, Cursor, VS Code' }
        connect    = @{ Num = 9;  Name = 'Cloudflare Tunnel'; Cat = 'Networking';     Desc = 'Remote access to your instance' }
        mesh       = @{ Num = 10; Name = 'Mesh Network';      Cat = 'Networking';     Desc = 'Multi-site sync' }
        desktop    = @{ Num = 11; Name = 'AitherDesktop';     Cat = 'Desktop';        Desc = 'PyQt6 native overlay' }
    }

    Write-Host ""
    Write-Host " Pick modules (comma-separated numbers, 'a' for all):" -ForegroundColor White
    Write-Host ""

    $lastCat = ''
    foreach ($entry in $modules.GetEnumerator()) {
        $m = $entry.Value
        if ($m.Cat -ne $lastCat) {
            Write-Host " --- $($m.Cat) ---" -ForegroundColor DarkCyan
            $lastCat = $m.Cat
        }
        $pad = if ($m.Num -lt 10) { ' ' } else { '' }
        Write-Host " [$pad$($m.Num)] $($m.Name)" -ForegroundColor White -NoNewline
        Write-Host " — $($m.Desc)" -ForegroundColor DarkGray
    }

    Write-Host ""
    $input = Read-Host " Modules"
    $input = $input.Trim()

    if (-not $input -or $input -eq 'q') { return $null }

    if ($input -eq 'a' -or $input -eq 'A') {
        $BaseVariables['SetupModules'] = 'all'
        return $BaseVariables
    }

    # Map numbers to keys
    $selectedKeys = @()
    foreach ($num in ($input -split '[,\s]+')) {
        $n = 0
        if ([int]::TryParse($num.Trim(), [ref]$n)) {
            $match = $modules.GetEnumerator() | Where-Object { $_.Value.Num -eq $n }
            if ($match) { $selectedKeys += $match.Key }
        }
    }

    if ($selectedKeys.Count -eq 0) {
        Write-Host " No valid modules selected." -ForegroundColor Red
        return $null
    }

    $BaseVariables['SetupModules'] = ($selectedKeys -join ',')
    return $BaseVariables
}


function Invoke-FilteredPlaybook {
    <#
    .SYNOPSIS
        Load a playbook, filter its Sequence by Module keys, execute the filtered version.
    #>

    param(
        [Parameter(Mandatory)]
        [string]$PlaybookName,

        [Parameter(Mandatory)]
        [string[]]$ModuleKeys,

        [hashtable]$Variables = @{},

        [switch]$DryRun
    )

    $pb = Get-AitherPlaybook -Name $PlaybookName
    if (-not $pb) {
        Write-Host " Playbook not found: $PlaybookName" -ForegroundColor Red
        return
    }

    # Filter sequence: keep steps whose Module field matches selected keys
    $filtered = @()
    foreach ($step in $pb.Sequence) {
        $stepModule = $step.Module
        if (-not $stepModule) {
            # Steps without a Module field always run
            $filtered += $step
        }
        elseif ($stepModule -in $ModuleKeys) {
            $filtered += $step
        }
    }

    if ($filtered.Count -eq 0) {
        Write-Host " No matching steps found for modules: $($ModuleKeys -join ', ')" -ForegroundColor Red
        return
    }

    Write-Host " Running $($filtered.Count) steps (filtered from $($pb.Sequence.Count)):" -ForegroundColor White
    foreach ($step in $filtered) {
        Write-Host " - $($step.Name)" -ForegroundColor Gray
    }
    Write-Host ""

    # Replace sequence with filtered version and execute
    $pb.Sequence = $filtered
    return Invoke-AitherPlaybook -Playbook $pb -Variables $Variables -DryRun:$DryRun -ShowOutput -ContinueOnError
}