use crate::cli::render::{self, Align, GlyphMode, Icons, LoreRenderer, StyledCell, Table, Theme};

use super::types::{
    ActivityEventType, AttentionState, MeActivityEvent, MeDashboard, MeIssue, MeMention, MeMr,
    MeSummary, SinceLastCheck,
};

// ─── Layout Helpers ─────────────────────────────────────────────────────────

/// Compute the title/summary column width for a section given its fixed overhead.
/// Returns a terminal-aware width with a minimum of 20.
fn title_width(overhead: usize) -> usize {
    render::flex_width(overhead, 20)
}

// ─── Glyph Mode Helper ──────────────────────────────────────────────────────

/// Get the current glyph mode, defaulting to Unicode if renderer not initialized.
fn glyph_mode() -> GlyphMode {
    LoreRenderer::try_get().map_or(GlyphMode::Unicode, LoreRenderer::glyph_mode)
}

// ─── Attention Icons ─────────────────────────────────────────────────────────

/// Return the attention icon for the current glyph mode.
fn attention_icon(state: &AttentionState) -> &'static str {
    let mode = glyph_mode();
    match state {
        AttentionState::NeedsAttention => match mode {
            GlyphMode::Nerd => "\u{f0f3}",    // bell
            GlyphMode::Unicode => "\u{25c6}", // diamond
            GlyphMode::Ascii => "[!]",
        },
        AttentionState::NotStarted => match mode {
            GlyphMode::Nerd => "\u{f005}",    // star
            GlyphMode::Unicode => "\u{2605}", // black star
            GlyphMode::Ascii => "[*]",
        },
        AttentionState::AwaitingResponse => match mode {
            GlyphMode::Nerd => "\u{f017}",    // clock
            GlyphMode::Unicode => "\u{25f7}", // white circle with upper right quadrant
            GlyphMode::Ascii => "[~]",
        },
        AttentionState::Stale => match mode {
            GlyphMode::Nerd => "\u{f54c}",    // skull
            GlyphMode::Unicode => "\u{2620}", // skull and crossbones
            GlyphMode::Ascii => "[x]",
        },
        AttentionState::NotReady => match mode {
            GlyphMode::Nerd => "\u{f040}",    // pencil
            GlyphMode::Unicode => "\u{270e}", // lower right pencil
            GlyphMode::Ascii => "[D]",
        },
    }
}

/// Style for an attention state.
fn attention_style(state: &AttentionState) -> lipgloss::Style {
    match state {
        AttentionState::NeedsAttention => Theme::warning(),
        AttentionState::NotStarted => Theme::info(),
        AttentionState::AwaitingResponse | AttentionState::Stale => Theme::dim(),
        AttentionState::NotReady => Theme::state_draft(),
    }
}

/// Render the styled attention icon for an item.
fn styled_attention(state: &AttentionState) -> String {
    let icon = attention_icon(state);
    attention_style(state).render(icon)
}

// ─── Merge Status Labels ────────────────────────────────────────────────────

/// Convert GitLab's `detailed_merge_status` API values to human-friendly labels.
fn humanize_merge_status(status: &str) -> &str {
    match status {
        "not_approved" => "needs approval",
        "requested_changes" => "changes requested",
        "mergeable" => "ready to merge",
        "not_open" => "not open",
        "checking" => "checking",
        "ci_must_pass" => "CI pending",
        "ci_still_running" => "CI running",
        "discussions_not_resolved" => "unresolved threads",
        "draft_status" => "draft",
        "need_rebase" => "needs rebase",
        "conflict" | "has_conflicts" => "has conflicts",
        "blocked_status" => "blocked",
        "approvals_syncing" => "syncing approvals",
        "jira_association_missing" => "missing Jira link",
        "unchecked" => "unchecked",
        other => other,
    }
}

// ─── Event Badges ────────────────────────────────────────────────────────────

/// Return the badge label text for an activity event type.
fn activity_badge_label(event_type: &ActivityEventType) -> String {
    match event_type {
        ActivityEventType::Note => "note",
        ActivityEventType::StatusChange => "status",
        ActivityEventType::LabelChange => "label",
        ActivityEventType::Assign | ActivityEventType::Unassign => "assign",
        ActivityEventType::ReviewRequest => "review",
        ActivityEventType::MilestoneChange => "milestone",
    }
    .to_string()
}

/// Return the style for an activity event badge.
fn activity_badge_style(event_type: &ActivityEventType) -> lipgloss::Style {
    match event_type {
        ActivityEventType::Note => Theme::info(),
        ActivityEventType::StatusChange => Theme::warning(),
        ActivityEventType::LabelChange => Theme::accent(),
        ActivityEventType::Assign
        | ActivityEventType::Unassign
        | ActivityEventType::ReviewRequest => Theme::success(),
        ActivityEventType::MilestoneChange => accent_magenta(),
    }
}

/// Magenta accent for milestone badges.
fn accent_magenta() -> lipgloss::Style {
    if LoreRenderer::try_get().is_some_and(LoreRenderer::colors_enabled) {
        lipgloss::Style::new().foreground("#d946ef")
    } else {
        lipgloss::Style::new()
    }
}

/// Very dark gray for system events (label, assign, status, milestone, review).
fn system_event_style() -> lipgloss::Style {
    if LoreRenderer::try_get().is_some_and(LoreRenderer::colors_enabled) {
        lipgloss::Style::new().foreground("#555555")
    } else {
        lipgloss::Style::new().faint()
    }
}

// ─── Summary Header ─────────────────────────────────────────────────────────

/// Print the summary header with counts and attention legend (Task #14).
pub fn print_summary_header(summary: &MeSummary, username: &str) {
    println!();
    println!(
        "{}",
        Theme::bold().render(&format!(
            "{} {} -- Personal Dashboard",
            Icons::user(),
            username,
        ))
    );
    println!("{}", "\u{2500}".repeat(render::terminal_width()));

    // Counts line
    let needs = if summary.needs_attention_count > 0 {
        Theme::warning().render(&format!("{} need attention", summary.needs_attention_count))
    } else {
        Theme::dim().render("0 need attention")
    };

    let mentioned = if summary.mentioned_in_count > 0 {
        format!("  {} mentioned", summary.mentioned_in_count)
    } else {
        String::new()
    };

    println!(
        "  {} projects  {} issues  {} authored MRs  {} reviewing MRs{}  {}",
        summary.project_count,
        summary.open_issue_count,
        summary.authored_mr_count,
        summary.reviewing_mr_count,
        mentioned,
        needs,
    );

    // Attention legend
    print_attention_legend();
}

/// Print the attention icon legend.
fn print_attention_legend() {
    println!();
    let states = [
        (AttentionState::NeedsAttention, "needs attention"),
        (AttentionState::NotStarted, "not started"),
        (AttentionState::AwaitingResponse, "awaiting response"),
        (AttentionState::Stale, "stale (30d+)"),
        (AttentionState::NotReady, "draft (not ready)"),
    ];

    let legend: Vec<String> = states
        .iter()
        .map(|(state, label)| format!("{} {}", styled_attention(state), Theme::dim().render(label)))
        .collect();

    println!("  {}", legend.join("  "));
}

// ─── Open Issues Section ─────────────────────────────────────────────────────

/// Print the open issues section (Task #15).
pub fn print_issues_section(issues: &[MeIssue], single_project: bool) {
    if issues.is_empty() {
        println!("{}", render::section_divider("Open Issues (0)"));
        println!(
            "    {}",
            Theme::dim().render("No open issues assigned to you.")
        );
        return;
    }

    println!(
        "{}",
        render::section_divider(&format!("Open Issues ({})", issues.len()))
    );

    for issue in issues {
        let attn = styled_attention(&issue.attention_state);
        let ref_str = format!("#{}", issue.iid);
        let status = issue
            .status_name
            .as_deref()
            .map(|s| format!(" [{s}]"))
            .unwrap_or_default();
        let time = render::format_relative_time(issue.updated_at);

        // Line 1: attention icon, issue ref, title, status, relative time
        println!(
            "    {} {}  {}{}  {}",
            attn,
            Theme::issue_ref().render(&ref_str),
            render::truncate(&issue.title, title_width(43)),
            Theme::dim().render(&status),
            Theme::dim().render(&time),
        );

        // Line 2: project path (suppressed in single-project mode)
        if !single_project {
            println!("      {}", Theme::dim().render(&issue.project_path),);
        }
    }
}

// ─── MR Sections ─────────────────────────────────────────────────────────────

/// Print the authored MRs section (Task #16).
pub fn print_authored_mrs_section(mrs: &[MeMr], single_project: bool) {
    if mrs.is_empty() {
        println!("{}", render::section_divider("Authored MRs (0)"));
        println!(
            "    {}",
            Theme::dim().render("No open MRs authored by you.")
        );
        return;
    }

    println!(
        "{}",
        render::section_divider(&format!("Authored MRs ({})", mrs.len()))
    );

    for mr in mrs {
        let attn = styled_attention(&mr.attention_state);
        let ref_str = format!("!{}", mr.iid);
        let draft = if mr.draft {
            Theme::state_draft().render(" [draft]")
        } else {
            String::new()
        };
        let merge_status = mr
            .detailed_merge_status
            .as_deref()
            .filter(|s| !s.is_empty() && *s != "not_open")
            .map(|s| format!(" ({})", humanize_merge_status(s)))
            .unwrap_or_default();
        let time = render::format_relative_time(mr.updated_at);

        // Line 1: attention, MR ref, title, draft, merge status, time
        println!(
            "    {} {}  {}{}{}  {}",
            attn,
            Theme::mr_ref().render(&ref_str),
            render::truncate(&mr.title, title_width(48)),
            draft,
            Theme::dim().render(&merge_status),
            Theme::dim().render(&time),
        );

        // Line 2: project path
        if !single_project {
            println!("      {}", Theme::dim().render(&mr.project_path),);
        }
    }
}

/// Print the reviewing MRs section (Task #16).
pub fn print_reviewing_mrs_section(mrs: &[MeMr], single_project: bool) {
    if mrs.is_empty() {
        println!("{}", render::section_divider("Reviewing MRs (0)"));
        println!(
            "    {}",
            Theme::dim().render("No open MRs awaiting your review.")
        );
        return;
    }

    println!(
        "{}",
        render::section_divider(&format!("Reviewing MRs ({})", mrs.len()))
    );

    for mr in mrs {
        let attn = styled_attention(&mr.attention_state);
        let ref_str = format!("!{}", mr.iid);
        let author = mr
            .author_username
            .as_deref()
            .map(|a| format!(" by {}", Theme::username().render(&format!("@{a}"))))
            .unwrap_or_default();
        let draft = if mr.draft {
            Theme::state_draft().render(" [draft]")
        } else {
            String::new()
        };
        let time = render::format_relative_time(mr.updated_at);

        // Line 1: attention, MR ref, title, author, draft, time
        println!(
            "    {} {}  {}{}{}  {}",
            attn,
            Theme::mr_ref().render(&ref_str),
            render::truncate(&mr.title, title_width(50)),
            author,
            draft,
            Theme::dim().render(&time),
        );

        // Line 2: project path
        if !single_project {
            println!("      {}", Theme::dim().render(&mr.project_path),);
        }
    }
}

// ─── Mentioned In Section ────────────────────────────────────────────────

/// Print the "Mentioned In" section for items where user is @mentioned but
/// not assigned, authored, or reviewing.
pub fn print_mentioned_in_section(mentions: &[MeMention], single_project: bool) {
    if mentions.is_empty() {
        return;
    }

    println!(
        "{}",
        render::section_divider(&format!("Mentioned In ({})", mentions.len()))
    );

    for item in mentions {
        let attn = styled_attention(&item.attention_state);
        let ref_str = match item.entity_type.as_str() {
            "issue" => format!("#{}", item.iid),
            "mr" => format!("!{}", item.iid),
            _ => format!("{}:{}", item.entity_type, item.iid),
        };
        let ref_style = match item.entity_type.as_str() {
            "issue" => Theme::issue_ref(),
            "mr" => Theme::mr_ref(),
            _ => Theme::bold(),
        };
        let state_tag = match item.state.as_str() {
            "opened" => String::new(),
            other => format!(" [{}]", other),
        };
        let time = render::format_relative_time(item.updated_at);

        println!(
            "    {} {}  {}{}  {}",
            attn,
            ref_style.render(&ref_str),
            render::truncate(&item.title, title_width(43)),
            Theme::dim().render(&state_tag),
            Theme::dim().render(&time),
        );

        if !single_project {
            println!("      {}", Theme::dim().render(&item.project_path));
        }
    }
}

// ─── Activity Feed ───────────────────────────────────────────────────────────

/// Print the activity feed section (Task #17).
pub fn print_activity_section(events: &[MeActivityEvent], single_project: bool) {
    if events.is_empty() {
        println!("{}", render::section_divider("Activity (0)"));
        println!(
            "    {}",
            Theme::dim().render("No recent activity on your items.")
        );
        return;
    }

    println!(
        "{}",
        render::section_divider(&format!("Activity ({})", events.len()))
    );

    // Columns: badge | ref | summary | actor | time
    // Table handles alignment, padding, and truncation automatically.
    let mut table = Table::new()
        .columns(5)
        .indent(4)
        .align(1, Align::Right)
        .align(4, Align::Right)
        .flex_col(2);

    for event in events {
        let badge_label = activity_badge_label(&event.event_type);
        let badge_style = activity_badge_style(&event.event_type);

        let ref_text = match event.entity_type.as_str() {
            "issue" => format!("#{}", event.entity_iid),
            "mr" => format!("!{}", event.entity_iid),
            _ => format!("{}:{}", event.entity_type, event.entity_iid),
        };
        let is_system = !matches!(event.event_type, ActivityEventType::Note);
        // System events → very dark gray; own notes → standard dim; else → full color.
        let subdued = is_system || event.is_own;
        let subdued_style = || {
            if is_system {
                system_event_style()
            } else {
                Theme::dim()
            }
        };

        let badge_style_final = if subdued {
            subdued_style()
        } else {
            badge_style
        };

        let ref_style = if subdued {
            Some(subdued_style())
        } else {
            match event.entity_type.as_str() {
                "issue" => Some(Theme::issue_ref()),
                "mr" => Some(Theme::mr_ref()),
                _ => None,
            }
        };

        let clean_summary = event.summary.replace('\n', " ");
        let summary_style: Option<lipgloss::Style> =
            if subdued { Some(subdued_style()) } else { None };

        let actor_text = if event.is_own {
            event
                .actor
                .as_deref()
                .map_or("(you)".to_string(), |a| format!("@{a} (you)"))
        } else {
            event
                .actor
                .as_deref()
                .map_or(String::new(), |a| format!("@{a}"))
        };
        let actor_style = if subdued {
            subdued_style()
        } else {
            Theme::username()
        };

        let time = render::format_relative_time_compact(event.timestamp);

        table.add_row(vec![
            StyledCell::styled(badge_label, badge_style_final),
            match ref_style {
                Some(s) => StyledCell::styled(ref_text, s),
                None => StyledCell::plain(ref_text),
            },
            match summary_style {
                Some(s) => StyledCell::styled(clean_summary, s),
                None => StyledCell::plain(clean_summary),
            },
            StyledCell::styled(actor_text, actor_style),
            StyledCell::styled(time, Theme::dim()),
        ]);
    }

    // Render table rows and interleave per-event detail lines
    let rendered = table.render();
    for (line, event) in rendered.lines().zip(events.iter()) {
        println!("{line}");
        if !single_project {
            println!("      {}", Theme::dim().render(&event.project_path));
        }
        if let Some(preview) = &event.body_preview
            && !preview.is_empty()
        {
            let clean = preview.replace('\n', " ");
            let truncated = render::truncate(&clean, render::flex_width(8, 30));
            println!("      {}", Theme::dim().render(&format!("\"{truncated}\"")));
        }
    }
}

/// Format an entity reference (#N for issues, !N for MRs), right-aligned to 6 chars.
#[cfg(test)]
fn format_entity_ref(entity_type: &str, iid: i64) -> String {
    match entity_type {
        "issue" => {
            let s = format!("{:>6}", format!("#{iid}"));
            Theme::issue_ref().render(&s)
        }
        "mr" => {
            let s = format!("{:>6}", format!("!{iid}"));
            Theme::mr_ref().render(&s)
        }
        _ => format!("{:>6}", format!("{entity_type}:{iid}")),
    }
}

// ─── Since Last Check ────────────────────────────────────────────────────────

/// Print the "since last check" section at the top of the dashboard.
pub fn print_since_last_check_section(since: &SinceLastCheck, single_project: bool) {
    let relative = render::format_relative_time(since.cursor_ms);

    if since.groups.is_empty() {
        println!(
            "\n    {}",
            Theme::dim().render(&format!(
                "No new events since {} ({relative})",
                render::format_datetime(since.cursor_ms),
            ))
        );
        return;
    }

    println!(
        "{}",
        render::section_divider(&format!("Since Last Check ({relative})"))
    );

    for group in &since.groups {
        // Entity header: !247  Fix race condition...
        let ref_str = match group.entity_type.as_str() {
            "issue" => format!("#{}", group.entity_iid),
            "mr" => format!("!{}", group.entity_iid),
            _ => format!("{}:{}", group.entity_type, group.entity_iid),
        };
        let ref_style = match group.entity_type.as_str() {
            "issue" => Theme::issue_ref(),
            "mr" => Theme::mr_ref(),
            _ => Theme::bold(),
        };

        println!();
        println!(
            "    {}  {}",
            ref_style.render(&ref_str),
            Theme::bold().render(&render::truncate(&group.entity_title, title_width(20))),
        );
        if !single_project {
            println!("      {}", Theme::dim().render(&group.project_path));
        }

        // Sub-events as indented rows
        let mut table = Table::new()
            .columns(3)
            .indent(6)
            .align(2, Align::Right)
            .flex_col(1);

        for event in &group.events {
            let badge = activity_badge_label(&event.event_type);
            let badge_style = activity_badge_style(&event.event_type);

            let actor_prefix = event
                .actor
                .as_deref()
                .map(|a| format!("@{a} "))
                .unwrap_or_default();
            let clean_summary = event.summary.replace('\n', " ");
            let summary_text = format!("{actor_prefix}{clean_summary}");

            let time = render::format_relative_time_compact(event.timestamp);

            table.add_row(vec![
                StyledCell::styled(badge, badge_style),
                StyledCell::plain(summary_text),
                StyledCell::styled(time, Theme::dim()),
            ]);
        }

        let rendered = table.render();
        for (line, event) in rendered.lines().zip(group.events.iter()) {
            println!("{line}");
            if let Some(preview) = &event.body_preview
                && !preview.is_empty()
            {
                let clean = preview.replace('\n', " ");
                let truncated = render::truncate(&clean, render::flex_width(10, 30));
                println!(
                    "        {}",
                    Theme::dim().render(&format!("\"{truncated}\""))
                );
            }
        }
    }

    // Footer
    println!(
        "\n    {}",
        Theme::dim().render(&format!(
            "{} events across {} items",
            since.total_event_count,
            since.groups.len()
        ))
    );
}

// ─── Full Dashboard ──────────────────────────────────────────────────────────

/// Render the complete human-mode dashboard.
pub fn print_me_dashboard(dashboard: &MeDashboard, single_project: bool) {
    if let Some(ref since) = dashboard.since_last_check {
        print_since_last_check_section(since, single_project);
    }
    print_summary_header(&dashboard.summary, &dashboard.username);
    print_issues_section(&dashboard.open_issues, single_project);
    print_authored_mrs_section(&dashboard.open_mrs_authored, single_project);
    print_reviewing_mrs_section(&dashboard.reviewing_mrs, single_project);
    print_mentioned_in_section(&dashboard.mentioned_in, single_project);
    print_activity_section(&dashboard.activity, single_project);
    println!();
}

/// Render a filtered dashboard (only requested sections).
pub fn print_me_dashboard_filtered(
    dashboard: &MeDashboard,
    single_project: bool,
    show_issues: bool,
    show_mrs: bool,
    show_mentions: bool,
    show_activity: bool,
) {
    if let Some(ref since) = dashboard.since_last_check {
        print_since_last_check_section(since, single_project);
    }
    print_summary_header(&dashboard.summary, &dashboard.username);

    if show_issues {
        print_issues_section(&dashboard.open_issues, single_project);
    }
    if show_mrs {
        print_authored_mrs_section(&dashboard.open_mrs_authored, single_project);
        print_reviewing_mrs_section(&dashboard.reviewing_mrs, single_project);
    }
    if show_mentions {
        print_mentioned_in_section(&dashboard.mentioned_in, single_project);
    }
    if show_activity {
        print_activity_section(&dashboard.activity, single_project);
    }
    println!();
}

// ─── Tests ───────────────────────────────────────────────────────────────────

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

    #[test]
    fn attention_icon_returns_nonempty_for_all_states() {
        let states = [
            AttentionState::NeedsAttention,
            AttentionState::NotStarted,
            AttentionState::AwaitingResponse,
            AttentionState::Stale,
            AttentionState::NotReady,
        ];
        for state in &states {
            assert!(!attention_icon(state).is_empty(), "empty for {state:?}");
        }
    }

    #[test]
    fn format_entity_ref_issue() {
        let result = format_entity_ref("issue", 42);
        assert!(result.contains("42"), "got: {result}");
    }

    #[test]
    fn format_entity_ref_mr() {
        let result = format_entity_ref("mr", 99);
        assert!(result.contains("99"), "got: {result}");
    }

    #[test]
    fn activity_badge_label_returns_nonempty_for_all_types() {
        let types = [
            ActivityEventType::Note,
            ActivityEventType::StatusChange,
            ActivityEventType::LabelChange,
            ActivityEventType::Assign,
            ActivityEventType::Unassign,
            ActivityEventType::ReviewRequest,
            ActivityEventType::MilestoneChange,
        ];
        for t in &types {
            assert!(!activity_badge_label(t).is_empty(), "empty for {t:?}");
        }
    }
}
