use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fs;
use std::path::Path;

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BenchmarkEntry {
    pub time_ms: f64,
    pub pdf_size_kb: f64,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BenchmarkBaseline {
    pub version: u32,
    pub environment: BenchmarkEnvironment,
    pub benchmarks: BTreeMap<String, BenchmarkEntry>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BenchmarkEnvironment {
    pub os: String,
    pub rust: String,
    pub profile: String,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ThresholdRule {
    pub pass_pct: f64,
    pub fail_pct: f64,
    pub min_absolute_ms: f64,
}

#[derive(Debug, Clone, PartialEq)]
pub struct BenchmarkComparison {
    pub name: String,
    pub baseline_ms: f64,
    pub current_ms: f64,
    pub change_pct: f64,
    pub baseline_pdf_size_kb: f64,
    pub current_pdf_size_kb: f64,
    pub status: String,
}

pub fn compare_result(
    name: impl Into<String>,
    baseline: &BenchmarkEntry,
    current: &BenchmarkEntry,
    rule: &ThresholdRule,
) -> BenchmarkComparison {
    let baseline_ms = baseline.time_ms;
    let current_ms = current.time_ms;
    let delta_ms = (current_ms - baseline_ms).abs();
    let change_pct = if baseline_ms == 0.0 {
        0.0
    } else {
        (current_ms - baseline_ms) / baseline_ms * 100.0
    };

    let status = if delta_ms < rule.min_absolute_ms || change_pct.abs() < rule.pass_pct {
        "通过"
    } else if change_pct.abs() <= rule.fail_pct {
        "警告"
    } else {
        "失败"
    };

    BenchmarkComparison {
        name: name.into(),
        baseline_ms,
        current_ms,
        change_pct,
        baseline_pdf_size_kb: baseline.pdf_size_kb,
        current_pdf_size_kb: current.pdf_size_kb,
        status: status.to_string(),
    }
}

#[derive(Debug, Deserialize)]
pub struct ThresholdsFile {
    pub default: ThresholdRule,
    pub benchmarks: BTreeMap<String, ThresholdRule>,
}

pub fn format_markdown_report(rows: &[BenchmarkComparison]) -> String {
    let mut out = String::from("| 场景 | 基准值 | 当前值 | 变化 | PDF 大小 | 状态 |\n|---|---:|---:|---:|---:|---|\n");
    for row in rows {
        out.push_str(&format!(
            "| {} | {:.2} ms | {:.2} ms | {:+.1}% | {:.1} KB -> {:.1} KB | {} |\n",
            row.name,
            row.baseline_ms,
            row.current_ms,
            row.change_pct,
            row.baseline_pdf_size_kb,
            row.current_pdf_size_kb,
            row.status
        ));
    }
    out
}

pub fn format_text_report(rows: &[BenchmarkComparison]) -> String {
    let mut out = String::new();
    out.push_str("基准测试对比结果\n");
    out.push_str("================\n\n");
    for row in rows {
        out.push_str(&format!(
            "{}: {:.2} ms -> {:.2} ms ({:+.1}%) [{}]\n",
            row.name, row.baseline_ms, row.current_ms, row.change_pct, row.status
        ));
        out.push_str(&format!(
            "  PDF 大小: {:.1} KB -> {:.1} KB\n",
            row.baseline_pdf_size_kb, row.current_pdf_size_kb
        ));
    }
    let any_fail = rows.iter().any(|r| r.status == "失败");
    let any_warn = rows.iter().any(|r| r.status == "警告");
    out.push('\n');
    if any_fail {
        out.push_str("总评: 失败 - 检测到性能退化\n");
    } else if any_warn {
        out.push_str("总评: 警告 - 检测到性能变化\n");
    } else {
        out.push_str("总评: 通过\n");
    }
    out
}

pub fn load_thresholds(path: &Path) -> std::result::Result<ThresholdsFile, Box<dyn std::error::Error>> {
    let data = fs::read_to_string(path)?;
    let thresholds: ThresholdsFile = serde_json::from_str(&data)?;
    Ok(thresholds)
}

pub fn run_comparison(
    baseline_path: &Path,
    current_path: &Path,
    thresholds_path: &Path,
) -> std::result::Result<Vec<BenchmarkComparison>, Box<dyn std::error::Error>> {
    let baseline_data = fs::read_to_string(baseline_path)?;
    let baseline: BenchmarkBaseline = serde_json::from_str(&baseline_data)?;

    let current_data = fs::read_to_string(current_path)?;
    let current: BTreeMap<String, BenchmarkEntry> = serde_json::from_str(&current_data)?;

    let thresholds = load_thresholds(thresholds_path)?;

    let mut results = Vec::new();
    for (name, current_entry) in &current {
        let baseline_entry = match baseline.benchmarks.get(name) {
            Some(e) => e,
            None => continue,
        };
        let rule = thresholds.benchmarks.get(name)
            .unwrap_or(&thresholds.default);
        let comparison = compare_result(name, baseline_entry, current_entry, rule);
        results.push(comparison);
    }
    results.sort_by(|a, b| a.name.cmp(&b.name));
    Ok(results)
}
