use std::path::PathBuf;

use clap::Args;
use serde::Serialize;

#[derive(Args)]
pub struct ClassifyArgs {
    /// Path to the git repository
    #[arg(long, default_value = ".")]
    pub repo: PathBuf,

    /// Base ref (commit, branch, or tag)
    #[arg(long)]
    pub base: String,

    /// Head ref (commit, branch, or tag)
    #[arg(long)]
    pub head: String,

    /// Output format
    #[arg(long, default_value = "summary")]
    pub format: OutputFormat,
}

#[derive(Clone, Debug, clap::ValueEnum)]
pub enum OutputFormat {
    Summary,
    Json,
    SummaryJson,
    Sarif,
}

// ── Summary-json output types ───────────────────────────────────────────────

#[derive(Serialize)]
pub struct SummaryOutput {
    pub base_ref: String,
    pub head_ref: String,
    pub stats: SummaryPriorityCounts,
    pub focus: Vec<FocusEntry>,
}

#[derive(Serialize)]
pub struct SummaryPriorityCounts {
    pub high: usize,
    pub moderate: usize,
    pub low: usize,
    pub filtered: usize,
}

#[derive(Serialize)]
pub struct FocusEntry {
    pub file: String,
    pub lines: [u32; 2],
    pub category: String,
    pub priority: String,
    pub rule: String,
    #[serde(rename = "in", skip_serializing_if = "Option::is_none")]
    pub enclosing: Option<String>,
}

pub fn run(args: ClassifyArgs) -> miette::Result<()> {
    let result = diffsift_core::classify_diff(&args.repo, &args.base, &args.head)?;

    match args.format {
        OutputFormat::Json => {
            let json = serde_json::to_string_pretty(&result)
                .map_err(|e| miette::miette!("JSON serialization failed: {e}"))?;
            println!("{json}");
        }
        OutputFormat::Summary => {
            print_summary(&result);
        }
        OutputFormat::SummaryJson => {
            let output = build_summary_output(&result);
            let json = serde_json::to_string_pretty(&output)
                .map_err(|e| miette::miette!("JSON serialization failed: {e}"))?;
            println!("{json}");
        }
        OutputFormat::Sarif => {
            let sarif = crate::sarif::build_sarif_output(&result);
            let json = serde_json::to_string_pretty(&sarif)
                .map_err(|e| miette::miette!("SARIF serialization failed: {e}"))?;
            println!("{json}");
        }
    }

    Ok(())
}

fn build_summary_output(result: &diffsift_core::ClassifiedDiff) -> SummaryOutput {
    let focus: Vec<FocusEntry> = result
        .files
        .iter()
        .flat_map(|f| {
            f.hunks
                .iter()
                .filter(|h| h.priority >= diffsift_core::Priority::Moderate)
                .map(move |h| {
                    let rule = h
                        .patterns
                        .first()
                        .map(|p| format!("{}: {}", p.pattern_id, p.pattern_name))
                        .unwrap_or_else(|| "unknown".to_string());
                    FocusEntry {
                        file: f.path.clone(),
                        lines: [h.start_line, h.end_line],
                        category: format!("{:?}", h.category),
                        priority: format!("{:?}", h.priority),
                        rule,
                        enclosing: h
                            .enclosing_symbol
                            .clone()
                            .or_else(|| h.enclosing_node.clone()),
                    }
                })
        })
        .collect();

    SummaryOutput {
        base_ref: result.base_ref.clone(),
        head_ref: result.head_ref.clone(),
        stats: SummaryPriorityCounts {
            high: result.summary.priority_counts.high,
            moderate: result.summary.priority_counts.moderate,
            low: result.summary.priority_counts.low,
            filtered: result.summary.priority_counts.filtered,
        },
        focus,
    }
}

fn print_summary(result: &diffsift_core::ClassifiedDiff) {
    println!(
        "Analyzed {} hunks across {} files in {}ms\n",
        result.summary.total_hunks, result.summary.total_files, result.classification_time_ms
    );

    println!("Priority  | Hunks");
    println!("----------|------");
    println!("HIGH      | {}", result.summary.priority_counts.high);
    println!("MODERATE  | {}", result.summary.priority_counts.moderate);
    println!("LOW       | {}", result.summary.priority_counts.low);
    println!("FILTERED  | {}", result.summary.priority_counts.filtered);
    println!();

    if !result.summary.category_counts.is_empty() {
        println!("Category            | Hunks");
        println!("--------------------|------");
        for cc in &result.summary.category_counts {
            println!("{:<20}| {}", format!("{:?}", cc.category), cc.count);
        }
        println!();
    }

    // Show high-priority focus areas
    let high_hunks: Vec<_> = result
        .files
        .iter()
        .flat_map(|f| {
            f.hunks
                .iter()
                .filter(|h| h.priority == diffsift_core::Priority::High)
                .map(move |h| (&f.path, h))
        })
        .collect();

    if !high_hunks.is_empty() {
        println!("Focus areas:");
        for (path, hunk) in &high_hunks {
            let pattern_desc = hunk
                .patterns
                .first()
                .map(|p| p.pattern_name.as_str())
                .unwrap_or("unknown");
            let symbol = hunk.enclosing_node.as_deref().unwrap_or("top-level");
            println!(
                "  {}:{}-{} — {:?} in {} ({})",
                path, hunk.start_line, hunk.end_line, hunk.category, symbol, pattern_desc
            );
        }
    }
}
