use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::Paragraph;
use ratatui::Frame;

use crate::state::AppState;
use crate::theme;

pub fn render(state: &AppState, frame: &mut Frame, area: Rect) {
    if state.nav_mode && area.height >= 2 {
        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Length(1), Constraint::Length(1)])
            .split(area);
        render_nav_hint(frame, chunks[0]);
        render_regular_footer(state, frame, chunks[1]);
    } else {
        render_regular_footer(state, frame, area);
    }
}

fn render_nav_hint(frame: &mut Frame, area: Rect) {
    let spans = vec![Span::styled(
        " F3 history · ↑↓ move · Space toggle · gg top · G bot · Ctrl+E all · Esc back",
        theme::footer(),
    )];
    frame.render_widget(
        Paragraph::new(Line::from(spans)).style(Style::default()),
        area,
    );
}

fn render_regular_footer(state: &AppState, frame: &mut Frame, area: Rect) {
    let f = &state.footer;
    let mut spans = vec![
        Span::styled(truncate(&f.cwd, 32), theme::footer()),
        Span::raw(" · "),
    ];
    if let Some(name) = &f.session_name {
        spans.push(Span::styled(name.to_string(), theme::footer()));
        spans.push(Span::raw(" · "));
    }
    if f.show_cost {
        let cost_str = if f.turn_count > 0 && f.input_tokens == 0 && f.output_tokens == 0 {
            // A completed turn with 0/0 means the provider did not surface
            // token counts (CLI shells may not). A fresh session still
            // renders Σ 0↑ 0↓ even when the model name is already populated.
            // See spec §6 error-matrix row.
            "tokens: unavailable".to_string()
        } else {
            format!("Σ {}↑ {}↓", f.input_tokens, f.output_tokens)
        };
        spans.push(Span::styled(cost_str, theme::footer()));
        spans.push(Span::raw(" · "));
    }
    spans.push(Span::styled(&f.model, theme::footer()));

    if let Some(pct) = f.context_pct {
        spans.push(Span::raw(" · "));
        spans.push(Span::styled(
            format!("ctx {:.0}%", pct * 100.0),
            theme::footer(),
        ));
    }

    if f.skills_loaded > 0 {
        spans.push(Span::raw("  "));
        spans.push(Span::styled(
            format!("Skills: {}", f.skills_loaded),
            Style::default().fg(Color::Cyan),
        ));
    }

    // v0.10 Phase A: permission mode indicator.
    spans.push(Span::raw(" · "));
    let (mode_str, mode_style) = match state.permission_mode {
        capo_agent::PermissionMode::Bypass => (
            "[bypass]",
            Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
        ),
        capo_agent::PermissionMode::AcceptEdits => {
            ("[accept-edits]", Style::default().fg(Color::Yellow))
        }
        capo_agent::PermissionMode::Prompt => {
            ("[prompt]", Style::default().add_modifier(Modifier::DIM))
        }
    };
    spans.push(Span::styled(mode_str, mode_style));

    frame.render_widget(
        Paragraph::new(Line::from(spans)).style(Style::default()),
        area,
    );
}

fn truncate(s: &str, max_chars: usize) -> String {
    if s.chars().count() <= max_chars {
        return s.to_string();
    }
    let keep = max_chars.saturating_sub(1);
    let mut out: String = s.chars().take(keep).collect();
    out.push('…');
    out
}

#[cfg(test)]
mod tests {
    use super::*;
    use ratatui::backend::TestBackend;
    use ratatui::Terminal;

    fn state_with_footer(f: crate::state::FooterState) -> AppState {
        AppState {
            footer: f,
            ..AppState::default()
        }
    }

    fn state_with_mode(mode: capo_agent::PermissionMode) -> AppState {
        let base = state_with_footer(crate::state::FooterState {
            cwd: "/Users/dev/project".into(),
            session_name: Some("demo".into()),
            input_tokens: 1100,
            output_tokens: 275,
            show_cost: true,
            turn_count: 2,
            cost_usd: 0.0,
            context_pct: None,
            model: "claude-sonnet-4-6".into(),
            skills_loaded: 0,
        });
        AppState {
            permission_mode: mode,
            ..base
        }
    }

    #[test]
    fn footer_renders_bypass_mode_indicator() -> Result<(), Box<dyn std::error::Error>> {
        let state = state_with_mode(capo_agent::PermissionMode::Bypass);
        let mut term = ratatui::Terminal::new(ratatui::backend::TestBackend::new(120, 1))
            .unwrap_or_else(|e| panic!("term: {e}"));
        term.draw(|f| render(&state, f, f.area()))
            .unwrap_or_else(|e| panic!("draw: {e}"));
        insta::assert_snapshot!(term.backend());
        Ok(())
    }

    #[test]
    fn footer_renders_accept_edits_mode_indicator() -> Result<(), Box<dyn std::error::Error>> {
        let state = state_with_mode(capo_agent::PermissionMode::AcceptEdits);
        let mut term = ratatui::Terminal::new(ratatui::backend::TestBackend::new(120, 1))
            .unwrap_or_else(|e| panic!("term: {e}"));
        term.draw(|f| render(&state, f, f.area()))
            .unwrap_or_else(|e| panic!("draw: {e}"));
        insta::assert_snapshot!(term.backend());
        Ok(())
    }

    #[test]
    fn footer_renders_prompt_mode_indicator() -> Result<(), Box<dyn std::error::Error>> {
        let state = state_with_mode(capo_agent::PermissionMode::Prompt);
        let mut term = ratatui::Terminal::new(ratatui::backend::TestBackend::new(120, 1))
            .unwrap_or_else(|e| panic!("term: {e}"));
        term.draw(|f| render(&state, f, f.area()))
            .unwrap_or_else(|e| panic!("draw: {e}"));
        insta::assert_snapshot!(term.backend());
        Ok(())
    }

    #[test]
    fn renders_basic_footer() -> Result<(), Box<dyn std::error::Error>> {
        let state = state_with_footer(crate::state::FooterState {
            cwd: "/Users/dev/project".into(),
            session_name: Some("demo".into()),
            input_tokens: 800,
            output_tokens: 200,
            show_cost: true,
            turn_count: 1,
            cost_usd: 0.012,
            context_pct: Some(0.25),
            model: "claude-sonnet-4-6".into(),
            skills_loaded: 2,
        });
        let mut term = Terminal::new(TestBackend::new(100, 1))?;
        term.draw(|f| render(&state, f, f.area()))?;
        insta::assert_snapshot!(term.backend());
        Ok(())
    }

    #[test]
    fn footer_hides_cost_element_when_show_cost_false() -> Result<(), Box<dyn std::error::Error>> {
        let state = state_with_footer(crate::state::FooterState {
            cwd: "/Users/dev/project".into(),
            session_name: Some("demo".into()),
            input_tokens: 800,
            output_tokens: 200,
            show_cost: false,
            turn_count: 1,
            cost_usd: 0.0,
            context_pct: None,
            model: "claude-sonnet-4-6".into(),
            skills_loaded: 0,
        });
        let mut term = Terminal::new(TestBackend::new(100, 1))?;
        term.draw(|f| render(&state, f, f.area()))?;
        insta::assert_snapshot!(term.backend());
        Ok(())
    }

    #[test]
    fn footer_renders_nav_hint_row_when_nav_mode() -> Result<(), Box<dyn std::error::Error>> {
        let mut state = state_with_footer(crate::state::FooterState {
            cwd: "/Users/dev/project".into(),
            session_name: Some("demo".into()),
            input_tokens: 1100,
            output_tokens: 275,
            show_cost: true,
            turn_count: 2,
            cost_usd: 0.0,
            context_pct: None,
            model: "claude-sonnet-4-6".into(),
            skills_loaded: 0,
        });
        state.nav_mode = true;
        let mut term =
            Terminal::new(TestBackend::new(100, 2)).unwrap_or_else(|e| panic!("term: {e}"));
        term.draw(|f| render(&state, f, f.area()))
            .unwrap_or_else(|e| panic!("draw: {e}"));
        insta::assert_snapshot!(term.backend());
        Ok(())
    }

    #[test]
    fn footer_shows_unavailable_when_tokens_zero_with_populated_model(
    ) -> Result<(), Box<dyn std::error::Error>> {
        // Provider didn't surface token counts (CLI shells) — show
        // "tokens: unavailable" rather than misleading 0↑/0↓.
        let state = state_with_footer(crate::state::FooterState {
            cwd: "/Users/dev/project".into(),
            session_name: None,
            input_tokens: 0,
            output_tokens: 0,
            show_cost: true,
            turn_count: 1,
            cost_usd: 0.0,
            context_pct: None,
            model: "gemini-1.5-pro".into(),
            skills_loaded: 0,
        });
        let mut term = Terminal::new(TestBackend::new(100, 1))?;
        term.draw(|f| render(&state, f, f.area()))?;
        insta::assert_snapshot!(term.backend());
        Ok(())
    }
}
