repo-nav.psm1

#Requires -Version 7.0

# repo-nav.psm1 — Module entry point.
#
# Un user de PowerShell Gallery hace `Install-Module repo-nav` y queda con esta
# carpeta en su $PSModulePath. Al `Import-Module repo-nav` PowerShell ejecuta
# este archivo. Cargamos las clases en orden bottom-up (igual que repo-nav.ps1
# para users locales) y exportamos UN cmdlet `Invoke-RepoNav` + alias `rnav`.

# OJO: usamos $script:ModuleRoot (no $script:RNRoot) para apuntar al directorio
# del módulo. Bootstrap.ps1 reasigna $script:RNRoot al scope donde se dot-sourcea,
# y queda apuntando a `src/` (un nivel adentro del módulo) — eso es lo que el
# resto del código v3 espera. No queremos que se pise nuestro module root.
$script:ModuleRoot = $PSScriptRoot

# Versión, leída del manifest. Single source of truth (el manifest manda).
# Se asigna antes de cargar las clases para que esté disponible aunque el
# Bootstrap haya pisado $script:RNRoot.
$script:RNVersion = (Import-PowerShellDataFile -Path (Join-Path $script:ModuleRoot 'repo-nav.psd1')).ModuleVersion

# Carga de clases. Bootstrap define $global:RNLoadOrder con el orden bottom-up
# (Models → Util → Services → UI → App). Después dot-sourceamos cada uno desde
# acá — desde el scope del módulo — para que las classes queden visibles entre
# archivos (PS classes solo se ven cuando se cargan en el mismo scope que el
# llamador).
. (Join-Path $script:ModuleRoot 'src/App/Bootstrap.ps1')
foreach ($rel in $global:RNLoadOrder) {
    . (Join-Path $script:ModuleRoot "src/$rel")
}

# UTF-8 global. La TUI lo configura adentro de Start-RepoNavV3 también, pero
# subcomandos no-TUI (help, doctor) escriben antes de eso.
try {
    [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
    $OutputEncoding = [System.Text.Encoding]::UTF8
} catch {
    $null = $_
}

function Invoke-RepoNav {
    <#
    .SYNOPSIS
        Interactive multi-repository navigator for the terminal.
 
    .DESCRIPTION
        Opens a TUI listing all git repositories under the current path, with
        branch operations, favorites, aliases, and bilingual UI (es/en).
 
        Subcommands:
          list, (empty) Open the TUI (default).
          init Install the alias in your $PROFILE.
          uninstall Remove the alias from your $PROFILE.
          config Open the configuration screen directly.
          doctor Print install/dependencies/settings report.
          help Show this help.
          version Print the module version.
 
    .PARAMETER Subcommand
        Action to run. Defaults to 'list' (open the TUI).
 
    .PARAMETER Theme
        Override the active theme by key (midnight, aurora, ocean, forest,
        solarized, hc, light, default).
 
    .EXAMPLE
        rnav
        # Opens the TUI in the current directory.
 
    .EXAMPLE
        rnav doctor
        # Prints a diagnostic report.
 
    .EXAMPLE
        rnav -Theme aurora
        # Opens the TUI with the Aurora theme.
 
    .LINK
        https://github.com/jobshimo/repo-nav
    #>

    [CmdletBinding()]
    param(
        [Parameter(Position = 0)]
        [string] $Subcommand = '',

        [string] $Theme,

        [switch] $Help,

        [switch] $Version,

        [Parameter(ValueFromRemainingArguments)]
        $RemainingArgs
    )

    if ($Help)    { Show-RepoNavHelp;    return }
    if ($Version) { Show-RepoNavVersion; return }

    switch ($Subcommand) {
        ''        { } # default → list
        'list'    { }
        'help'    { Show-RepoNavHelp;    return }
        'version' { Show-RepoNavVersion; return }
        'init'    { Invoke-RepoNavInit -ExtraArgs $RemainingArgs; return }
        'uninstall' { Invoke-RepoNavUninstall -ExtraArgs $RemainingArgs; return }
        'doctor'  { Invoke-RepoNavDoctor; return }
        'config'  { Invoke-RepoNavConfig -Theme $Theme; return }
        default {
            Write-Host "[error] unknown subcommand: '$Subcommand'. Try: rnav help" -ForegroundColor Red
            return
        }
    }

    # Default = list = TUI principal.
    if ($Theme) {
        Start-RepoNavV3 -ThemeKey $Theme
    } else {
        Start-RepoNavV3
    }
}

function Show-RepoNavHelp {
    $v = $script:RNVersion
    Write-Host @"
repo-nav v$v — interactive multi-repository navigator.
 
USAGE
  rnav [<subcommand>] [-Theme <name>]
 
SUBCOMMANDS
  list, (empty) Open the TUI with the repository list.
  init Install the alias in your `$PROFILE.
  uninstall Remove the alias from your `$PROFILE.
  config Open the configuration screen directly.
  doctor Diagnostic report (install, dependencies, settings).
  help Show this help.
  version Print the module version.
 
OPTIONS (TUI)
  -Theme <name> Override theme: midnight, aurora, ocean, forest, solarized,
                   hc, light, default.
 
EXAMPLES
  rnav # open TUI in current directory
  rnav -Theme aurora # open TUI with Aurora theme
  rnav doctor # diagnostic
  rnav config # configuration only
  rnav init # add the alias to your `$PROFILE
 
DOCS
  https://github.com/jobshimo/repo-nav
"@

}

function Show-RepoNavVersion {
    $v = $script:RNVersion
    Write-Host "repo-nav v$v"
}

function Invoke-RepoNavInit {
    param([object[]] $ExtraArgs)
    $installScript = Join-Path $script:ModuleRoot 'Install.ps1'
    if (-not (Test-Path -LiteralPath $installScript)) {
        Write-Host "[error] Install.ps1 not found in $script:ModuleRoot" -ForegroundColor Red
        return
    }
    & $installScript @ExtraArgs
}

function Invoke-RepoNavUninstall {
    param([object[]] $ExtraArgs)
    $installScript = Join-Path $script:ModuleRoot 'Install.ps1'
    if (-not (Test-Path -LiteralPath $installScript)) {
        Write-Host "[error] Install.ps1 not found in $script:ModuleRoot" -ForegroundColor Red
        return
    }
    & $installScript -Uninstall @ExtraArgs
}

function Invoke-RepoNavDoctor {
    $svc = [SetupService]::new()
    $profileInfo  = $svc.GetProfileInfo()
    $settingsInfo = $svc.GetSettingsInfo()
    $deps         = $svc.CheckDependencies()
    $aliases      = $svc.GetInstalledAliases()

    $v = $script:RNVersion
    Write-Host ''
    Write-Host "repo-nav v$v — doctor" -ForegroundColor Cyan
    Write-Host ('─' * 60) -ForegroundColor DarkGray

    Write-Host ''
    Write-Host 'Installation' -ForegroundColor White
    if ($aliases.Count -gt 0) {
        Write-Host ('[ok] Aliases registered: ' + ($aliases -join ', ')) -ForegroundColor Green
    } else {
        Write-Host '[warn] No alias registered. The module already exports `rnav` automatically when imported.' -ForegroundColor Yellow
    }
    $profileStr = if ($profileInfo.Path) { $profileInfo.Path } else { '(undefined)' }
    Write-Host "[info] Profile: $profileStr" -ForegroundColor Cyan
    Write-Host ("[info] Profile exists: " + ($profileInfo.Exists)) -ForegroundColor Cyan
    Write-Host ("[info] Module root: $script:ModuleRoot") -ForegroundColor Cyan

    Write-Host ''
    Write-Host 'Settings' -ForegroundColor White
    if ($settingsInfo.Exists -and $settingsInfo.Valid) {
        Write-Host "[ok] Settings file OK ($($settingsInfo.Path))" -ForegroundColor Green
    } elseif ($settingsInfo.Exists -and -not $settingsInfo.Valid) {
        Write-Host "[error] Settings file exists but JSON is corrupt: $($settingsInfo.Path)" -ForegroundColor Red
    } else {
        Write-Host "[info] Settings file not yet created (will be on first save): $($settingsInfo.Path)" -ForegroundColor Cyan
    }

    Write-Host ''
    Write-Host 'Dependencies' -ForegroundColor White
    foreach ($name in @('PowerShell', 'Git', 'Node', 'Gh')) {
        $d = $deps[$name]
        $label = if ($name -eq 'Gh') { 'gh (GitHub CLI)' } else { $name.ToLower() }
        if ($d.Available) {
            Write-Host "[ok] $label $($d.Version)" -ForegroundColor Green
        } elseif ($d.Required) {
            Write-Host "[error] $label not available (required). $($d.Notes)" -ForegroundColor Red
        } else {
            Write-Host "[info] $label not available (optional). $($d.Notes)" -ForegroundColor Cyan
        }
    }
    Write-Host ''
}

function Invoke-RepoNavConfig {
    param([string] $Theme)

    $settingsSvc = [SettingsService]::new()
    $settings    = $settingsSvc.Load()
    $themeKey    = if ($Theme) { $Theme } elseif ($settings.ThemeKey) { $settings.ThemeKey } else { 'midnight' }
    if (-not $global:RNThemes.ContainsKey($themeKey)) { $themeKey = 'midnight' }

    $themeSvc   = [ThemeService]::new($themeKey)
    $renderer   = [Renderer]::new($themeSvc)
    $primitives = [Primitives]::new($themeSvc)
    $frame      = [Frame]::new($themeSvc, $renderer)
    $header     = [AppHeader]::new($themeSvc, $renderer, $primitives)
    $statusBar  = [StatusBar]::new($themeSvc, $renderer, $primitives)
    $setupSvc   = [SetupService]::new()

    $cfg = [ConfigScreen]::new($themeSvc, $renderer, $primitives, $frame, $header, $statusBar, $settingsSvc, $setupSvc)

    # Cursor + alt buffer ownership (ADR-0004): el root maneja, sub-screen no.
    $errOut = [Console]::Error
    $errOut.Write([AnsiService]::EnterAltBuffer())
    $errOut.Write([AnsiService]::HideCursor())
    try {
        $cfg.Open()
    } finally {
        $errOut.Write([AnsiService]::ShowCursor())
        $errOut.Write([AnsiService]::LeaveAltBuffer())
    }
}

# Alias rnav → Invoke-RepoNav. El user puede ejecutar `rnav` directamente
# después de Import-Module — equivalente a Invoke-RepoNav con los mismos args.
Set-Alias -Name rnav -Value Invoke-RepoNav -Force

Export-ModuleMember -Function 'Invoke-RepoNav' -Alias 'rnav'