#! powershell
param (
    [string]$Profile = 'default',
    [switch]$DryRun
)

Set-StrictMode -Version Latest
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

$ScriptRoot  = Split-Path -Parent $MyInvocation.MyCommand.Path
$CatalogName = if ($Profile -and $Profile -ne 'default') { "comandos.$Profile.json" } else { "comandos.json" }
$CatalogFile = Join-Path $ScriptRoot $CatalogName
$HistoryFile = Join-Path $ScriptRoot "menu.log"

$FallbackCatalogJson = @'
[
  {
    "category": "Atualizacoes",
    "option": "Atualizar todos os apps",
    "command": "winget upgrade --all",
    "description": "Executa o winget upgrade em lote para todos os pacotes gerenciados.",
    "prerequisite": "Winget instalado e acesso a internet.",
    "risk": "Pode solicitar reinicio automatico apos atualizacoes.",
    "example": "winget upgrade --all"
  },
  {
    "category": "Atualizacoes",
    "option": "Atualizar app especifico",
    "command": "winget upgrade --id <nome_do_pacote>",
    "description": "Atualiza o pacote especificado apos informar o ID correto.",
    "prerequisite": "Winget configurado e nome valido.",
    "risk": "Falha se o app estiver travado ou ja atualizado.",
    "example": "winget upgrade --id Google.Chrome"
  },
  {
    "category": "Atualizacoes",
    "option": "Atualizar Winget",
    "command": "winget upgrade --id Microsoft.Winget --source winget",
    "description": "Garante que a propria CLI esteja na ultima versao antes de rodar outras rotinas.",
    "prerequisite": "Winget previamente instalado.",
    "risk": "Sem impacto significativo.",
    "example": "winget upgrade --id Microsoft.Winget --source winget"
  },
  {
    "category": "Atualizacoes",
    "option": "Atualizar modulos PowerShell",
    "command": "Update-Module -Force -Name PSReadLine",
    "description": "Atualiza o PSReadLine e outros modulos registrados.",
    "prerequisite": "PSGallery disponivel e sessao como administrador.",
    "risk": "Interrupcoes podem quebrar modulos se ocorrerem erros.",
    "example": "Update-Module -Force -Name PSReadLine"
  },
  {
    "category": "Atualizacoes",
    "option": "Atualizar Chocolatey (se instalado)",
    "command": "choco upgrade all -y",
    "description": "Atualiza todos os pacotes do Chocolatey em modo silencioso.",
    "prerequisite": "Chocolatey instalado e shell com permissao.",
    "risk": "Pode reiniciar apps durante atualizacao.",
    "example": "choco upgrade all -y"
  },
  {
    "category": "Execucao remota",
    "option": "Executar utilitario remoto",
    "command": "irm https://get.activated.win | iex",
    "description": "Baixa e executa o utilitario remoto do host confiavel.",
    "prerequisite": "Acesso HTTPS liberado e host validado.",
    "risk": "Executa codigo externo; use somente fontes confiaveis.",
    "example": "irm https://get.activated.win | iex"
  },
  {
    "category": "Execucao remota",
    "option": "Atualizar catalogo remoto",
    "command": "irm https://meusite.com/comandos.md | Out-File -Encoding utf8 comandos.md",
    "description": "Sincroniza o catalogo local com a versao hospedada.",
    "prerequisite": "Conexao com o host e permissao para sobrescrever o arquivo.",
    "risk": "Se o host estiver comprometido, o catalogo pode ser alterado.",
    "example": "irm https://meusite.com/comandos.md | Out-File comandos.md"
  },
  {
    "category": "Execucao remota",
    "option": "Executar script interno",
    "command": "Invoke-Expression (Invoke-RestMethod https://meusite.com/rotinas/check.ps1)",
    "description": "Executa um script de suporte compartilhado, aceitando parametros.",
    "prerequisite": "Host validado e rede segura.",
    "risk": "Codigo remoto com alto impacto; confirme assinatura.",
    "example": "Invoke-Expression (Invoke-RestMethod https://meusite.com/rotinas/check.ps1)"
  },
  {
    "category": "Execucao remota",
    "option": "Executar script assinado no processo atual",
    "command": "Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass; irm https://meusite.com/tools/assinado.ps1 -OutFile script.ps1; .\\script.ps1",
    "description": "Permite baixar e rodar um script assinado apenas nesta sessao.",
    "prerequisite": "Permissao para alterar politica na sessao atual.",
    "risk": "Executa codigo externo; valide origem e assinatura.",
    "example": "Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass; irm https://meusite.com/tools/assinado.ps1 -OutFile script.ps1; .\\script.ps1"
  },
  {
    "category": "Limpeza",
    "option": "Limpar cache DNS",
    "command": "Clear-DnsClientCache",
    "description": "Remove caches DNS apos trocas de rede ou gateway.",
    "prerequisite": "Direitos de administrador.",
    "risk": "Sem impacto negativo visivel.",
    "example": "Clear-DnsClientCache"
  },
  {
    "category": "Limpeza",
    "option": "Limpar arquivos temporarios",
    "command": "Get-ChildItem $env:TEMP -Recurse | Remove-Item -Force -Recurse",
    "description": "Remove arquivos temporarios do usuario.",
    "prerequisite": "Espaco livre e permissao de administrador.",
    "risk": "Pode excluir arquivos ainda em uso.",
    "example": "gci $env:TEMP -Recurse | Remove-Item -Force -Recurse"
  },
  {
    "category": "Limpeza",
    "option": "Limpar logs do Windows",
    "command": "wevtutil cl System; wevtutil cl Application",
    "description": "Limpa logs principais antes de novas analises.",
    "prerequisite": "Sessao com privilegios de admin.",
    "risk": "Perde historico de eventos antigos.",
    "example": "wevtutil cl System"
  },
  {
    "category": "Limpeza",
    "option": "Limpar cache do Edge",
    "command": "Remove-Item \"$env:LOCALAPPDATA\\Microsoft\\Edge\\User Data\\Default\\Cache\\*\" -Recurse -Force",
    "description": "Remove cache local do Microsoft Edge para liberar espaco.",
    "prerequisite": "Edge instalado e fechado.",
    "risk": "Pode limpar logins/sessoes do navegador.",
    "example": "Remove-Item \"$env:LOCALAPPDATA\\Microsoft\\Edge\\User Data\\Default\\Cache\\*\" -Recurse -Force"
  },
  {
    "category": "Diagnostico",
    "option": "Testar conectividade (Test-NetConnection)",
    "command": "Test-NetConnection www.microsoft.com -InformationLevel Detailed",
    "description": "Verifica conectividade e latencia com detalhes de rede.",
    "prerequisite": "Acesso a internet.",
    "risk": "Sem impacto.",
    "example": "Test-NetConnection www.microsoft.com -InformationLevel Detailed"
  },
  {
    "category": "Diagnostico",
    "option": "Ping 8.8.8.8",
    "command": "ping 8.8.8.8 -n 5",
    "description": "Teste rapido de latencia e perda de pacotes.",
    "prerequisite": "Rede liberada para ICMP.",
    "risk": "Sem impacto.",
    "example": "ping 8.8.8.8 -n 5"
  },
  {
    "category": "Diagnostico",
    "option": "Diagnostico de disco",
    "command": "Get-PhysicalDisk | Select-Object FriendlyName, MediaType, HealthStatus, OperationalStatus",
    "description": "Lista discos fisicos e estado de saude.",
    "prerequisite": "PowerShell 5+.",
    "risk": "Somente leitura.",
    "example": "Get-PhysicalDisk"
  },
  {
    "category": "Diagnostico",
    "option": "Processos que mais consomem CPU",
    "command": "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 Name, CPU, Id",
    "description": "Mostra top 10 processos por uso de CPU.",
    "prerequisite": "Permissao para listar processos.",
    "risk": "Somente leitura.",
    "example": "Get-Process | Sort-Object CPU -Descending | Select -First 10"
  },
  {
    "category": "Diagnostico",
    "option": "Coletar informacoes do sistema",
    "command": "systeminfo | more",
    "description": "Exibe relatorio completo do Windows.",
    "prerequisite": "Nenhum.",
    "risk": "Somente leitura.",
    "example": "systeminfo | more"
  },
  {
    "category": "Rede",
    "option": "Renovar endereco IP",
    "command": "ipconfig /release; ipconfig /renew",
    "description": "Libera e renova enderecos DHCP.",
    "prerequisite": "Sessao com privilegios de admin costuma ser necessaria.",
    "risk": "Interrompe momentaneamente a rede.",
    "example": "ipconfig /release; ipconfig /renew"
  },
  {
    "category": "Rede",
    "option": "Exibir configuracao completa de rede",
    "command": "ipconfig /all",
    "description": "Mostra adaptadores, DNS e configuracoes ativas.",
    "prerequisite": "Nenhum.",
    "risk": "Somente leitura.",
    "example": "ipconfig /all"
  },
  {
    "category": "Rede",
    "option": "Limpar cache ARP",
    "command": "arp -d *",
    "description": "Remove entradas ARP para forcar nova resolucao.",
    "prerequisite": "Permissao elevada.",
    "risk": "Pode causar pequena latencia ate recarregar ARP.",
    "example": "arp -d *"
  },
  {
    "category": "Rede",
    "option": "Traceroute para 8.8.8.8",
    "command": "tracert 8.8.8.8",
    "description": "Rastreia caminho ate o DNS do Google.",
    "prerequisite": "Rede liberada para ICMP.",
    "risk": "Somente leitura.",
    "example": "tracert 8.8.8.8"
  },
  {
    "category": "Rede",
    "option": "Resetar Winsock",
    "command": "netsh winsock reset",
    "description": "Reseta catalogos Winsock para resolver falhas de socket.",
    "prerequisite": "Executar como administrador.",
    "risk": "Pode exigir reinicio do sistema.",
    "example": "netsh winsock reset"
  },
  {
    "category": "Sistema",
    "option": "Reparar sistema (SFC)",
    "command": "sfc /scannow",
    "description": "Verifica e repara arquivos de sistema corrompidos.",
    "prerequisite": "Prompt elevado.",
    "risk": "Pode demorar; requer reboot se reparar arquivos.",
    "example": "sfc /scannow"
  },
  {
    "category": "Sistema",
    "option": "Reparar imagem (DISM)",
    "command": "DISM /Online /Cleanup-Image /RestoreHealth",
    "description": "Repara a imagem do Windows antes de rodar SFC.",
    "prerequisite": "Prompt elevado e internet se precisar de fonte.",
    "risk": "Pode demorar; alto uso de CPU/disk.",
    "example": "DISM /Online /Cleanup-Image /RestoreHealth"
  },
  {
    "category": "Sistema",
    "option": "Reiniciar Explorer",
    "command": "Stop-Process -Name explorer -Force; Start-Process explorer",
    "description": "Reinicia o shell do Windows para corrigir travamentos.",
    "prerequisite": "Nenhum.",
    "risk": "Janelas de Explorer serao fechadas.",
    "example": "Stop-Process -Name explorer -Force"
  },
  {
    "category": "Sistema",
    "option": "Reiniciar Spooler de impressao",
    "command": "Restart-Service -Name Spooler",
    "description": "Corrige problemas de fila de impressao.",
    "prerequisite": "Permissao para gerenciar servicos.",
    "risk": "Jobs de impressao em andamento podem falhar.",
    "example": "Restart-Service -Name Spooler"
  },
  {
    "category": "Sistema",
    "option": "Listar servicos desativados",
    "command": "Get-Service | Where-Object { $_.StartType -eq 'Disabled' } | Select-Object Name, Status, StartType",
    "description": "Mostra servicos desativados que podem impactar funcoes.",
    "prerequisite": "Nenhum.",
    "risk": "Somente leitura.",
    "example": "Get-Service | Where-Object { $_.StartType -eq 'Disabled' }"
  },
  {
    "category": "Seguranca",
    "option": "Atualizar assinaturas do Defender",
    "command": "Update-MpSignature",
    "description": "Forca download de assinaturas mais recentes.",
    "prerequisite": "Microsoft Defender habilitado.",
    "risk": "Uso de banda.",
    "example": "Update-MpSignature"
  },
  {
    "category": "Seguranca",
    "option": "Scan rapido com Defender",
    "command": "Start-MpScan -ScanType QuickScan",
    "description": "Roda verificacao rapida contra malware.",
    "prerequisite": "Defender ativo.",
    "risk": "Uso de CPU durante o scan.",
    "example": "Start-MpScan -ScanType QuickScan"
  },
  {
    "category": "Seguranca",
    "option": "Status de protecao em tempo real",
    "command": "Get-MpComputerStatus | Select-Object AMServiceEnabled, AntispywareEnabled, RealTimeProtectionEnabled",
    "description": "Mostra status dos servicos essenciais do Defender.",
    "prerequisite": "Defender instalado.",
    "risk": "Somente leitura.",
    "example": "Get-MpComputerStatus"
  },
  {
    "category": "Seguranca",
    "option": "Listar ameacas detectadas",
    "command": "Get-MpThreatDetection",
    "description": "Consulta historico de deteccoes do Defender.",
    "prerequisite": "Defender habilitado.",
    "risk": "Somente leitura.",
    "example": "Get-MpThreatDetection"
  },
  {
    "category": "Utilitarios",
    "option": "Abrir Gerenciador de Tarefas",
    "command": "taskmgr",
    "description": "Abre rapidamente o gerenciador de tarefas.",
    "prerequisite": "Nenhum.",
    "risk": "Sem impacto.",
    "example": "taskmgr"
  },
  {
    "category": "Utilitarios",
    "option": "Abrir Painel de Controle",
    "command": "control",
    "description": "Atalho para o painel classico do Windows.",
    "prerequisite": "Nenhum.",
    "risk": "Sem impacto.",
    "example": "control"
  },
  {
    "category": "Utilitarios",
    "option": "Forcar atualizacao de politicas",
    "command": "gpupdate /force",
    "description": "Reaplica politicas de grupo.",
    "prerequisite": "Rede com dominio ou politicas locais.",
    "risk": "Pode fechar sessoes se politicas mudarem.",
    "example": "gpupdate /force"
  },
  {
    "category": "Utilitarios",
    "option": "Ver uptime da maquina",
    "command": "(Get-Date) - (gcim win32_operatingsystem).LastBootUpTime",
    "description": "Calcula ha quanto tempo o Windows esta ligado.",
    "prerequisite": "Nenhum.",
    "risk": "Somente leitura.",
    "example": "(Get-Date) - (gcim win32_operatingsystem).LastBootUpTime"
  },
  {
    "category": "Office e navegador",
    "option": "Reparar Microsoft Office (Click-to-Run)",
    "command": "\"C:\\\\Program Files\\\\Common Files\\\\Microsoft Shared\\\\ClickToRun\\\\OfficeC2RClient.exe\" /updatepromptuser /repairpromptuser",
    "description": "Executa o reparo do Office via ClickToRun.",
    "prerequisite": "Office C2R instalado.",
    "risk": "Pode fechar apps Office durante o reparo.",
    "example": "\"C:\\\\Program Files\\\\Common Files\\\\Microsoft Shared\\\\ClickToRun\\\\OfficeC2RClient.exe\" /updatepromptuser /repairpromptuser"
  },
  {
    "category": "Office e navegador",
    "option": "Abrir Outlook em modo seguro",
    "command": "outlook.exe /safe",
    "description": "Abre o Outlook sem add-ins para diagnostico.",
    "prerequisite": "Outlook instalado.",
    "risk": "Add-ins ficam inativos na sessao.",
    "example": "outlook.exe /safe"
  },
  {
    "category": "Office e navegador",
    "option": "Resetar cache do Teams",
    "command": "taskkill /IM Teams.exe /F; Remove-Item \"$env:APPDATA\\Microsoft\\Teams\\Cache\\*\" -Recurse -Force",
    "description": "Limpa cache do Teams para corrigir travamentos.",
    "prerequisite": "Teams instalado e fechado.",
    "risk": "Remove sessoes e configuracoes locais.",
    "example": "taskkill /IM Teams.exe /F; Remove-Item \"$env:APPDATA\\Microsoft\\Teams\\Cache\\*\" -Recurse -Force"
  },
  {
    "category": "Suporte remoto",
    "option": "Abrir Quick Assist",
    "command": "ms-quick-assist:",
    "description": "Abre o assistente rapido para suporte.",
    "prerequisite": "Windows 10+ com Quick Assist.",
    "risk": "Somente leitura ate iniciar sessao remota.",
    "example": "ms-quick-assist:"
  },
  {
    "category": "Suporte remoto",
    "option": "Gerar relatorio DxDiag",
    "command": "dxdiag /t \"$env:TEMP\\dxdiag.txt\"",
    "description": "Gera relatorio completo de hardware e DirectX.",
    "prerequisite": "DirectX Diagnostics instalado (nativo).",
    "risk": "Somente leitura.",
    "example": "dxdiag /t \"$env:TEMP\\dxdiag.txt\""
  },
  {
    "category": "Suporte remoto",
    "option": "Abrir Remote Desktop",
    "command": "mstsc",
    "description": "Abre o cliente RDP nativo.",
    "prerequisite": "Cliente RDP instalado (nativo).",
    "risk": "Somente leitura ate conectar.",
    "example": "mstsc"
  },
  {
    "category": "Armazenamento",
    "option": "Listar volumes",
    "command": "Get-Volume | Select-Object DriveLetter, FileSystemLabel, SizeRemaining, Size",
    "description": "Mostra volumes e espaco livre.",
    "prerequisite": "PowerShell 5+.",
    "risk": "Somente leitura.",
    "example": "Get-Volume"
  },
  {
    "category": "Armazenamento",
    "option": "Copiar pasta completa (robocopy)",
    "command": "robocopy C:\\\\Origem C:\\\\Destino /E /R:1 /W:2",
    "description": "Copia conteudo de Origem para Destino com tolerancia a falhas.",
    "prerequisite": "Definir caminhos Origem/Destino validos.",
    "risk": "Pode sobrescrever arquivos no destino.",
    "example": "robocopy C:\\\\Origem C:\\\\Destino /E /R:1 /W:2"
  }
]
'@

function Write-CatalogFile {
    param(
        [string]$Path,
        [System.Collections.IEnumerable]$Data
    )
    $json = $Data | ConvertTo-Json -Depth 4
    Set-Content -Path $Path -Value $json -Encoding ASCII
}

function Load-Catalog {
    param([string]$Path)
    if (Test-Path $Path) {
        try {
            return Get-Content -Raw -Path $Path | ConvertFrom-Json -ErrorAction Stop
        } catch {
            Write-Host "Erro ao ler $Path, usando catalogo interno." -ForegroundColor Yellow
            return $null
        }
    }
    return $null
}

function Get-BuiltInCatalog {
    try { return $FallbackCatalogJson | ConvertFrom-Json -ErrorAction Stop }
    catch { return @() }
}

function Ensure-Catalog {
    $catalog = Load-Catalog -Path $CatalogFile
    if (-not $catalog) {
        $catalog = Get-BuiltInCatalog
        Write-Host "Catalogo carregado do fallback interno." -ForegroundColor Yellow
        try { Write-CatalogFile -Path $CatalogFile -Data $catalog } catch { }
    }
    return $catalog
}

function Build-MenuItems {
    param([System.Collections.IEnumerable]$Catalog)
    $index = 1
    $items = @()
    foreach ($entry in ($Catalog | Sort-Object category, option)) {
        $items += [PSCustomObject]@{
            Index        = $index
            Category     = $entry.category
            Option       = $entry.option
            Command      = $entry.command
            Description  = $entry.description
            Prerequisite = $entry.prerequisite
            Risk         = $entry.risk
            Example      = $entry.example
        }
        $index++
    }
    return $items
}

function Get-CategoryList { param([array]$MenuItems) return ($MenuItems | Select-Object -Expand Category -Unique | Sort-Object) }

function Show-CategoryMenu {
    param([string[]]$Categories)

    Clear-Host
    Write-Host "============================================================="
    Write-Host "   AUTOMACAO CLI - Categorias" -ForegroundColor Cyan
    Write-Host ("   Perfil: {0} | Fonte: {1}" -f $Profile, $CatalogName) -ForegroundColor DarkGray
    Write-Host "============================================================="

    $i = 1
    foreach ($cat in $Categories) {
        Write-Host (" {0,2}) {1}" -f $i, $cat) -ForegroundColor White
        $i++
    }

    Write-Host ""
    Write-Host " R recarregar catalogo | Q sair" -ForegroundColor DarkGray
    Write-Host " Numero abre sub-menu da categoria" -ForegroundColor DarkGray
    if ($DryRun) { Write-Host " MODO DRY-RUN: apenas exibe o comando sem executar." -ForegroundColor Yellow }
    Write-Host "-------------------------------------------------------------"
}

function Show-CommandsMenu {
    param([string]$Category, [array]$Items)

    Clear-Host
    Write-Host "============================================================="
    Write-Host ("   {0}" -f $Category) -ForegroundColor Green
    Write-Host ("   Perfil: {0} | Fonte: {1}" -f $Profile, $CatalogName) -ForegroundColor DarkGray
    Write-Host "============================================================="

    $i = 1
    foreach ($item in $Items) {
        Write-Host (" {0,2}) {1}" -f $i, $item.Option) -ForegroundColor White
        $i++
    }

    Write-Host ""
    Write-Host " D<num> ver detalhes | B voltar | R recarregar | Q sair" -ForegroundColor DarkGray
    Write-Host " Numero executa comando" -ForegroundColor DarkGray
    if ($DryRun) { Write-Host " MODO DRY-RUN: apenas exibe o comando sem executar." -ForegroundColor Yellow }
    Write-Host "-------------------------------------------------------------"
}

function Show-Details {
    param([pscustomobject]$Item)
    $item = $Item
    if (-not $item) { Write-Host "Item nao encontrado." -ForegroundColor Yellow; return }
    Write-Host ""
    Write-Host ("{0}" -f $item.Option) -ForegroundColor Cyan
    Write-Host ("Categoria    : {0}" -f $item.Category)
    Write-Host ("Comando      : {0}" -f $item.Command)
    if ($item.Example) { Write-Host ("Exemplo      : {0}" -f $item.Example) }
    if ($item.Description) { Write-Host ("Descricao    : {0}" -f $item.Description) }
    if ($item.Prerequisite) { Write-Host ("Pre-requisito: {0}" -f $item.Prerequisite) }
    if ($item.Risk) { Write-Host ("Risco/Atencao: {0}" -f $item.Risk) }
    Write-Host ""
    Read-Host "Enter para voltar ao menu" | Out-Null
}

function Confirm-Execution {
    param([string]$Command)
    $resp = Read-Host ("Confirmar execucao? (s/N) -> {0}" -f $Command)
    return $resp -match '^(s|S|y|Y)$'
}

function Write-HistoryEntry {
    param([pscustomobject]$Item, [string]$Result)
    $line = "{0} | {1} | {2} | {3} | {4}" -f (Get-Date -Format s), $Profile, $Item.Category, $Item.Option, $Result
    try { Add-Content -Path $HistoryFile -Value $line -Encoding ASCII } catch { }
}

function Execute-MenuItem {
    param([pscustomobject]$Item)

    $item = $Item
    if (-not $item) { Write-Host "Opcao invalida." -ForegroundColor Yellow; return }

    Write-Host ""
    Write-Host ("Comando selecionado: {0}" -f $item.Command) -ForegroundColor Cyan
    if ($item.Prerequisite) { Write-Host ("Pre-requisito: {0}" -f $item.Prerequisite) -ForegroundColor DarkYellow }
    if ($item.Risk) { Write-Host ("Risco: {0}" -f $item.Risk) -ForegroundColor DarkYellow }

    if (-not (Confirm-Execution -Command $item.Command)) {
        Write-Host "Cancelado pelo usuario." -ForegroundColor Yellow
        return
    }

    if ($DryRun) {
        Write-Host "[DRY-RUN] Nao executado: $($item.Command)" -ForegroundColor Yellow
        Write-HistoryEntry -Item $item -Result "dry-run"
        return
    }

    try {
        Invoke-Expression $item.Command
        Write-HistoryEntry -Item $item -Result "ok"
    } catch {
        Write-Host ("Erro ao executar: {0}" -f $_.Exception.Message) -ForegroundColor Red
        Write-HistoryEntry -Item $item -Result ("erro: {0}" -f $_.Exception.Message)
    }
    Write-Host ""
    Read-Host "Enter para voltar ao menu" | Out-Null
}

$catalog = Ensure-Catalog
$menuItems = Build-MenuItems -Catalog $catalog
$categories = Get-CategoryList -MenuItems $menuItems

if (-not $menuItems -or $menuItems.Count -eq 0) {
    Write-Host "Nenhum item no catalogo." -ForegroundColor Red
    exit 1
}

while ($true) {
    Show-CategoryMenu -Categories $categories
    $choice = Read-Host "Selecione categoria, R ou Q"

    if ($choice -match '^(?i)q$') { break }
    elseif ($choice -match '^(?i)r$') {
        $catalog    = Ensure-Catalog
        $menuItems  = Build-MenuItems -Catalog $catalog
        $categories = Get-CategoryList -MenuItems $menuItems
        continue
    }
    elseif ($choice -match '^\d+$') {
        $catIndex = [int]$choice
        if ($catIndex -lt 1 -or $catIndex -gt $categories.Count) {
            Write-Host "Categoria invalida." -ForegroundColor Yellow
            Start-Sleep -Seconds 1
            continue
        }

        $selectedCategory = $categories[$catIndex - 1]
        $items = $menuItems | Where-Object { $_.Category -eq $selectedCategory }

        while ($true) {
            Show-CommandsMenu -Category $selectedCategory -Items $items
            $subChoice = Read-Host "Selecione numero, D<num>, B, R ou Q"

            if ($subChoice -match '^(?i)q$') { exit 0 }
            elseif ($subChoice -match '^(?i)r$') {
                $catalog    = Ensure-Catalog
                $menuItems  = Build-MenuItems -Catalog $catalog
                $categories = Get-CategoryList -MenuItems $menuItems
                $items = $menuItems | Where-Object { $_.Category -eq $selectedCategory }
                continue
            }
            elseif ($subChoice -match '^(?i)b$') { break }
            elseif ($subChoice -match '^(?i)d(\d+)$') {
                $idx = [int]$matches[1]
                if ($idx -lt 1 -or $idx -gt $items.Count) {
                    Write-Host "Item invalido." -ForegroundColor Yellow
                    Start-Sleep -Seconds 1
                    continue
                }
                $item = $items[$idx - 1]
                Show-Details -Item $item
                continue
            }
            elseif ($subChoice -match '^\d+$') {
                $idx = [int]$subChoice
                if ($idx -lt 1 -or $idx -gt $items.Count) {
                    Write-Host "Item invalido." -ForegroundColor Yellow
                    Start-Sleep -Seconds 1
                    continue
                }
                $item = $items[$idx - 1]
                Execute-MenuItem -Item $item
                continue
            }
            else {
                Write-Host "Entrada invalida. Use numero, D<num>, B, R ou Q." -ForegroundColor Yellow
                Start-Sleep -Seconds 1
            }
        }
        continue
    }
    else {
        Write-Host "Entrada invalida. Use numero, R ou Q." -ForegroundColor Yellow
        Start-Sleep -Seconds 1
    }
}
