// crates/atomcode-tuix/src/modals/model_picker.rs
//
// `/model` modal — provider list picker.
//
// Holds the provider list sorted alphabetically with the current default
// first. Up/Down navigates, Enter selects (persists to config + notifies
// agent), Esc cancels. Renders as a MenuPayload above the input box.

use anyhow::Result;
use atomcode_core::config::Config;
use crossterm::event::{KeyCode, KeyModifiers};

use super::{Modal, ModalAction};
use crate::event_loop::{build_status, save_and_reload, Buffer, LoopCtx};
use crate::render::{MenuPayload, Renderer, UiLine};
use crate::state::UiState;

pub struct ModelPicker {
    pub providers: Vec<String>,
    pub selected: usize,
}

impl ModelPicker {
    pub fn open(config: &Config) -> Self {
        let mut providers: Vec<String> = config.providers.keys().cloned().collect();
        providers.sort();
        // Put the current default at top for quick re-confirmation.
        let cur = config.default_provider.clone();
        if let Some(idx) = providers.iter().position(|p| *p == cur) {
            providers.swap(0, idx);
        }
        Self {
            providers,
            selected: 0,
        }
    }
}

impl Modal for ModelPicker {
    fn handle_key(
        &mut self,
        code: KeyCode,
        _mods: KeyModifiers,
        buf: &mut Buffer,
        state: &mut UiState,
        ctx: &mut LoopCtx,
        renderer: &mut dyn Renderer,
    ) -> Result<ModalAction> {
        match code {
            KeyCode::Up => {
                self.selected = self.selected.saturating_sub(1);
                self.draw(buf, state, ctx, renderer);
                Ok(ModalAction::Continue)
            }
            KeyCode::Down => {
                let max = self.providers.len().saturating_sub(1);
                if self.selected < max {
                    self.selected += 1;
                }
                self.draw(buf, state, ctx, renderer);
                Ok(ModalAction::Continue)
            }
            KeyCode::Enter => {
                let chosen = self.providers[self.selected].clone();
                let display = ctx
                    .config
                    .providers
                    .get(&chosen)
                    .map(|p| p.model.clone())
                    .unwrap_or_else(|| chosen.clone());
                ctx.config.default_provider = chosen.clone();
                ctx.model_name = display.clone();
                // Persist to config.toml + notify agent. Without this,
                // the switch lives only in memory and the next startup
                // reverts to whatever was last saved.
                save_and_reload(ctx, renderer);
                // Live-sync: broadcast this switch so an attached webui's model
                // dropdown follows. No-op when no live session exists (it just
                // updates the process-level selection, which is harmless).
                atomcode_daemon::live_set_provider(chosen.clone());
                // Clear any stale drift warning tied to the PREVIOUS
                // active provider — if the new provider is also CodingPlan,
                // the re-fire below will repopulate the slot with a
                // correct warning (or leave it clean).
                if let Ok(mut g) = ctx.monitor_warning.lock() {
                    *g = None;
                }
                // Same treatment for the usage slot: switching providers
                // invalidates the previous CodingPlan's quota snapshot.
                // Clearing here makes `build_usage_hint` short-circuit to
                // None until a fresh fetch lands; otherwise the user would
                // briefly see the OLD plan's percent on the new provider.
                if let Ok(mut g) = ctx.usage_slot.lock() {
                    *g = None;
                }
                // Re-fire the drift check if the new provider is also
                // CodingPlan-managed. Bypasses the 15-min cooldown on
                // purpose — explicit user action deserves a fresh read.
                if crate::event_loop::monitor::is_codingplan_provider(&chosen) {
                    ctx.monitor_last_check_at = Some(std::time::Instant::now());
                    crate::event_loop::monitor::spawn_check(
                        ctx.config.clone(),
                        ctx.model_name.clone(),
                        ctx.monitor_warning.clone(),
                        ctx.wake_tx.clone(),
                    );
                    // Mirror: re-fire usage check too. 30s cooldown is
                    // bypassed because the user just made an explicit
                    // switch — they want fresh data, not stale 30s data.
                    ctx.usage_last_check_at = Some(std::time::Instant::now());
                    crate::event_loop::usage_monitor::spawn_check(
                        ctx.usage_slot.clone(),
                        ctx.wake_tx.clone(),
                    );
                }
                renderer.render(UiLine::CommandOutput(
                    crate::i18n::t(crate::i18n::Msg::ModelSwitched { provider: &chosen, model: &display }).into_owned(),
                ));
                renderer.flush();
                Ok(ModalAction::Close)
            }
            KeyCode::Esc => Ok(ModalAction::Close),
            _ => Ok(ModalAction::Continue),
        }
    }

    fn draw(&self, buf: &Buffer, state: &UiState, ctx: &LoopCtx, renderer: &mut dyn Renderer) {
        let items: Vec<(String, String)> = self
            .providers
            .iter()
            .map(|name| {
                let desc = ctx
                    .config
                    .providers
                    .get(name)
                    .map(|c| format!("{} · {}", c.provider_type, c.model))
                    .unwrap_or_default();
                (name.clone(), desc)
            })
            .collect();
        let payload = MenuPayload {
            items,
            selected: self.selected,
            kind: crate::render::MenuKind::SlashCommand,
        };
        renderer.render(UiLine::InputPrompt {
            buf: buf.text.clone(),
            cursor_byte: buf.cursor,
            menu: Some(payload),
            status: build_status(state, ctx),
            attachments: Vec::new(),
        });
        renderer.flush();
    }
}