use rusqlite::Connection;

use crate::cli::render::{self, Icons, Theme};
use crate::core::error::Result;
use crate::core::time::ms_to_iso;

use super::types::*;

// ─── Query: Workload Mode ───────────────────────────────────────────────────

pub(super) fn query_workload(
    conn: &Connection,
    username: &str,
    project_id: Option<i64>,
    since_ms: Option<i64>,
    limit: usize,
    include_closed: bool,
) -> Result<WorkloadResult> {
    // Prevent overflow: saturating_add caps at usize::MAX instead of wrapping to 0.
    // The .min() ensures the value fits in i64 for SQLite's LIMIT clause.
    let limit_plus_one = limit.saturating_add(1).min(i64::MAX as usize) as i64;

    // Query 1: Open issues assigned to user
    let issues_sql = "SELECT i.iid,
                (p.path_with_namespace || '#' || i.iid) AS ref,
                i.title, p.path_with_namespace, i.updated_at
         FROM issues i
         JOIN issue_assignees ia ON ia.issue_id = i.id
         JOIN projects p ON i.project_id = p.id
         WHERE ia.username = ?1
           AND i.state = 'opened'
           AND (?2 IS NULL OR i.project_id = ?2)
           AND (?3 IS NULL OR i.updated_at >= ?3)
         ORDER BY i.updated_at DESC
         LIMIT ?4";

    let mut stmt = conn.prepare_cached(issues_sql)?;
    let assigned_issues: Vec<WorkloadIssue> = stmt
        .query_map(
            rusqlite::params![username, project_id, since_ms, limit_plus_one],
            |row| {
                Ok(WorkloadIssue {
                    iid: row.get(0)?,
                    ref_: row.get(1)?,
                    title: row.get(2)?,
                    project_path: row.get(3)?,
                    updated_at: row.get(4)?,
                })
            },
        )?
        .collect::<std::result::Result<Vec<_>, _>>()?;

    // Query 2: Open MRs authored
    let authored_sql = "SELECT m.iid,
                (p.path_with_namespace || '!' || m.iid) AS ref,
                m.title, m.draft, p.path_with_namespace, m.updated_at
         FROM merge_requests m
         JOIN projects p ON m.project_id = p.id
         WHERE m.author_username = ?1
           AND m.state = 'opened'
           AND (?2 IS NULL OR m.project_id = ?2)
           AND (?3 IS NULL OR m.updated_at >= ?3)
         ORDER BY m.updated_at DESC
         LIMIT ?4";
    let mut stmt = conn.prepare_cached(authored_sql)?;
    let authored_mrs: Vec<WorkloadMr> = stmt
        .query_map(
            rusqlite::params![username, project_id, since_ms, limit_plus_one],
            |row| {
                Ok(WorkloadMr {
                    iid: row.get(0)?,
                    ref_: row.get(1)?,
                    title: row.get(2)?,
                    draft: row.get::<_, i32>(3)? != 0,
                    project_path: row.get(4)?,
                    author_username: None,
                    updated_at: row.get(5)?,
                })
            },
        )?
        .collect::<std::result::Result<Vec<_>, _>>()?;

    // Query 3: Open MRs where user is reviewer
    let reviewing_sql = "SELECT m.iid,
                (p.path_with_namespace || '!' || m.iid) AS ref,
                m.title, m.draft, p.path_with_namespace,
                m.author_username, m.updated_at
         FROM merge_requests m
         JOIN mr_reviewers r ON r.merge_request_id = m.id
         JOIN projects p ON m.project_id = p.id
         WHERE r.username = ?1
           AND m.state = 'opened'
           AND (?2 IS NULL OR m.project_id = ?2)
           AND (?3 IS NULL OR m.updated_at >= ?3)
         ORDER BY m.updated_at DESC
         LIMIT ?4";
    let mut stmt = conn.prepare_cached(reviewing_sql)?;
    let reviewing_mrs: Vec<WorkloadMr> = stmt
        .query_map(
            rusqlite::params![username, project_id, since_ms, limit_plus_one],
            |row| {
                Ok(WorkloadMr {
                    iid: row.get(0)?,
                    ref_: row.get(1)?,
                    title: row.get(2)?,
                    draft: row.get::<_, i32>(3)? != 0,
                    project_path: row.get(4)?,
                    author_username: row.get(5)?,
                    updated_at: row.get(6)?,
                })
            },
        )?
        .collect::<std::result::Result<Vec<_>, _>>()?;

    // Query 4: Unresolved discussions where user participated
    let state_filter = if include_closed {
        ""
    } else {
        " AND (i.id IS NULL OR i.state = 'opened')
           AND (m.id IS NULL OR m.state = 'opened')"
    };
    let disc_sql = format!(
        "SELECT d.noteable_type,
                COALESCE(i.iid, m.iid) AS entity_iid,
                (p.path_with_namespace ||
                 CASE WHEN d.noteable_type = 'MergeRequest' THEN '!' ELSE '#' END ||
                 COALESCE(i.iid, m.iid)) AS ref,
                COALESCE(i.title, m.title) AS entity_title,
                p.path_with_namespace,
                d.last_note_at
         FROM discussions d
         JOIN projects p ON d.project_id = p.id
         LEFT JOIN issues i ON d.issue_id = i.id
         LEFT JOIN merge_requests m ON d.merge_request_id = m.id
         WHERE d.resolvable = 1 AND d.resolved = 0
           AND EXISTS (
             SELECT 1 FROM notes n
             WHERE n.discussion_id = d.id
             AND n.author_username = ?1
             AND n.is_system = 0
           )
           AND (?2 IS NULL OR d.project_id = ?2)
           AND (?3 IS NULL OR d.last_note_at >= ?3)
           {state_filter}
         ORDER BY d.last_note_at DESC
         LIMIT ?4"
    );

    let mut stmt = conn.prepare_cached(&disc_sql)?;
    let unresolved_discussions: Vec<WorkloadDiscussion> = stmt
        .query_map(
            rusqlite::params![username, project_id, since_ms, limit_plus_one],
            |row| {
                let noteable_type: String = row.get(0)?;
                let entity_type = if noteable_type == "MergeRequest" {
                    "MR"
                } else {
                    "Issue"
                };
                Ok(WorkloadDiscussion {
                    entity_type: entity_type.to_string(),
                    entity_iid: row.get(1)?,
                    ref_: row.get(2)?,
                    entity_title: row.get(3)?,
                    project_path: row.get(4)?,
                    last_note_at: row.get(5)?,
                })
            },
        )?
        .collect::<std::result::Result<Vec<_>, _>>()?;

    // Truncation detection
    let assigned_issues_truncated = assigned_issues.len() > limit;
    let authored_mrs_truncated = authored_mrs.len() > limit;
    let reviewing_mrs_truncated = reviewing_mrs.len() > limit;
    let unresolved_discussions_truncated = unresolved_discussions.len() > limit;

    let assigned_issues: Vec<WorkloadIssue> = assigned_issues.into_iter().take(limit).collect();
    let authored_mrs: Vec<WorkloadMr> = authored_mrs.into_iter().take(limit).collect();
    let reviewing_mrs: Vec<WorkloadMr> = reviewing_mrs.into_iter().take(limit).collect();
    let unresolved_discussions: Vec<WorkloadDiscussion> =
        unresolved_discussions.into_iter().take(limit).collect();

    Ok(WorkloadResult {
        username: username.to_string(),
        assigned_issues,
        authored_mrs,
        reviewing_mrs,
        unresolved_discussions,
        assigned_issues_truncated,
        authored_mrs_truncated,
        reviewing_mrs_truncated,
        unresolved_discussions_truncated,
    })
}

// ─── Human Renderer: Workload ───────────────────────────────────────────────

pub(super) fn print_workload_human(r: &WorkloadResult) {
    println!();
    println!(
        "{}",
        Theme::bold().render(&format!(
            "{} {} -- Workload Summary",
            Icons::user(),
            r.username
        ))
    );
    println!("{}", "\u{2500}".repeat(60));

    if !r.assigned_issues.is_empty() {
        println!(
            "{}",
            render::section_divider(&format!("Assigned Issues ({})", r.assigned_issues.len()))
        );
        for item in &r.assigned_issues {
            println!(
                "    {} {}  {}",
                Theme::info().render(&item.ref_),
                render::truncate(&item.title, render::flex_width(25, 20)),
                Theme::dim().render(&render::format_relative_time(item.updated_at)),
            );
        }
        if r.assigned_issues_truncated {
            println!(
                "    {}",
                Theme::dim().render("(truncated; rerun with a higher --limit)")
            );
        }
    }

    if !r.authored_mrs.is_empty() {
        println!(
            "{}",
            render::section_divider(&format!("Authored MRs ({})", r.authored_mrs.len()))
        );
        for mr in &r.authored_mrs {
            let draft = if mr.draft { " [draft]" } else { "" };
            println!(
                "    {} {}{}  {}",
                Theme::info().render(&mr.ref_),
                render::truncate(&mr.title, render::flex_width(33, 20)),
                Theme::dim().render(draft),
                Theme::dim().render(&render::format_relative_time(mr.updated_at)),
            );
        }
        if r.authored_mrs_truncated {
            println!(
                "    {}",
                Theme::dim().render("(truncated; rerun with a higher --limit)")
            );
        }
    }

    if !r.reviewing_mrs.is_empty() {
        println!(
            "{}",
            render::section_divider(&format!("Reviewing MRs ({})", r.reviewing_mrs.len()))
        );
        for mr in &r.reviewing_mrs {
            let author = mr
                .author_username
                .as_deref()
                .map(|a| format!(" by @{a}"))
                .unwrap_or_default();
            println!(
                "    {} {}{}  {}",
                Theme::info().render(&mr.ref_),
                render::truncate(&mr.title, render::flex_width(40, 20)),
                Theme::dim().render(&author),
                Theme::dim().render(&render::format_relative_time(mr.updated_at)),
            );
        }
        if r.reviewing_mrs_truncated {
            println!(
                "    {}",
                Theme::dim().render("(truncated; rerun with a higher --limit)")
            );
        }
    }

    if !r.unresolved_discussions.is_empty() {
        println!(
            "{}",
            render::section_divider(&format!(
                "Unresolved Discussions ({})",
                r.unresolved_discussions.len()
            ))
        );
        for disc in &r.unresolved_discussions {
            println!(
                "    {} {} {}  {}",
                Theme::dim().render(&disc.entity_type),
                Theme::info().render(&disc.ref_),
                render::truncate(&disc.entity_title, render::flex_width(30, 20)),
                Theme::dim().render(&render::format_relative_time(disc.last_note_at)),
            );
        }
        if r.unresolved_discussions_truncated {
            println!(
                "    {}",
                Theme::dim().render("(truncated; rerun with a higher --limit)")
            );
        }
    }

    if r.assigned_issues.is_empty()
        && r.authored_mrs.is_empty()
        && r.reviewing_mrs.is_empty()
        && r.unresolved_discussions.is_empty()
    {
        println!();
        println!(
            "  {}",
            Theme::dim().render("No open work items found for this user.")
        );
    }

    println!();
}

// ─── JSON Renderer: Workload ────────────────────────────────────────────────

pub(super) fn workload_to_json(r: &WorkloadResult) -> serde_json::Value {
    serde_json::json!({
        "username": r.username,
        "assigned_issues": r.assigned_issues.iter().map(|i| serde_json::json!({
            "iid": i.iid,
            "ref": i.ref_,
            "title": i.title,
            "project_path": i.project_path,
            "updated_at": ms_to_iso(i.updated_at),
        })).collect::<Vec<_>>(),
        "authored_mrs": r.authored_mrs.iter().map(|m| serde_json::json!({
            "iid": m.iid,
            "ref": m.ref_,
            "title": m.title,
            "draft": m.draft,
            "project_path": m.project_path,
            "updated_at": ms_to_iso(m.updated_at),
        })).collect::<Vec<_>>(),
        "reviewing_mrs": r.reviewing_mrs.iter().map(|m| serde_json::json!({
            "iid": m.iid,
            "ref": m.ref_,
            "title": m.title,
            "draft": m.draft,
            "project_path": m.project_path,
            "author_username": m.author_username,
            "updated_at": ms_to_iso(m.updated_at),
        })).collect::<Vec<_>>(),
        "unresolved_discussions": r.unresolved_discussions.iter().map(|d| serde_json::json!({
            "entity_type": d.entity_type,
            "entity_iid": d.entity_iid,
            "ref": d.ref_,
            "entity_title": d.entity_title,
            "project_path": d.project_path,
            "last_note_at": ms_to_iso(d.last_note_at),
        })).collect::<Vec<_>>(),
        "summary": {
            "assigned_issue_count": r.assigned_issues.len(),
            "authored_mr_count": r.authored_mrs.len(),
            "reviewing_mr_count": r.reviewing_mrs.len(),
            "unresolved_discussion_count": r.unresolved_discussions.len(),
        },
        "truncation": {
            "assigned_issues_truncated": r.assigned_issues_truncated,
            "authored_mrs_truncated": r.authored_mrs_truncated,
            "reviewing_mrs_truncated": r.reviewing_mrs_truncated,
            "unresolved_discussions_truncated": r.unresolved_discussions_truncated,
        }
    })
}
