src/UI/Screens/ConfigScreen.ps1

# ConfigScreen — Pantalla TUI de configuración global de repo-nav.
#
# Muestra el estado de la instalación (alias registrado, profile, deps) y
# permite togglear settings runtime (AutoLoadMode). Se accede via:
# rnav config — desde la línea de comandos (se invoca directo aquí)
# En la TUI: pendiente de binding (probable: 'P' para Preferences).
#
# Decisiones de scope:
# - Cambiar/quitar/agregar el alias NO se hace desde acá. El usuario lo hace
# con `rnav init -AliasName <nuevo> -Force` o `rnav uninstall`. Razón: para
# modificar el $PROFILE necesitamos mutar archivo del usuario, lo que abre
# ventanas de error que es mejor manejar desde el flujo normal de install
# con su own pre-flight, prompts y mensajes — no replicar acá.
# - Las settings que sí se pueden tocar (AutoLoadMode, futuros toggles) van
# por SettingsService directo, sin shell-out.

class ConfigScreen {
    [object] $Theme
    [object] $Renderer
    [object] $Primitives
    [object] $Frame
    [object] $AppHeader
    [object] $StatusBar
    [object] $I18n = $null   # Inyectable post-construct.

    [object] $SettingsSvc
    [object] $SetupSvc
    [object] $Settings           # snapshot mutable; al salir se Save al SettingsSvc

    [int]    $SelectedIndex = 0
    [bool]   $Dirty = $false     # si se cambió algo y hay que persistir
    [bool]   $QuitRequested = $false
    [bool]   $UseAltBuffer = $true

    # Items navegables: AutoLoadMode (cycle), BackgroundFetchInterval (cycle), Volver.
    static [string[]] $AutoLoadModes = @('All', 'Favorites', 'None')

    # Valores en segundos. 0 = off; el resto, intervalos típicos.
    # Etiqueta paralela en BgFetchLabels para mostrar al usuario.
    static [int[]] $BgFetchIntervals = @(0, 300, 600, 1800)
    static [string[]] $BgFetchLabels = @('off', 'cada 5 min', 'cada 10 min', 'cada 30 min')

    ConfigScreen($theme, $renderer, $primitives, $frame, $appHeader, $statusBar, $settingsSvc, $setupSvc) {
        $this.Theme       = $theme
        $this.Renderer    = $renderer
        $this.Primitives  = $primitives
        $this.Frame       = $frame
        $this.AppHeader   = $appHeader
        $this.StatusBar   = $statusBar
        $this.SettingsSvc = $settingsSvc
        $this.SetupSvc    = $setupSvc
    }

    [void] Open() {
        $this.Settings = $this.SettingsSvc.Load()
        $this.SelectedIndex = 0
        $this.Dirty = $false

        if ([Console]::IsInputRedirected) {
            $lines = $this.BuildLines()
            foreach ($l in $lines) { Write-Host $l }
            return
        }

        $errOut = [Console]::Error
        $this.Render($errOut)

        try {
            while ($true) {
                $key = [Console]::ReadKey($true)
                $exit = $this.HandleKey($key)
                if ($exit -eq 'back') { break }
                if ($exit -eq 'quit') { $this.QuitRequested = $true; break }
                $this.Render($errOut)
            }
        }
        finally {
            if ($this.Dirty) {
                $this.SettingsSvc.Save($this.Settings)
            }
        }
    }

    hidden [void] Render([object]$errOut) {
        if ([Console]::IsInputRedirected) { return }
        $lines = $this.BuildLines()
        $payload = [AnsiService]::BeginSync() +
                   [AnsiService]::MoveTo(1, 1) +
                   ($lines -join "`e[K`n") +
                   "`e[K`e[J" +
                   [AnsiService]::EndSync()
        $errOut.Write($payload)
        $errOut.Flush()
    }

    hidden [string] HandleKey([System.ConsoleKeyInfo]$key) {
        $k = $key.Key
        $c = $key.KeyChar

        if ($c -eq 'q' -or $c -eq 'Q') { return 'quit' }
        if ($k -eq 'Escape' -or $k -eq 'LeftArrow') { return 'back' }

        # 3 items navegables: 0=AutoLoadMode, 1=BackgroundFetchInterval, 2=Volver.
        $maxIdx = 2

        if ($k -eq 'UpArrow' -or $c -eq 'k') {
            $this.SelectedIndex = if ($this.SelectedIndex -le 0) { $maxIdx } else { $this.SelectedIndex - 1 }
            return ''
        }
        if ($k -eq 'DownArrow' -or $c -eq 'j') {
            $this.SelectedIndex = if ($this.SelectedIndex -ge $maxIdx) { 0 } else { $this.SelectedIndex + 1 }
            return ''
        }

        if ($k -eq 'Enter' -or $c -eq ' ') {
            switch ($this.SelectedIndex) {
                0 { $this.CycleAutoLoadMode(); return '' }
                1 { $this.CycleBgFetchInterval(); return '' }
                2 { return 'back' }
            }
        }
        return ''
    }

    hidden [void] CycleBgFetchInterval() {
        $intervals = [ConfigScreen]::BgFetchIntervals
        $current = if ($null -ne $this.Settings.BackgroundFetchInterval) {
            [int]$this.Settings.BackgroundFetchInterval
        } else { 0 }
        $idx = [array]::IndexOf($intervals, $current)
        if ($idx -lt 0) { $idx = 0 }
        $next = $intervals[($idx + 1) % $intervals.Count]
        $this.Settings.BackgroundFetchInterval = $next
        $this.Dirty = $true
    }

    hidden [void] CycleAutoLoadMode() {
        $modes = [ConfigScreen]::AutoLoadModes
        $current = if ($this.Settings.AutoLoadMode) { $this.Settings.AutoLoadMode } else { 'All' }
        $idx = [array]::IndexOf($modes, $current)
        if ($idx -lt 0) { $idx = 0 }
        $next = $modes[($idx + 1) % $modes.Count]
        $this.Settings.AutoLoadMode = $next
        $this.Dirty = $true
    }

    [string[]] BuildLines() {
        $reset = [AnsiService]::Reset
        $r = $this.Renderer
        $t = $this.Theme
        $lines = [System.Collections.Generic.List[string]]::new()

        # Title bar — version se podría inyectar desde el caller, por ahora
        # estática 'v3' para no acoplar la screen al RNVersion del entry script.
        $lines.Add($this.Frame.TitleBar('repo-nav', 'Configuración', 'v3'))
        $lines.Add($this.AppHeader.Render(@(), 'Config', @()))
        $lines.Add($r.HRule())

        # ─── Sección Instalación ────────────────────────────────────────────
        $lines.Add('')
        $lines.Add(' ' + $t.Fg('fg2') + 'Instalación' + $reset)
        $lines.Add($r.HRule())

        $aliases = $this.SetupSvc.GetInstalledAliases()
        if ($aliases.Count -gt 0) {
            $aliasStr = $aliases -join ', '
            $lines.Add(' ' + $t.Fg('fg2') + 'Alias: ' + $reset + $t.Fg('gitClean') + $aliasStr + $reset)
        } else {
            $lines.Add(' ' + $t.Fg('fg2') + 'Alias: ' + $reset + $t.Fg('gitConflict') + '(no instalado)' + $reset)
            $lines.Add(' ' + $t.Fg('fg3') + ' Ejecutá: rnav init' + $reset)
        }
        $profileInfo = $this.SetupSvc.GetProfileInfo()
        $profilePath = if ($profileInfo.Path) { $profileInfo.Path } else { '(no definido)' }
        $lines.Add(' ' + $t.Fg('fg2') + 'Profile: ' + $reset + $t.Fg('fg1') + $profilePath + $reset)
        $lines.Add(' ' + $t.Fg('fg3') + 'Para cambiar el alias: rnav init -AliasName <nuevo> -Force' + $reset)
        $lines.Add(' ' + $t.Fg('fg3') + 'Para quitar: rnav uninstall' + $reset)

        # ─── Sección Dependencias ───────────────────────────────────────────
        $lines.Add('')
        $lines.Add(' ' + $t.Fg('fg2') + 'Dependencias' + $reset)
        $lines.Add($r.HRule())
        $deps = $this.SetupSvc.CheckDependencies()
        foreach ($name in @('PowerShell', 'Git', 'Node', 'Gh')) {
            $d = $deps[$name]
            $label = if ($name -eq 'Gh') { 'gh (GitHub CLI)' } else { $name.ToLower() }
            $labelPad = $label.PadRight(18)
            if ($d.Available) {
                $tick = $t.Fg('gitClean') + '✓' + $reset
                $verStr = $t.Fg('fg1') + $d.Version + $reset
                $lines.Add(' ' + $tick + ' ' + $t.Fg('fg2') + $labelPad + $reset + $verStr)
            } elseif ($d.Required) {
                $cross = $t.Fg('gitConflict') + '✗' + $reset
                $note = $t.Fg('gitConflict') + '(requerido) ' + $reset + $t.Fg('fg3') + $d.Notes + $reset
                $lines.Add(' ' + $cross + ' ' + $t.Fg('fg2') + $labelPad + $reset + $note)
            } else {
                $cross = $t.Fg('fg3') + '·' + $reset
                $note = $t.Fg('fg3') + '(opcional) ' + $d.Notes + $reset
                $lines.Add(' ' + $cross + ' ' + $t.Fg('fg2') + $labelPad + $reset + $note)
            }
        }

        # ─── Sección Settings (interactivos) ────────────────────────────────
        $lines.Add('')
        $lines.Add(' ' + $t.Fg('fg2') + 'Preferencias' + $reset)
        $lines.Add($r.HRule())

        # Item 0: AutoLoadMode (cycle)
        $modeNow = if ($this.Settings.AutoLoadMode) { $this.Settings.AutoLoadMode } else { 'All' }
        $modeDesc = switch ($modeNow) {
            'All'       { 'todos los repos cargan git status al arrancar' }
            'Favorites' { 'solo los favoritos cargan; el resto se carga al pasar encima' }
            'None'      { 'ninguno carga; pulsá R o navegá para cargar' }
            default     { '' }
        }
        $lines.Add($this.RenderItem(0, 'AutoLoadMode', "$modeNow ▶ ($modeDesc)"))

        # Item 1: BackgroundFetchInterval (cycle off / 5min / 10min / 30min)
        $bgInterval = if ($null -ne $this.Settings.BackgroundFetchInterval) {
            [int]$this.Settings.BackgroundFetchInterval
        } else { 0 }
        $bgIdx = [array]::IndexOf([ConfigScreen]::BgFetchIntervals, $bgInterval)
        if ($bgIdx -lt 0) { $bgIdx = 0 }
        $bgLabel = [ConfigScreen]::BgFetchLabels[$bgIdx]
        $bgDesc = if ($bgInterval -eq 0) {
            'fetch sigue manual (binding R, pull, push). Recomendado para red estable.'
        } else {
            'corre git fetch en background sin bloquear UI. Útil con red lenta.'
        }
        $lines.Add($this.RenderItem(1, 'BackgroundFetch', "$bgLabel ▶ ($bgDesc)"))

        # Item 2: Volver
        $lines.Add('')
        $lines.Add($this.RenderItem(2, '[ Volver ]', ''))

        # Hint de que hay cambios pendientes
        if ($this.Dirty) {
            $lines.Add('')
            $lines.Add(' ' + $t.Fg('gitDirty') + '· Cambios pendientes — se guardan al volver con ↵ o Esc.' + $reset)
        }

        # StatusBar
        $tHelper = if ($null -ne $this.I18n) { $this.I18n } else { [I18nService]::new('es') }
        $hints = @(
            @{ k = '↑↓';  label = $tHelper.T('hint.move') }
            @{ k = '↵';   label = $tHelper.T('hint.toggle') }
            @{ k = 'Esc'; label = $tHelper.T('hint.back') }
            @{ k = 'Q';   label = $tHelper.T('hint.quit') }
        )
        $lines.Add($r.HRule())
        return @($lines.ToArray()) + $this.StatusBar.RenderAnchored($hints, '', $lines.Count)
    }

    hidden [string] RenderItem([int]$idx, [string]$label, [string]$value) {
        $reset = [AnsiService]::Reset
        $sel = ($this.SelectedIndex -eq $idx)
        $marker = if ($sel) { $this.Theme.Fg('acc') + '▎ ▶ ' + $reset } else { ' · ' }
        $labelFmt = [Renderer]::PadRight($this.Theme.Fg($(if ($sel) { 'acc' } else { 'fg2' })) + $label + $reset, 22)
        $valFmt = if ($value) { ' ' + $this.Theme.Fg('fg1') + $value + $reset } else { '' }
        return $marker + $labelFmt + $valFmt
    }
}