use std::collections::HashMap;

use rusqlite::Connection;

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

use super::types::*;

// ─── Query: Reviews Mode ────────────────────────────────────────────────────

pub(super) fn query_reviews(
    conn: &Connection,
    username: &str,
    project_id: Option<i64>,
    since_ms: i64,
) -> Result<ReviewsResult> {
    // Force the partial index on DiffNote queries (same rationale as expert mode).
    // COUNT + COUNT(DISTINCT) + category extraction all benefit from 26K DiffNote
    // scan vs 282K notes full scan: measured 25x speedup.
    let total_sql = "SELECT COUNT(*) FROM notes n
         INDEXED BY idx_notes_diffnote_path_created
         JOIN discussions d ON n.discussion_id = d.id
         JOIN merge_requests m ON d.merge_request_id = m.id
         WHERE n.author_username = ?1
           AND n.note_type = 'DiffNote'
           AND n.is_system = 0
           AND (m.author_username IS NULL OR m.author_username != ?1)
           AND n.created_at >= ?2
           AND (?3 IS NULL OR n.project_id = ?3)";

    let total_diffnotes: u32 = conn.query_row(
        total_sql,
        rusqlite::params![username, since_ms, project_id],
        |row| row.get(0),
    )?;

    // Count distinct MRs reviewed
    let mrs_sql = "SELECT COUNT(DISTINCT m.id) FROM notes n
         INDEXED BY idx_notes_diffnote_path_created
         JOIN discussions d ON n.discussion_id = d.id
         JOIN merge_requests m ON d.merge_request_id = m.id
         WHERE n.author_username = ?1
           AND n.note_type = 'DiffNote'
           AND n.is_system = 0
           AND (m.author_username IS NULL OR m.author_username != ?1)
           AND n.created_at >= ?2
           AND (?3 IS NULL OR n.project_id = ?3)";

    let mrs_reviewed: u32 = conn.query_row(
        mrs_sql,
        rusqlite::params![username, since_ms, project_id],
        |row| row.get(0),
    )?;

    // Extract prefixed categories: body starts with **prefix**
    let cat_sql = "SELECT
            SUBSTR(ltrim(n.body), 3, INSTR(SUBSTR(ltrim(n.body), 3), '**') - 1) AS raw_prefix,
            COUNT(*) AS cnt
         FROM notes n INDEXED BY idx_notes_diffnote_path_created
         JOIN discussions d ON n.discussion_id = d.id
         JOIN merge_requests m ON d.merge_request_id = m.id
         WHERE n.author_username = ?1
           AND n.note_type = 'DiffNote'
           AND n.is_system = 0
           AND (m.author_username IS NULL OR m.author_username != ?1)
           AND ltrim(n.body) LIKE '**%**%'
           AND n.created_at >= ?2
           AND (?3 IS NULL OR n.project_id = ?3)
         GROUP BY raw_prefix
         ORDER BY cnt DESC";

    let mut stmt = conn.prepare_cached(cat_sql)?;
    let raw_categories: Vec<(String, u32)> = stmt
        .query_map(rusqlite::params![username, since_ms, project_id], |row| {
            Ok((row.get::<_, String>(0)?, row.get(1)?))
        })?
        .collect::<std::result::Result<Vec<_>, _>>()?;

    // Normalize categories: lowercase, strip trailing colon/space,
    // merge nit/nitpick variants, merge (non-blocking) variants
    let mut merged: HashMap<String, u32> = HashMap::new();
    for (raw, count) in &raw_categories {
        let normalized = normalize_review_prefix(raw);
        if !normalized.is_empty() {
            *merged.entry(normalized).or_insert(0) += count;
        }
    }

    let categorized_count: u32 = merged.values().sum();

    let mut categories: Vec<ReviewCategory> = merged
        .into_iter()
        .map(|(name, count)| {
            let percentage = if categorized_count > 0 {
                f64::from(count) / f64::from(categorized_count) * 100.0
            } else {
                0.0
            };
            ReviewCategory {
                name,
                count,
                percentage,
            }
        })
        .collect();

    categories.sort_by_key(|b| std::cmp::Reverse(b.count));

    Ok(ReviewsResult {
        username: username.to_string(),
        total_diffnotes,
        categorized_count,
        mrs_reviewed,
        categories,
    })
}

/// Normalize a raw review prefix like "Suggestion (non-blocking):" into "suggestion".
pub(super) fn normalize_review_prefix(raw: &str) -> String {
    let s = raw.trim().trim_end_matches(':').trim().to_lowercase();

    // Strip "(non-blocking)" and similar parentheticals
    let s = if let Some(idx) = s.find('(') {
        s[..idx].trim().to_string()
    } else {
        s
    };

    // Merge nit/nitpick variants
    match s.as_str() {
        "nitpick" | "nit" => "nit".to_string(),
        other => other.to_string(),
    }
}

// ─── Human Renderer ─────────────────────────────────────────────────────────

pub(super) fn print_reviews_human(r: &ReviewsResult) {
    println!();
    println!(
        "{}",
        Theme::bold().render(&format!(
            "{} {} -- Review Patterns",
            Icons::user(),
            r.username
        ))
    );
    println!("{}", "\u{2500}".repeat(60));
    println!();

    if r.total_diffnotes == 0 {
        println!(
            "  {}",
            Theme::dim().render("No review comments found for this user.")
        );
        println!();
        return;
    }

    println!(
        "  {} DiffNotes across {} MRs ({} categorized)",
        Theme::bold().render(&r.total_diffnotes.to_string()),
        Theme::bold().render(&r.mrs_reviewed.to_string()),
        Theme::bold().render(&r.categorized_count.to_string()),
    );
    println!();

    if !r.categories.is_empty() {
        println!(
            "  {:<16} {:>6} {:>6}",
            Theme::bold().render("Category"),
            Theme::bold().render("Count"),
            Theme::bold().render("%"),
        );

        for cat in &r.categories {
            println!(
                "  {:<16} {:>6} {:>5.1}%",
                Theme::info().render(&cat.name),
                cat.count,
                cat.percentage,
            );
        }
    }

    let uncategorized = r.total_diffnotes - r.categorized_count;
    if uncategorized > 0 {
        println!();
        println!(
            "  {} {} uncategorized (no **prefix** convention)",
            Theme::dim().render("Note:"),
            uncategorized,
        );
    }

    println!();
}

// ─── Robot Renderer ─────────────────────────────────────────────────────────

pub(super) fn reviews_to_json(r: &ReviewsResult) -> serde_json::Value {
    serde_json::json!({
        "username": r.username,
        "total_diffnotes": r.total_diffnotes,
        "categorized_count": r.categorized_count,
        "mrs_reviewed": r.mrs_reviewed,
        "categories": r.categories.iter().map(|c| serde_json::json!({
            "name": c.name,
            "count": c.count,
            "percentage": (c.percentage * 10.0).round() / 10.0,
        })).collect::<Vec<_>>(),
    })
}
