//! Ratatui adapter for `hjkl-statusline`.
//!
//! Converts a [`Bar`] to a `ratatui::text::Line` and paints it via
//! `Paragraph` into the supplied `Rect`.
//!
//! Style conversion is not implemented here: `hjkl-statusline` re-exports the
//! `hjkl-theme` style types verbatim, so `hjkl_theme_tui::ToRatatui` owns the
//! theme→ratatui mapping.
//!
//! # Usage
//!
//! ```no_run
//! // let bar: hjkl_statusline::Bar = build_normal_statusline(&app_state);
//! // hjkl_statusline_tui::render(frame, &bar, area);
//! ```

#![forbid(unsafe_code)]

use hjkl_statusline::{Bar, Segment};
use hjkl_theme_tui::ToRatatui;
use ratatui::{
    Frame,
    layout::Rect,
    text::{Line, Span},
    widgets::Paragraph,
};

/// Convert a [`Segment`] to a ratatui [`Span`].
fn segment_to_span(seg: &Segment) -> Span<'static> {
    match seg {
        Segment::Text { content, style } => {
            Span::styled(content.clone().into_owned(), style.to_ratatui())
        }
        // Future variants (Icon, Separator, PowerlineChevron, …) fall back to empty.
        _ => Span::raw(String::new()),
    }
}

/// Render a [`Bar`] into `area` using the supplied ratatui [`Frame`].
///
/// Calls [`Bar::layout`] with `area.width`, converts each segment to a
/// `Span`, and renders via `Paragraph`. The bar is always exactly one row.
pub fn render(frame: &mut Frame, bar: &Bar, area: Rect) {
    let segments = bar.layout(area.width);
    let spans: Vec<Span<'static>> = segments.iter().map(segment_to_span).collect();
    let line = Line::from(spans);
    frame.render_widget(Paragraph::new(line), area);
}

/// Convert a [`Bar`] to a ratatui [`Line`] at the given `width`.
///
/// Useful when the caller already manages its own widget construction.
pub fn to_line(bar: &Bar, width: u16) -> Line<'static> {
    let segments = bar.layout(width);
    let spans: Vec<Span<'static>> = segments.iter().map(segment_to_span).collect();
    Line::from(spans)
}

#[cfg(test)]
mod tests {
    use std::borrow::Cow;

    use super::*;
    use hjkl_statusline::{
        Bar, Color, Segment, StatusTheme, Style, StyleExt, cursor_segment, mode_segment,
    };
    use ratatui::{
        Terminal,
        backend::TestBackend,
        style::{Color as RColor, Modifier as RMod},
    };

    /// Build a test theme via mutation — `StatusTheme` is `#[non_exhaustive]`
    /// so struct literals are not permitted outside the defining crate.
    fn test_theme() -> StatusTheme {
        let mut t = StatusTheme::new(Color::rgb(0x2a, 0x32, 0x40), Color::rgb(0xe5, 0xe9, 0xf0));
        t.fill_bg = Color::rgb(0x1e, 0x22, 0x2a);
        t.mode_normal_bg = Color::rgb(0x5e, 0x81, 0xac);
        t.mode_normal_fg = Color::rgb(0x2e, 0x34, 0x40);
        t.mode_insert_bg = Color::rgb(0x7e, 0xe7, 0x87);
        t.mode_insert_fg = Color::rgb(0x2e, 0x34, 0x40);
        t.mode_visual_bg = Color::rgb(0xd0, 0x8e, 0x4b);
        t.mode_visual_fg = Color::rgb(0x2e, 0x34, 0x40);
        t.dirty_fg = Color::rgb(0xeb, 0xcb, 0x8b);
        t.readonly_fg = Color::rgb(0xbf, 0x61, 0x6a);
        t.new_file_fg = Color::rgb(0xa3, 0xbe, 0x8c);
        t.recording_bg = Color::rgb(0xbf, 0x61, 0x6a);
        t.recording_fg = Color::rgb(0x2e, 0x34, 0x40);
        t
    }

    #[test]
    fn render_emits_one_line_widget() {
        let theme = test_theme();
        let width: u16 = 40;
        let height: u16 = 1;

        let mut bar = Bar {
            fill_style: Style::default_style().bg(theme.fill_bg).fg(theme.fg),
            ..Default::default()
        };
        bar.left.push(mode_segment("NORMAL", &theme));
        bar.left.push(Segment::Text {
            content: Cow::Borrowed(" [No Name] "),
            style: Style::default_style().bg(theme.bg).fg(theme.fg),
        });
        bar.right.push(cursor_segment(0, 0, &theme));

        let backend = TestBackend::new(width, height);
        let mut terminal = Terminal::new(backend).expect("test terminal");
        terminal
            .draw(|frame| {
                let area = Rect::new(0, 0, width, height);
                render(frame, &bar, area);
            })
            .expect("draw");

        let buf = terminal.backend().buffer().clone();
        // Collect all rendered chars.
        let rendered: String = (0..width)
            .map(|x| {
                buf.cell((x, 0))
                    .map(|c| c.symbol().to_string())
                    .unwrap_or_default()
            })
            .collect();

        assert!(
            rendered.contains("NORMAL"),
            "mode label must appear: {rendered:?}"
        );
        assert!(
            rendered.contains("[No Name]"),
            "filename must appear: {rendered:?}"
        );
        assert!(
            rendered.contains("1:1"),
            "cursor position must appear: {rendered:?}"
        );
    }

    #[test]
    fn to_line_width_matches() {
        let theme = test_theme();
        let width: u16 = 50;
        let mut bar = Bar {
            fill_style: Style::default_style().bg(theme.fill_bg).fg(theme.fg),
            ..Default::default()
        };
        bar.left.push(mode_segment("INSERT", &theme));
        bar.right.push(cursor_segment(5, 3, &theme));

        let line = to_line(&bar, width);
        let total: usize = line.spans.iter().map(|s| s.content.chars().count()).sum();
        assert_eq!(total, width as usize, "line width must equal bar width");
    }

    // The three tests below pin the conversions the crate used to implement
    // locally; they now exercise the shared `hjkl-theme-tui` `ToRatatui` impls
    // and must produce identical results.

    #[test]
    fn to_ratatui_color_roundtrip() {
        let c = Color::rgb(0x5e, 0x81, 0xac);
        assert_eq!(c.to_ratatui(), RColor::Rgb(0x5e, 0x81, 0xac));
    }

    #[test]
    fn to_ratatui_style_bold_and_italic() {
        let s = Style::default_style()
            .fg(Color::rgb(0xff, 0x00, 0x00))
            .bold()
            .italic();
        let rs = s.to_ratatui();
        assert!(rs.add_modifier.contains(RMod::BOLD));
        assert!(rs.add_modifier.contains(RMod::ITALIC));
        assert_eq!(rs.fg, Some(RColor::Rgb(0xff, 0x00, 0x00)));
    }

    #[test]
    fn to_ratatui_modifier_maps_all_flags() {
        // Regression: underline/reverse/strikethrough used to be dropped.
        let m = hjkl_statusline::Modifiers {
            bold: true,
            italic: true,
            underline: true,
            reverse: true,
            strikethrough: true,
        };
        let rm = m.to_ratatui();
        assert!(rm.contains(RMod::BOLD));
        assert!(rm.contains(RMod::ITALIC));
        assert!(rm.contains(RMod::UNDERLINED));
        assert!(rm.contains(RMod::REVERSED));
        assert!(rm.contains(RMod::CROSSED_OUT));
    }
}
