use ratatui::{
    buffer::Buffer,
    layout::Rect,
    style::{Color, Modifier, Style},
    widgets::{Block, Borders, Clear, Widget},
};

/// Number of help pages available.
pub const HELP_PAGE_COUNT: usize = 5;

/// Style presets used throughout help pages.
struct HelpStyles {
    heading: Style,
    key: Style,
    dim: Style,
    normal: Style,
    accent: Style,
    banner: Style,
}

impl HelpStyles {
    fn new() -> Self {
        Self {
            heading: Style::default()
                .fg(Color::Blue)
                .add_modifier(Modifier::BOLD),
            key: Style::default().fg(Color::Cyan),
            dim: Style::default().fg(Color::DarkGray),
            normal: Style::default(),
            accent: Style::default().fg(Color::Green),
            banner: Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        }
    }
}

/// A styled line in a help page: two columns (key + description) plus a style.
/// When `desc` is empty the `key` text spans the full width.
struct HelpLine {
    key: &'static str,
    desc: &'static str,
    style: Style,
}

impl HelpLine {
    fn heading(text: &'static str, styles: &HelpStyles) -> Self {
        Self {
            key: text,
            desc: "",
            style: styles.heading,
        }
    }

    fn key(k: &'static str, d: &'static str, styles: &HelpStyles) -> Self {
        Self {
            key: k,
            desc: d,
            style: styles.key,
        }
    }

    fn dim(k: &'static str, d: &'static str, styles: &HelpStyles) -> Self {
        Self {
            key: k,
            desc: d,
            style: styles.dim,
        }
    }

    fn text(t: &'static str, styles: &HelpStyles) -> Self {
        Self {
            key: t,
            desc: "",
            style: styles.normal,
        }
    }

    fn accent(t: &'static str, styles: &HelpStyles) -> Self {
        Self {
            key: t,
            desc: "",
            style: styles.accent,
        }
    }

    fn banner(t: &'static str, styles: &HelpStyles) -> Self {
        Self {
            key: t,
            desc: "",
            style: styles.banner,
        }
    }

    fn blank() -> Self {
        Self {
            key: "",
            desc: "",
            style: Style::default(),
        }
    }
}

/// The page titles shown in the tab bar.
const PAGE_TITLES: [&str; HELP_PAGE_COUNT] = [
    "Welcome",
    "Navigation",
    "Editing",
    "Panels & Views",
    "Commands",
];

// ── Page content builders ───────────────────────────────────────────────────

fn page_welcome(s: &HelpStyles) -> Vec<HelpLine> {
    vec![
        HelpLine::blank(),
        HelpLine::banner("  improvise", s),
        HelpLine::text(
            "  A multi-dimensional data modeling tool in your terminal.",
            s,
        ),
        HelpLine::blank(),
        HelpLine::text(
            "  improvise lets you build spreadsheet-like models organized",
            s,
        ),
        HelpLine::text(
            "  by categories (dimensions).  Each category has items, and",
            s,
        ),
        HelpLine::text(
            "  every combination of items across categories forms a cell.",
            s,
        ),
        HelpLine::text(
            "  Think of it like a pivot table you can build from scratch.",
            s,
        ),
        HelpLine::blank(),
        HelpLine::heading("Quick start", s),
        HelpLine::blank(),
        HelpLine::text("  1. Create categories (dimensions) for your model:", s),
        HelpLine::accent("       :add-cat Region          :add-cat Product", s),
        HelpLine::blank(),
        HelpLine::text("  2. Add items to each category:", s),
        HelpLine::accent("       :add-items Region North South East West", s),
        HelpLine::accent("       :add-items Product Widget Gadget", s),
        HelpLine::blank(),
        HelpLine::text(
            "  3. Navigate with hjkl or arrow keys and press i to edit cells.",
            s,
        ),
        HelpLine::blank(),
        HelpLine::text("  4. Add formulas to compute values automatically:", s),
        HelpLine::accent("       :formula Product Total = Widget + Gadget", s),
        HelpLine::blank(),
        HelpLine::text("  5. Save your work:", s),
        HelpLine::accent("       :w mymodel.improv", s),
        HelpLine::blank(),
        HelpLine::heading("Core concepts", s),
        HelpLine::blank(),
        HelpLine::text(
            "  Category    A dimension of your data (e.g. Region, Time, Product).",
            s,
        ),
        HelpLine::text(
            "  Item        A member of a category (e.g. North, Q1, Widget).",
            s,
        ),
        HelpLine::text(
            "  View        A saved layout: which categories go on rows, columns, or pages.",
            s,
        ),
        HelpLine::text(
            "  Tile        The row/column/page assignment of a category.",
            s,
        ),
        HelpLine::text(
            "  Formula     A computed item: derives its value from other items.",
            s,
        ),
        HelpLine::blank(),
        HelpLine::dim("  Tip: press Tab or l/n to go to the next page.", "", s),
    ]
}

fn page_navigation(s: &HelpStyles) -> Vec<HelpLine> {
    vec![
        HelpLine::blank(),
        HelpLine::heading("Cursor movement", s),
        HelpLine::blank(),
        HelpLine::key("  hjkl  /  Arrow keys", "Move one cell", s),
        HelpLine::key("  gg", "Jump to first row", s),
        HelpLine::key("  G", "Jump to last row", s),
        HelpLine::key("  0  /  Home", "Jump to first column", s),
        HelpLine::key("  $  /  End", "Jump to last column", s),
        HelpLine::blank(),
        HelpLine::heading("Scrolling", s),
        HelpLine::blank(),
        HelpLine::key("  Ctrl+D", "Scroll half-page down", s),
        HelpLine::key("  Ctrl+U", "Scroll half-page up", s),
        HelpLine::key("  PageDown", "Scroll three-quarters page down", s),
        HelpLine::key("  PageUp", "Scroll three-quarters page up", s),
        HelpLine::blank(),
        HelpLine::heading("Page-axis cycling", s),
        HelpLine::blank(),
        HelpLine::text("  When a category is on the Page axis, only one item is", s),
        HelpLine::text(
            "  visible at a time.  Use [ and ] to cycle through them.",
            s,
        ),
        HelpLine::blank(),
        HelpLine::key("  [", "Previous page-axis item", s),
        HelpLine::key("  ]", "Next page-axis item", s),
        HelpLine::blank(),
        HelpLine::heading("Search", s),
        HelpLine::blank(),
        HelpLine::key(
            "  /",
            "Start search — type a pattern, matching cells highlight",
            s,
        ),
        HelpLine::key("  n", "Jump to next match", s),
        HelpLine::key("  N", "Jump to previous match", s),
        HelpLine::key("  Esc  or  Enter", "Exit search mode", s),
        HelpLine::blank(),
        HelpLine::heading("View history", s),
        HelpLine::blank(),
        HelpLine::key("  >", "Drill into selected cell (record view)", s),
        HelpLine::key("  <", "Go back to previous view", s),
    ]
}

fn page_editing(s: &HelpStyles) -> Vec<HelpLine> {
    vec![
        HelpLine::blank(),
        HelpLine::heading("Entering edit mode", s),
        HelpLine::blank(),
        HelpLine::key("  i  /  a", "Edit current cell (insert mode)", s),
        HelpLine::key("  Enter", "Edit current cell (same as i)", s),
        HelpLine::key("  Esc", "Cancel edit, return to Normal mode", s),
        HelpLine::blank(),
        HelpLine::heading("While editing", s),
        HelpLine::blank(),
        HelpLine::text("  Type normally to enter a value.  Values can be:", s),
        HelpLine::accent("    Numbers:   42   3.14   -100", s),
        HelpLine::accent("    Text:      hello world", s),
        HelpLine::blank(),
        HelpLine::key("  Enter", "Commit value and move down", s),
        HelpLine::key(
            "  Tab",
            "Commit value and move right (stay in edit mode)",
            s,
        ),
        HelpLine::key("  Esc", "Discard edits and return to Normal", s),
        HelpLine::blank(),
        HelpLine::heading("Copy and paste", s),
        HelpLine::blank(),
        HelpLine::key("  yy", "Yank (copy) the current cell value", s),
        HelpLine::key("  p", "Paste the yanked value into the current cell", s),
        HelpLine::blank(),
        HelpLine::heading("Cell operations", s),
        HelpLine::blank(),
        HelpLine::key("  x", "Clear the current cell", s),
        HelpLine::blank(),
        HelpLine::heading("Formulas", s),
        HelpLine::blank(),
        HelpLine::text(
            "  Formulas are computed items.  A formula belongs to a category",
            s,
        ),
        HelpLine::text(
            "  and derives its value from other items in that category.",
            s,
        ),
        HelpLine::blank(),
        HelpLine::text(
            "  Example:  in a Product category with items Widget and Gadget:",
            s,
        ),
        HelpLine::accent("    :formula Product Total = Widget + Gadget", s),
        HelpLine::blank(),
        HelpLine::text("  Supported operators:  +  -  *  /", s),
        HelpLine::text(
            "  Formula cells update automatically when source values change.",
            s,
        ),
    ]
}

fn page_panels(s: &HelpStyles) -> Vec<HelpLine> {
    vec![
        HelpLine::blank(),
        HelpLine::heading("Side panels", s),
        HelpLine::blank(),
        HelpLine::text(
            "  Panels open on the right side of the screen and give you",
            s,
        ),
        HelpLine::text("  quick access to formulas, categories, and views.", s),
        HelpLine::blank(),
        HelpLine::key("  F", "Toggle Formula panel", s),
        HelpLine::dim("       n", "New formula", s),
        HelpLine::dim("       d", "Delete selected formula", s),
        HelpLine::blank(),
        HelpLine::key("  C", "Toggle Category panel", s),
        HelpLine::dim("       n", "New category", s),
        HelpLine::dim("       a", "Add items to selected category", s),
        HelpLine::dim("       d", "Delete selected category/item", s),
        HelpLine::blank(),
        HelpLine::key("  N", "Quick-add a new category (from anywhere)", s),
        HelpLine::blank(),
        HelpLine::key("  V", "Toggle View panel", s),
        HelpLine::dim("       n", "New view", s),
        HelpLine::dim("       d", "Delete selected view", s),
        HelpLine::dim("       Enter", "Switch to selected view", s),
        HelpLine::blank(),
        HelpLine::key("  Tab", "Cycle focus between open panels", s),
        HelpLine::blank(),
        HelpLine::heading("Tile select mode (T)", s),
        HelpLine::blank(),
        HelpLine::text("  Tiles control which axis each category is placed on.", s),
        HelpLine::text("  Press T to enter tile-select mode, then:", s),
        HelpLine::blank(),
        HelpLine::key("  h / l  (← →)", "Select previous / next category tile", s),
        HelpLine::key("  Space / Enter", "Cycle axis: Row → Col → Page", s),
        HelpLine::key("  r", "Set axis to Row", s),
        HelpLine::key("  c", "Set axis to Col", s),
        HelpLine::key("  p", "Set axis to Page", s),
        HelpLine::key("  Esc", "Exit tile-select mode", s),
        HelpLine::blank(),
        HelpLine::heading("Groups and visibility", s),
        HelpLine::blank(),
        HelpLine::key("  z", "Toggle collapse of nearest group above cursor", s),
        HelpLine::key("  H", "Hide current row item", s),
        HelpLine::dim("       :show-item <cat> <item>", "Restore a hidden item", s),
    ]
}

fn page_commands(s: &HelpStyles) -> Vec<HelpLine> {
    vec![
        HelpLine::blank(),
        HelpLine::heading("Command line ( : )", s),
        HelpLine::blank(),
        HelpLine::text(
            "  Press : to open the command line.  Commands are entered",
            s,
        ),
        HelpLine::text("  vim-style and executed with Enter.  Esc cancels.", s),
        HelpLine::blank(),
        HelpLine::heading("File operations", s),
        HelpLine::blank(),
        HelpLine::key("  :w [path]", "Save (path optional after first save)", s),
        HelpLine::key("  :wq", "Save and quit", s),
        HelpLine::key("  :q", "Quit (warns if unsaved changes)", s),
        HelpLine::key("  :q!", "Force quit without saving", s),
        HelpLine::key("  ZZ", "Save and quit (same as :wq)", s),
        HelpLine::key("  Ctrl+S", "Save (same as :w)", s),
        HelpLine::blank(),
        HelpLine::heading("Import and export", s),
        HelpLine::blank(),
        HelpLine::key("  :import <path>", "Open the JSON/CSV import wizard", s),
        HelpLine::key("  :export [path.csv]", "Export the active view to CSV", s),
        HelpLine::blank(),
        HelpLine::heading("Model building", s),
        HelpLine::blank(),
        HelpLine::key("  :add-cat <name>", "Add a new category", s),
        HelpLine::key("  :add-item <cat> <item>", "Add one item to a category", s),
        HelpLine::key(
            "  :add-items <cat> a b c ...",
            "Add multiple items at once",
            s,
        ),
        HelpLine::key("  :formula <cat> <Name=expr>", "Add a formula", s),
        HelpLine::blank(),
        HelpLine::heading("Views", s),
        HelpLine::blank(),
        HelpLine::key("  :add-view [name]", "Create a new view", s),
        HelpLine::blank(),
        HelpLine::heading("Display", s),
        HelpLine::blank(),
        HelpLine::key("  :set-format <fmt>", "Set number format (e.g. ',.2')", s),
        HelpLine::blank(),
        HelpLine::heading("Other keys", s),
        HelpLine::blank(),
        HelpLine::key("  ?  or  F1", "Open this help screen", s),
        HelpLine::key("  :help", "Open this help screen", s),
    ]
}

/// Builds the content lines for a given page index.
fn page_content(page: usize) -> Vec<HelpLine> {
    let styles = HelpStyles::new();
    match page {
        0 => page_welcome(&styles),
        1 => page_navigation(&styles),
        2 => page_editing(&styles),
        3 => page_panels(&styles),
        4 => page_commands(&styles),
        _ => page_welcome(&styles),
    }
}

// ── Widget ──────────────────────────────────────────────────────────────────

pub struct HelpWidget {
    page: usize,
}

impl HelpWidget {
    pub fn new(page: usize) -> Self {
        // Clamp to valid range
        let page = page.min(HELP_PAGE_COUNT - 1);
        Self { page }
    }
}

impl Widget for HelpWidget {
    fn render(self, area: Rect, buf: &mut Buffer) {
        // Use most of the screen, leaving a small margin
        let margin_x = if area.width > 90 { 4 } else { 1 };
        let margin_y = if area.height > 30 { 2 } else { 1 };
        let popup_w = area.width.saturating_sub(margin_x * 2);
        let popup_h = area.height.saturating_sub(margin_y * 2);
        let x = area.x + margin_x;
        let y = area.y + margin_y;
        let popup_area = Rect::new(x, y, popup_w, popup_h);

        Clear.render(popup_area, buf);

        let block = Block::default()
            .borders(Borders::ALL)
            .title("  improvise — help  ")
            .border_style(Style::default().fg(Color::Blue));
        let inner = block.inner(popup_area);
        block.render(popup_area, buf);

        if inner.height < 4 || inner.width < 20 {
            return;
        }

        // ── Tab bar ─────────────────────────────────────────────────────
        let tab_y = inner.y;
        render_tab_bar(buf, inner.x, tab_y, inner.width, self.page);

        // ── Separator line ──────────────────────────────────────────────
        let sep_y = tab_y + 1;
        let sep_line: String = "─".repeat(inner.width as usize);
        buf.set_string(
            inner.x,
            sep_y,
            &sep_line,
            Style::default().fg(Color::DarkGray),
        );

        // ── Page content ────────────────────────────────────────────────
        let content_start_y = sep_y + 1;
        let content_height = inner.height.saturating_sub(4); // tab + sep + footer_sep + footer
        let lines = page_content(self.page);
        let key_col_width = 32usize;
        let normal_style = Style::default();

        for (i, line) in lines.iter().enumerate() {
            if i >= content_height as usize {
                break;
            }
            let ly = content_start_y + i as u16;

            if line.desc.is_empty() {
                // Single-column line (headings, text, blanks)
                buf.set_string(inner.x, ly, line.key, line.style);
            } else {
                // Two-column line: key on the left, description on the right
                buf.set_string(inner.x, ly, line.key, line.style);
                let desc_x = inner.x + key_col_width as u16;
                if desc_x < inner.x + inner.width {
                    buf.set_string(desc_x, ly, line.desc, normal_style);
                }
            }
        }

        // ── Footer separator ────────────────────────────────────────────
        let footer_sep_y = inner.y + inner.height - 2;
        let footer_sep: String = "─".repeat(inner.width as usize);
        buf.set_string(
            inner.x,
            footer_sep_y,
            &footer_sep,
            Style::default().fg(Color::DarkGray),
        );

        // ── Footer ─────────────────────────────────────────────────────
        let footer_y = inner.y + inner.height - 1;
        render_footer(buf, inner.x, footer_y, inner.width, self.page);
    }
}

/// Renders the tab bar showing all page titles with the active page highlighted.
fn render_tab_bar(buf: &mut Buffer, x: u16, y: u16, width: u16, active_page: usize) {
    let inactive_style = Style::default().fg(Color::DarkGray);
    let active_style = Style::default()
        .fg(Color::White)
        .bg(Color::Blue)
        .add_modifier(Modifier::BOLD);
    let separator_style = Style::default().fg(Color::DarkGray);

    let mut col = x;
    let max_col = x + width;

    for (i, title) in PAGE_TITLES.iter().enumerate() {
        if col >= max_col {
            break;
        }

        // Separator between tabs
        if i > 0 {
            if col + 3 >= max_col {
                break;
            }
            buf.set_string(col, y, " │ ", separator_style);
            col += 3;
        }

        let style = if i == active_page {
            active_style
        } else {
            inactive_style
        };

        let label = format!(" {} ", title);
        let label_width = label.len() as u16;
        if col + label_width > max_col {
            break;
        }
        buf.set_string(col, y, &label, style);
        col += label_width;
    }
}

/// Renders the footer with page navigation hints.
fn render_footer(buf: &mut Buffer, x: u16, y: u16, width: u16, page: usize) {
    let dim = Style::default().fg(Color::DarkGray);
    let key_style = Style::default().fg(Color::Cyan);

    let page_indicator = format!("  page {} of {}  ", page + 1, HELP_PAGE_COUNT);

    let nav_parts: Vec<(&str, Style)> = if page == 0 && HELP_PAGE_COUNT > 1 {
        vec![
            ("  ", dim),
            ("l", key_style),
            ("/", dim),
            ("Tab", key_style),
            (": next", dim),
        ]
    } else if page >= HELP_PAGE_COUNT - 1 {
        vec![
            ("  ", dim),
            ("h", key_style),
            ("/", dim),
            ("S-Tab", key_style),
            (": prev", dim),
        ]
    } else {
        vec![
            ("  ", dim),
            ("h", key_style),
            (": prev  ", dim),
            ("l", key_style),
            (": next", dim),
        ]
    };

    let close_parts: Vec<(&str, Style)> = vec![
        ("  ", dim),
        ("q", key_style),
        ("/", dim),
        ("Esc", key_style),
        (": close", dim),
    ];

    // Render page indicator on the left
    buf.set_string(x, y, &page_indicator, dim);

    // Render navigation hints after the page indicator
    let mut col = x + page_indicator.len() as u16;
    for (text, style) in &nav_parts {
        if col >= x + width {
            break;
        }
        buf.set_string(col, y, text, *style);
        col += text.len() as u16;
    }

    // Render close hint
    for (text, style) in &close_parts {
        if col >= x + width {
            break;
        }
        buf.set_string(col, y, text, *style);
        col += text.len() as u16;
    }
}
