use ratatui::{
    layout::{Constraint, Direction, Layout, Rect},
    style::{Color, Modifier, Style},
    text::{Line, Span},
    widgets::{Block, Borders, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState, Tabs},
    Frame,
};

use crate::ai::{PromptLoader, PromptSource};
use crate::app::{App, HelpTab};
use crate::config::{Config, KeybindingsConfig};
use crate::syntax::available_themes;

/// Format a key display with padding for alignment
fn fmt_key(key: &str, width: usize) -> String {
    format!("  {:<width$}", key, width = width)
}

/// Format a label with padding for alignment (used in config tab)
fn fmt_label(label: &str, width: usize) -> String {
    format!("  {:<width$}", label, width = width)
}

pub fn render(frame: &mut Frame, app: &mut App) {
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(3),
            Constraint::Min(0),
            Constraint::Length(1),
        ])
        .split(frame.area());

    render_tab_header(frame, app, chunks[0]);

    match app.help_tab {
        HelpTab::Keybindings => render_keybindings_tab(frame, app, chunks[1]),
        HelpTab::Config => render_config_tab(frame, app, chunks[1]),
    }

    render_help_footer(frame, app, chunks[2]);
}

fn render_tab_header(frame: &mut Frame, app: &App, area: Rect) {
    let keybindings_style = if app.help_tab == HelpTab::Keybindings {
        Style::default()
            .fg(Color::Yellow)
            .add_modifier(Modifier::BOLD)
    } else {
        Style::default().fg(Color::DarkGray)
    };

    let config_style = if app.help_tab == HelpTab::Config {
        Style::default()
            .fg(Color::Yellow)
            .add_modifier(Modifier::BOLD)
    } else {
        Style::default().fg(Color::DarkGray)
    };

    let selected = match app.help_tab {
        HelpTab::Keybindings => 0,
        HelpTab::Config => 1,
    };

    let titles = vec![
        Line::from(Span::styled("Keybindings", keybindings_style)),
        Line::from(Span::styled("Config", config_style)),
    ];

    let tabs = Tabs::new(titles)
        .select(selected)
        .block(
            Block::default()
                .borders(Borders::ALL)
                .title("Help ([/]: switch tab)"),
        )
        .highlight_style(
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        );

    frame.render_widget(tabs, area);
}

fn render_keybindings_tab(frame: &mut Frame, app: &mut App, area: Rect) {
    let kb = &app.config.keybindings;
    let help_lines = build_help_lines(kb);
    let total_lines = help_lines.len();
    let content_height = area.height.saturating_sub(2) as usize;

    let max_scroll = total_lines.saturating_sub(content_height);
    if app.help_scroll_offset > max_scroll {
        app.help_scroll_offset = max_scroll;
    }

    let scroll_info = if total_lines > content_height {
        format!(" ({}/{})", app.help_scroll_offset + 1, max_scroll + 1)
    } else {
        String::new()
    };

    let help = Paragraph::new(help_lines)
        .block(
            Block::default()
                .borders(Borders::ALL)
                .title(format!("Keybindings{}", scroll_info)),
        )
        .scroll((app.help_scroll_offset as u16, 0));
    frame.render_widget(help, area);

    if total_lines > content_height {
        let mut scrollbar_state =
            ScrollbarState::new(max_scroll + 1).position(app.help_scroll_offset);
        let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
            .begin_symbol(None)
            .end_symbol(None);
        frame.render_stateful_widget(
            scrollbar,
            area.inner(ratatui::layout::Margin {
                vertical: 1,
                horizontal: 0,
            }),
            &mut scrollbar_state,
        );
    }
}

fn render_config_tab(frame: &mut Frame, app: &mut App, area: Rect) {
    let config_lines = build_config_lines(&app.config);
    let total_lines = config_lines.len();
    let content_height = area.height.saturating_sub(2) as usize;

    let max_scroll = total_lines.saturating_sub(content_height);
    if app.config_scroll_offset > max_scroll {
        app.config_scroll_offset = max_scroll;
    }

    let scroll_info = if total_lines > content_height {
        format!(" ({}/{})", app.config_scroll_offset + 1, max_scroll + 1)
    } else {
        String::new()
    };

    let config = Paragraph::new(config_lines)
        .block(
            Block::default()
                .borders(Borders::ALL)
                .title(format!("Config{}", scroll_info)),
        )
        .scroll((app.config_scroll_offset as u16, 0));
    frame.render_widget(config, area);

    if total_lines > content_height {
        let mut scrollbar_state =
            ScrollbarState::new(max_scroll + 1).position(app.config_scroll_offset);
        let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
            .begin_symbol(None)
            .end_symbol(None);
        frame.render_stateful_widget(
            scrollbar,
            area.inner(ratatui::layout::Margin {
                vertical: 1,
                horizontal: 0,
            }),
            &mut scrollbar_state,
        );
    }
}

fn render_help_footer(frame: &mut Frame, app: &App, area: Rect) {
    let kb = &app.config.keybindings;
    let footer_text = format!(
        " {}/{}: close | [/]: switch tab | j/k: scroll | g/G: top/bottom",
        kb.quit.display(),
        kb.help.display()
    );
    let footer = Paragraph::new(Line::from(Span::styled(
        footer_text,
        Style::default().fg(Color::DarkGray),
    )));
    frame.render_widget(footer, area);
}

/// Build a config value line with optional "(local)" override marker.
fn config_value_line(
    label: &str,
    value: &str,
    key: &str,
    overrides: &std::collections::HashSet<String>,
) -> Line<'static> {
    let label_width = 20;
    if overrides.contains(key) {
        Line::from(vec![
            Span::raw(format!("{}{}", fmt_label(label, label_width), value)),
            Span::styled(" (local)", Style::default().fg(Color::Cyan)),
        ])
    } else {
        Line::from(format!("{}{}", fmt_label(label, label_width), value))
    }
}

pub fn build_config_lines(config: &Config) -> Vec<Line<'static>> {
    let label_width = 20;
    let overrides = &config.local_overrides;

    let global_config_path = Config::config_path();
    let global_status = if config.loaded_global_config.is_some() {
        format!("{} [loaded]", global_config_path.display())
    } else {
        format!("{} [not found]", global_config_path.display())
    };

    let local_config_path = config.project_root.join(".octorus/config.toml");
    let local_status = if config.loaded_local_config.is_some() {
        if overrides.is_empty() {
            format!("{} [loaded]", local_config_path.display())
        } else {
            format!("{} [loaded, overrides global]", local_config_path.display())
        }
    } else {
        format!("{} [not found]", local_config_path.display())
    };

    let editor_display = config
        .editor
        .as_deref()
        .unwrap_or("(default: $EDITOR)")
        .to_string();

    let prompt_dir_display = config
        .ai
        .prompt_dir
        .as_deref()
        .unwrap_or("(default)")
        .to_string();

    // Resolve prompt sources
    let prompt_loader = PromptLoader::new(&config.ai, &config.project_root);
    let prompt_sources = prompt_loader.resolve_all_sources();

    let mut lines = vec![
        Line::from(""),
        Line::from(vec![Span::styled(
            "Config Files",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!(
            "{}{}",
            fmt_label("Global", label_width),
            global_status
        )),
        Line::from(format!(
            "{}{}",
            fmt_label("Local", label_width),
            local_status
        )),
        Line::from(format!(
            "{}{}",
            fmt_label("Project root", label_width),
            config.project_root.display()
        )),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Diff Settings",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        config_value_line("Theme", &config.diff.theme, "diff.theme", overrides),
        config_value_line(
            "Tab width",
            &config.diff.tab_width.to_string(),
            "diff.tab_width",
            overrides,
        ),
        config_value_line(
            "Background color",
            &config.diff.bg_color.to_string(),
            "diff.bg_color",
            overrides,
        ),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Layout Settings",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        config_value_line(
            "Left panel width",
            &format!("{}%", config.layout.left_panel_width),
            "layout.left_panel_width",
            overrides,
        ),
        config_value_line(
            "Zen mode",
            &config.layout.zen_mode.to_string(),
            "layout.zen_mode",
            overrides,
        ),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Editor",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        config_value_line("Editor", &editor_display, "editor", overrides),
        Line::from(""),
        Line::from(vec![Span::styled(
            "AI Rally Settings",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        config_value_line("Reviewer", &config.ai.reviewer, "ai.reviewer", overrides),
        config_value_line("Reviewee", &config.ai.reviewee, "ai.reviewee", overrides),
        config_value_line(
            "Max iterations",
            &config.ai.max_iterations.to_string(),
            "ai.max_iterations",
            overrides,
        ),
        config_value_line(
            "Timeout (secs)",
            &config.ai.timeout_secs.to_string(),
            "ai.timeout_secs",
            overrides,
        ),
        config_value_line(
            "Auto post",
            &config.ai.auto_post.to_string(),
            "ai.auto_post",
            overrides,
        ),
        config_value_line(
            "Review only",
            &config.ai.review_only.to_string(),
            "ai.review_only",
            overrides,
        ),
        config_value_line(
            "Prompt dir",
            &prompt_dir_display,
            "ai.prompt_dir",
            overrides,
        ),
    ];

    // Reviewer additional tools (always show so local overrides to empty are visible)
    let reviewer_tools_display = if config.ai.reviewer_additional_tools.is_empty() {
        "(none)".to_string()
    } else {
        config.ai.reviewer_additional_tools.join(", ")
    };
    lines.push(config_value_line(
        "Reviewer tools",
        &reviewer_tools_display,
        "ai.reviewer_additional_tools",
        overrides,
    ));

    // Reviewee additional tools
    let reviewee_tools_display = if config.ai.reviewee_additional_tools.is_empty() {
        "(none)".to_string()
    } else {
        config.ai.reviewee_additional_tools.join(", ")
    };
    lines.push(config_value_line(
        "Reviewee tools",
        &reviewee_tools_display,
        "ai.reviewee_additional_tools",
        overrides,
    ));

    // Reviewee proposal additional tools (read-only mode)
    let proposal_tools_display = if config.ai.reviewee_proposal_additional_tools.is_empty() {
        "(none)".to_string()
    } else {
        config.ai.reviewee_proposal_additional_tools.join(", ")
    };
    lines.push(config_value_line(
        "Proposal tools",
        &proposal_tools_display,
        "ai.reviewee_proposal_additional_tools",
        overrides,
    ));

    // Post strategy for reviewee proposals
    let post_strategy_display = match config.ai.post_reviewee_proposals {
        crate::config::ProposalPostStrategy::Final => "final",
        crate::config::ProposalPostStrategy::Each => "each",
        crate::config::ProposalPostStrategy::None => "none",
    };
    lines.push(config_value_line(
        "Post proposals",
        post_strategy_display,
        "ai.post_reviewee_proposals",
        overrides,
    ));

    lines.push(Line::from(""));
    lines.push(Line::from(vec![Span::styled(
        "Shell Settings",
        Style::default()
            .fg(Color::Yellow)
            .add_modifier(Modifier::BOLD),
    )]));
    lines.push(config_value_line(
        "Timeout (secs)",
        &config.shell.timeout_secs.to_string(),
        "shell.timeout_secs",
        overrides,
    ));

    lines.push(Line::from(""));
    lines.push(Line::from(vec![Span::styled(
        "Prompt Resolution",
        Style::default()
            .fg(Color::Yellow)
            .add_modifier(Modifier::BOLD),
    )]));

    for (filename, source) in prompt_sources {
        let source_display = match source {
            PromptSource::Local(path) => format!("local ({})", path.display()),
            PromptSource::PromptDir(path) => format!("prompt_dir ({})", path.display()),
            PromptSource::Global(path) => format!("global ({})", path.display()),
            PromptSource::Embedded => "embedded default".to_string(),
        };
        lines.push(Line::from(format!(
            "{}{}",
            fmt_label(&filename, label_width),
            source_display
        )));
    }

    lines.push(Line::from(""));

    lines
}

fn build_help_lines(kb: &KeybindingsConfig) -> Vec<Line<'static>> {
    let key_width = 14; // Width for key column

    vec![
        Line::from(""),
        Line::from(vec![Span::styled(
            "Global",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!(
            "{}  Show this help",
            fmt_key(&kb.help.display(), key_width)
        )),
        Line::from(format!(
            "{}  Execute shell command",
            fmt_key(&kb.shell_command.display(), key_width)
        )),
        Line::from(format!(
            "{}  Quit / Back",
            fmt_key(&kb.quit.display(), key_width)
        )),
        Line::from(""),
        Line::from(vec![Span::styled(
            "File List View",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!(
            "{}  Move selection",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Open split view",
            fmt_key(&kb.open_panel.display(), key_width)
        )),
        Line::from(format!(
            "{}  Toggle tree view",
            fmt_key(&kb.tree_toggle.display(), key_width)
        )),
        Line::from(format!(
            "{}  Mark selected file as viewed",
            fmt_key(&kb.mark_viewed.display(), key_width)
        )),
        Line::from(format!(
            "{}  Mark selected directory as viewed",
            fmt_key(&kb.mark_viewed_dir.display(), key_width)
        )),
        Line::from(format!(
            "{}  Approve PR",
            fmt_key(&kb.approve.display(), key_width)
        )),
        Line::from(format!(
            "{}  Request changes",
            fmt_key(&kb.request_changes.display(), key_width)
        )),
        Line::from(format!(
            "{}  Comment only",
            fmt_key(&kb.comment.display(), key_width)
        )),
        Line::from(format!(
            "{}  View review comments",
            fmt_key(&kb.comment_list.display(), key_width)
        )),
        Line::from(format!(
            "{}  View PR description",
            fmt_key(&kb.pr_description.display(), key_width)
        )),
        Line::from(format!(
            "{}  View CI checks",
            fmt_key(&kb.ci_checks.display(), key_width)
        )),
        Line::from(format!(
            "{}  Start AI Rally (local mode seeds from local comments)",
            fmt_key(&kb.ai_rally.display(), key_width)
        )),
        Line::from(format!(
            "{}  Open PR in browser",
            fmt_key(&kb.open_in_browser.display(), key_width)
        )),
        Line::from(format!(
            "{}  Refresh (clear cache and reload)",
            fmt_key(&kb.refresh.display(), key_width)
        )),
        Line::from(format!(
            "{}  Toggle help",
            fmt_key(&kb.help.display(), key_width)
        )),
        Line::from(format!(
            "{}  Toggle local diff mode",
            fmt_key(&kb.toggle_local_mode.display(), key_width)
        )),
        Line::from(format!(
            "{}  Toggle auto-focus (local mode)",
            fmt_key(&kb.toggle_auto_focus.display(), key_width)
        )),
        Line::from(format!(
            "{}  Toggle zen mode",
            fmt_key(&kb.toggle_zen_mode.display(), key_width)
        )),
        Line::from(format!(
            "{}  Filter list",
            fmt_key(&kb.filter.display(), key_width)
        )),
        Line::from(format!(
            "{}  Open issue list",
            fmt_key(&kb.issue_list.display(), key_width)
        )),
        Line::from(format!("{}  Quit", fmt_key(&kb.quit.display(), key_width))),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Split View",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(vec![Span::styled(
            "  File List Focus:",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(format!(
            "{}  Move file selection (diff follows)",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Scroll diff page (regardless of focus)",
            fmt_key(
                &format!(
                    "{}/{}",
                    kb.diff_page_down.display(),
                    kb.diff_page_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Filter list",
            fmt_key(&kb.filter.display(), key_width)
        )),
        Line::from(format!(
            "{}, Right, {}     Focus diff pane",
            fmt_key(&kb.open_panel.display(), 5),
            kb.move_right.display()
        )),
        Line::from(format!(
            "  Left, {}, {}    Back to file list",
            kb.move_left.display(),
            kb.quit.display()
        )),
        Line::from(vec![Span::styled(
            "  Diff Focus:",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(format!(
            "{}  Scroll diff",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Page scroll (also J/K)",
            fmt_key(
                &format!("{}/{}", kb.page_down.display(), kb.page_up.display()),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Scroll diff page (regardless of focus)",
            fmt_key(
                &format!(
                    "{}/{}",
                    kb.diff_page_down.display(),
                    kb.diff_page_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Go to definition",
            fmt_key(&kb.go_to_definition.display(), key_width)
        )),
        Line::from(format!(
            "{}  Open file in $EDITOR",
            fmt_key(&kb.go_to_file.display(), key_width)
        )),
        Line::from(format!(
            "{}  Jump to first/last line",
            fmt_key(
                &format!(
                    "{}/{}",
                    kb.jump_to_first.display(),
                    kb.jump_to_last.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Jump back",
            fmt_key(&kb.jump_back.display(), key_width)
        )),
        Line::from(format!(
            "{}  Next/prev comment",
            fmt_key(
                &format!(
                    "{}/{}",
                    kb.next_comment.display(),
                    kb.prev_comment.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Open comment panel",
            fmt_key(&kb.open_panel.display(), key_width)
        )),
        Line::from(format!(
            "  Right, {}       Open fullscreen diff",
            kb.move_right.display()
        )),
        Line::from(format!(
            "  Left, {}        Back to file focus",
            kb.move_left.display()
        )),
        Line::from(format!(
            "{}  Back to file list",
            fmt_key(&kb.quit.display(), key_width)
        )),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Diff View",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!(
            "{}  Move line selection",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Go to definition",
            fmt_key(&kb.go_to_definition.display(), key_width)
        )),
        Line::from(format!(
            "{}  Open file in $EDITOR",
            fmt_key(&kb.go_to_file.display(), key_width)
        )),
        Line::from(format!(
            "{}  Jump to first/last line",
            fmt_key(
                &format!(
                    "{}/{}",
                    kb.jump_to_first.display(),
                    kb.jump_to_last.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Jump back",
            fmt_key(&kb.jump_back.display(), key_width)
        )),
        Line::from(format!(
            "{}  Jump to next comment",
            fmt_key(&kb.next_comment.display(), key_width)
        )),
        Line::from(format!(
            "{}  Jump to previous comment",
            fmt_key(&kb.prev_comment.display(), key_width)
        )),
        Line::from(format!(
            "{}  Open comment panel",
            fmt_key(&kb.open_panel.display(), key_width)
        )),
        Line::from(format!(
            "{}  Page down (also J)",
            fmt_key(&kb.page_down.display(), key_width)
        )),
        Line::from(format!(
            "{}  Page up (also K)",
            fmt_key(&kb.page_up.display(), key_width)
        )),
        Line::from(format!(
            "{}  Scroll diff page down",
            fmt_key(&kb.diff_page_down.display(), key_width)
        )),
        Line::from(format!(
            "{}  Scroll diff page up",
            fmt_key(&kb.diff_page_up.display(), key_width)
        )),
        Line::from(format!(
            "{}  Add comment at line",
            fmt_key(&kb.comment.display(), key_width)
        )),
        Line::from(format!(
            "{}  Add suggestion at line",
            fmt_key(&kb.suggestion.display(), key_width)
        )),
        Line::from(format!(
            "{}  Multiline select mode",
            fmt_key(
                &format!("{}/Shift+Enter", kb.multiline_select.display()),
                key_width,
            )
        )),
        Line::from(vec![Span::styled(
            "  Multiline Select Mode:",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(format!(
            "{}  Extend selection",
            fmt_key(
                &format!("{}/{}", kb.move_down.display(), kb.move_up.display()),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Comment on selection",
            fmt_key(&kb.comment.display(), key_width)
        )),
        Line::from(format!(
            "{}  Suggest on selection",
            fmt_key(&kb.suggestion.display(), key_width)
        )),
        Line::from(format!("{}  Cancel selection", fmt_key("Esc", key_width))),
        Line::from(format!(
            "{}  Toggle markdown rich display",
            fmt_key(&kb.toggle_markdown_rich.display(), key_width)
        )),
        Line::from(format!(
            "{}  Back to file list",
            fmt_key(&format!("{}, Esc", kb.quit.display()), key_width)
        )),
        Line::from(vec![Span::styled(
            "  Comment Panel (focused):",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(format!(
            "{}  Scroll panel",
            fmt_key(
                &format!("{}/{}", kb.move_down.display(), kb.move_up.display()),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Add comment",
            fmt_key(&kb.comment.display(), key_width)
        )),
        Line::from(format!(
            "{}  Add suggestion",
            fmt_key(&kb.suggestion.display(), key_width)
        )),
        Line::from(format!(
            "{}  Reply to comment",
            fmt_key(&kb.reply.display(), key_width)
        )),
        Line::from(format!(
            "{}  Select reply target (multiple)",
            fmt_key(
                &format!(
                    "{}/Shift-{}",
                    kb.tab_switch.display(),
                    kb.tab_switch.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Jump to next/prev comment",
            fmt_key(
                &format!(
                    "{}/{}",
                    kb.next_comment.display(),
                    kb.prev_comment.display()
                ),
                key_width
            )
        )),
        Line::from(format!("  Esc/{}        Close panel", kb.quit.display())),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Comment List View",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!(
            "{}  Switch tab (Review/Discussion)",
            fmt_key(
                &format!("{}, {}", kb.tab_prev.display(), kb.tab_next.display()),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Move selection",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Review: Jump to file | Discussion: View detail",
            fmt_key(&kb.open_panel.display(), key_width)
        )),
        Line::from(format!(
            "{}  Back to file list",
            fmt_key(&format!("{}, Esc", kb.quit.display()), key_width)
        )),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Local Mode",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(vec![Span::styled(
            "  In local mode, you can leave review comments on your own diff.",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(vec![Span::styled(
            "  Comments persist to disk across sessions.",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(format!(
            "{}  Add comment on diff line",
            fmt_key(&kb.comment.display(), key_width)
        )),
        Line::from(format!(
            "{}  Add suggestion on diff line",
            fmt_key(&kb.suggestion.display(), key_width)
        )),
        Line::from(format!(
            "{}  Reply to comment",
            fmt_key(&kb.reply.display(), key_width)
        )),
        Line::from(format!(
            "{}  View local comments (no Discussion tab)",
            fmt_key(&kb.comment_list.display(), key_width)
        )),
        Line::from(format!(
            "{}  Start AI Rally (seeds from open local comments)",
            fmt_key(&kb.ai_rally.display(), key_width)
        )),
        Line::from(format!(
            "{}  Toggle auto-focus on changed files",
            fmt_key(&kb.toggle_auto_focus.display(), key_width)
        )),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Input Mode (Comment/Suggestion/Reply)",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!(
            "{}  Submit",
            fmt_key(&kb.submit.display(), key_width)
        )),
        Line::from("  Esc             Cancel input"),
        Line::from(vec![Span::styled(
            "  Readline-style editing keys:",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from("  Ctrl-A / Ctrl-E    Jump to line start / end"),
        Line::from("  Ctrl-B / Ctrl-F    Move cursor back / forward one char"),
        Line::from("  Ctrl-P / Ctrl-N    Move cursor up / down one line"),
        Line::from("  Ctrl-D             Delete char under cursor"),
        Line::from("  Ctrl-H             Backspace"),
        Line::from("  Ctrl-K             Kill to end of line"),
        Line::from("  Ctrl-U             Kill to start of line"),
        Line::from("  Ctrl-W             Delete previous word"),
        Line::from(""),
        Line::from(vec![Span::styled(
            "AI Rally View",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(vec![Span::styled(
            "  (When AI requests permission or clarification)",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(format!(
            "{}  Grant permission / Answer yes",
            fmt_key(&kb.confirm_yes.display(), key_width)
        )),
        Line::from(format!(
            "{}  Deny permission / Skip",
            fmt_key(&kb.confirm_no.display(), key_width)
        )),
        Line::from(format!(
            "{}  Retry on error",
            fmt_key(&kb.retry.display(), key_width)
        )),
        Line::from(format!(
            "{}  Send to background",
            fmt_key(&kb.rally_background.display(), key_width)
        )),
        Line::from(format!(
            "{}  Pause / Resume",
            fmt_key(&kb.rally_pause.display(), key_width)
        )),
        Line::from(format!(
            "{}  Abort rally",
            fmt_key(&kb.quit.display(), key_width)
        )),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Git Ops View",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!(
            "{}  Open git ops (from File List)",
            fmt_key(&kb.git_ops.display(), key_width)
        )),
        Line::from(vec![Span::styled(
            "  Tree Focus:",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(format!(
            "{}  Move selection",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Stage/unstage file or directory",
            fmt_key(&kb.git_ops_stage.display(), key_width)
        )),
        Line::from(format!(
            "{}  Stage all files",
            fmt_key(&kb.git_ops_stage_all.display(), key_width)
        )),
        Line::from(format!(
            "{}  Discard changes",
            fmt_key(&kb.git_ops_discard.display(), key_width)
        )),
        Line::from(format!(
            "{}  Commit (opens editor)",
            fmt_key(&kb.git_ops_commit.display(), key_width)
        )),
        Line::from(format!(
            "{}  Undo last operation",
            fmt_key(&kb.git_ops_undo.display(), key_width)
        )),
        Line::from(format!(
            "{}  Refresh status",
            fmt_key(&kb.refresh.display(), key_width)
        )),
        Line::from(format!(
            "{}  Push to origin",
            fmt_key(&kb.git_ops_push.display(), key_width)
        )),
        Line::from(format!(
            "{}  Toggle directory expand/collapse",
            fmt_key(&kb.open_panel.display(), key_width)
        )),
        Line::from(format!(
            "{}  Switch to commits pane",
            fmt_key(&kb.tab_switch.display(), key_width)
        )),
        Line::from(format!(
            "{}  Focus diff pane",
            fmt_key("Enter (file), l", key_width)
        )),
        Line::from(format!(
            "{}  Close git ops",
            fmt_key(&format!("{}, Esc", kb.quit.display()), key_width)
        )),
        Line::from(vec![Span::styled(
            "  Commits Focus:",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(format!(
            "{}  Move commit selection",
            fmt_key("j/k, Down/Up", key_width)
        )),
        Line::from(format!("{}  Jump to first/last", fmt_key("g/G", key_width))),
        Line::from(format!(
            "{}  Undo last operation",
            fmt_key(&kb.git_ops_undo.display(), key_width)
        )),
        Line::from(format!(
            "{}  Reset --soft to selected commit (local mode only)",
            fmt_key(&kb.git_ops_reset.display(), key_width)
        )),
        Line::from(format!(
            "{}  Switch to tree pane",
            fmt_key("Tab", key_width)
        )),
        Line::from(format!(
            "{}  Focus diff pane (commit diff)",
            fmt_key("Enter, l", key_width)
        )),
        Line::from(vec![Span::styled(
            "  Diff Focus:",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(format!(
            "{}  Scroll diff",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Page scroll (also J/K)",
            fmt_key(
                &format!("{}/{}", kb.page_down.display(), kb.page_up.display()),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Jump to first/last",
            fmt_key(
                &format!(
                    "{}/{}",
                    kb.jump_to_first.display(),
                    kb.jump_to_last.display()
                ),
                key_width
            )
        )),
        Line::from("  h/Left/Esc      Back to previous pane"),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Git Ops View",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!(
            "{}  Open git ops (from File List)",
            fmt_key(&kb.git_ops.display(), key_width)
        )),
        Line::from(vec![Span::styled(
            "  Tree Focus:",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(format!(
            "{}  Move selection",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Stage/unstage file or directory",
            fmt_key(&kb.git_ops_stage.display(), key_width)
        )),
        Line::from(format!(
            "{}  Stage all files",
            fmt_key(&kb.git_ops_stage_all.display(), key_width)
        )),
        Line::from(format!(
            "{}  Discard changes",
            fmt_key(&kb.git_ops_discard.display(), key_width)
        )),
        Line::from(format!(
            "{}  Commit (opens editor)",
            fmt_key(&kb.git_ops_commit.display(), key_width)
        )),
        Line::from(format!(
            "{}  Undo last operation",
            fmt_key(&kb.git_ops_undo.display(), key_width)
        )),
        Line::from(format!(
            "{}  Refresh status",
            fmt_key(&kb.refresh.display(), key_width)
        )),
        Line::from(format!(
            "{}  Toggle directory expand/collapse",
            fmt_key(&kb.open_panel.display(), key_width)
        )),
        Line::from(format!(
            "{}  Focus diff pane",
            fmt_key(&format!("{}, l", kb.tab_switch.display()), key_width)
        )),
        Line::from(format!(
            "{}  Close git ops",
            fmt_key(&format!("{}, Esc", kb.quit.display()), key_width)
        )),
        Line::from(vec![Span::styled(
            "  Diff Focus:",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(format!(
            "{}  Scroll diff",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Page scroll (also J/K)",
            fmt_key(
                &format!("{}/{}", kb.page_down.display(), kb.page_up.display()),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Jump to first/last",
            fmt_key(
                &format!(
                    "{}/{}",
                    kb.jump_to_first.display(),
                    kb.jump_to_last.display()
                ),
                key_width
            )
        )),
        Line::from("  h/Left/Esc      Back to previous pane"),
        Line::from(""),
        Line::from(vec![Span::styled(
            "PR List View",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!(
            "{}  Move selection",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  View PR",
            fmt_key(&kb.open_panel.display(), key_width)
        )),
        Line::from(format!(
            "{}  Open issue list",
            fmt_key(&kb.issue_list.display(), key_width)
        )),
        Line::from(format!(
            "{}  Filter: open/closed/all",
            fmt_key(
                &format!(
                    "{}/{}/{}",
                    kb.filter_open.display(),
                    kb.filter_closed.display(),
                    kb.filter_all.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Open in browser",
            fmt_key(&kb.open_in_browser.display(), key_width)
        )),
        Line::from(format!(
            "{}  Refresh",
            fmt_key(&kb.refresh.display(), key_width)
        )),
        Line::from(format!(
            "{}  Filter list",
            fmt_key(&kb.filter.display(), key_width)
        )),
        Line::from(format!("{}  Quit", fmt_key(&kb.quit.display(), key_width))),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Issue List View",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!(
            "{}  Move selection",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  View issue detail",
            fmt_key(&kb.open_panel.display(), key_width)
        )),
        Line::from(format!(
            "{}  Filter: open/closed/all",
            fmt_key(
                &format!(
                    "{}/{}/{}",
                    kb.filter_open.display(),
                    kb.filter_closed.display(),
                    kb.filter_all.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Open in browser",
            fmt_key(&kb.open_in_browser.display(), key_width)
        )),
        Line::from(format!(
            "{}  Refresh",
            fmt_key(&kb.refresh.display(), key_width)
        )),
        Line::from(format!(
            "{}  Filter list",
            fmt_key(&kb.filter.display(), key_width)
        )),
        Line::from(format!(
            "{}  Back to PR list",
            fmt_key(&kb.quit.display(), key_width)
        )),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Issue Detail View",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!(
            "{}  Scroll body",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  Switch focus (Body/Linked PRs)",
            fmt_key(&kb.tab_switch.display(), key_width)
        )),
        Line::from(format!(
            "{}  Open linked PR",
            fmt_key(&kb.open_panel.display(), key_width)
        )),
        Line::from(format!(
            "{}  Open in browser",
            fmt_key(&kb.open_in_browser.display(), key_width)
        )),
        Line::from(format!(
            "{}  Add comment",
            fmt_key(&kb.comment.display(), key_width)
        )),
        Line::from(format!(
            "{}  View issue comments",
            fmt_key(&kb.comment_list.display(), key_width)
        )),
        Line::from(format!(
            "{}  Toggle markdown rich display",
            fmt_key(&kb.toggle_markdown_rich.display(), key_width)
        )),
        Line::from(format!(
            "{}  Back to issue list",
            fmt_key(&format!("{}, Esc", kb.quit.display()), key_width)
        )),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Issue Comment List View",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!(
            "{}  Move selection",
            fmt_key(
                &format!(
                    "{}/{}, Down/Up",
                    kb.move_down.display(),
                    kb.move_up.display()
                ),
                key_width
            )
        )),
        Line::from(format!(
            "{}  View comment detail",
            fmt_key(&kb.open_panel.display(), key_width)
        )),
        Line::from(format!(
            "{}  Add comment",
            fmt_key(&kb.comment.display(), key_width)
        )),
        Line::from(format!(
            "{}  Reply to comment (in detail mode)",
            fmt_key(&kb.reply.display(), key_width)
        )),
        Line::from(format!(
            "{}  Open in browser",
            fmt_key(&kb.open_in_browser.display(), key_width)
        )),
        Line::from(format!(
            "{}  Back to issue detail",
            fmt_key(&format!("{}, Esc", kb.quit.display()), key_width)
        )),
        Line::from(""),
        Line::from(vec![Span::styled(
            "Available Themes",
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(format!("  {}", available_themes().join(", "))),
        Line::from(vec![Span::styled(
            "  Set in ~/.config/octorus/config.toml: [diff] theme = \"Dracula\"",
            Style::default().fg(Color::DarkGray),
        )]),
        Line::from(""),
    ]
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_build_config_lines_does_not_panic() {
        let config = Config::default();
        let lines = build_config_lines(&config);
        assert!(!lines.is_empty());
    }

    #[test]
    fn test_build_config_lines_contains_sections() {
        let config = Config::default();
        let lines = build_config_lines(&config);
        let text: Vec<String> = lines.iter().map(|l| l.to_string()).collect();
        let joined = text.join("\n");

        assert!(joined.contains("Config Files"));
        assert!(joined.contains("Diff Settings"));
        assert!(joined.contains("Layout Settings"));
        assert!(joined.contains("Editor"));
        assert!(joined.contains("AI Rally Settings"));
        assert!(joined.contains("Prompt Resolution"));
    }

    #[test]
    fn test_build_help_lines_lists_readline_editing_keys() {
        // The text-input section in the help screen documents the emacs-style
        // editing bindings. Anchor on a few of them so a future refactor of
        // help.rs that drops the section is caught.
        let kb = KeybindingsConfig::default();
        let lines = build_help_lines(&kb);
        let joined: String = lines
            .iter()
            .map(|l| l.to_string())
            .collect::<Vec<_>>()
            .join("\n");
        assert!(joined.contains("Readline-style editing keys"));
        for key in ["Ctrl-A", "Ctrl-E", "Ctrl-W", "Ctrl-K", "Ctrl-U"] {
            assert!(joined.contains(key), "help missing binding: {key}");
        }
    }

    #[test]
    fn test_build_config_lines_shows_default_values() {
        let config = Config::default();
        let lines = build_config_lines(&config);
        let text: Vec<String> = lines.iter().map(|l| l.to_string()).collect();
        let joined = text.join("\n");

        assert!(joined.contains("base16-ocean.dark"));
        assert!(joined.contains("35%"));
        assert!(joined.contains("Layout Settings"));
        assert!(joined.contains("claude"));
        // Prompt resolution should show some source (embedded or global depending on env)
        assert!(
            joined.contains("reviewer.md"),
            "Should contain prompt filename"
        );
    }
}
