//! 评分引擎 — 基于想法内容的多维度启发式评分
//!
//! 各维度分数均为 0-10（IPC 层会 *10 缩放为 0-100 以匹配前端）。
//! 启发式依据：优先级、描述充实度、标签、关键词信号——保证稳定且有区分度。
//! TODO: 接入 AI 做语义级深度评分。

use crate::capture::Idea;

/// 想法评分详情（重新导出 capture 模块中的定义）
pub use crate::capture::IdeaScores;

use df_types::types::Priority;

/// 评分权重配置
#[derive(Debug, Clone)]
pub struct ScoringWeights {
    pub feasibility: f64,
    pub impact: f64,
    pub urgency: f64,
}

impl Default for ScoringWeights {
    fn default() -> Self {
        Self {
            feasibility: 0.4,
            impact: 0.4,
            urgency: 0.2,
        }
    }
}

/// 评分引擎
pub struct ScoringEngine;

impl ScoringEngine {
    /// 使用默认权重计算评分
    pub fn compute_default(idea: &Idea) -> IdeaScores {
        Self::compute(idea, &ScoringWeights::default())
    }

    /// 使用指定权重计算评分
    pub fn compute(idea: &Idea, weights: &ScoringWeights) -> IdeaScores {
        let feasibility = Self::heuristic_feasibility(idea);
        let impact = Self::heuristic_impact(idea);
        let urgency = Self::heuristic_urgency(idea);

        let overall = feasibility * weights.feasibility
            + impact * weights.impact
            + urgency * weights.urgency;

        IdeaScores {
            feasibility,
            impact,
            urgency,
            overall,
        }
    }

    /// 启发式可行性评分（描述充实度 + 技术/资源信号词）
    fn heuristic_feasibility(idea: &Idea) -> f64 {
        let mut score = 5.0_f64;
        let desc = idea.description.trim();
        if !desc.is_empty() {
            score += 1.5;
        }
        let len = desc.chars().count();
        if (50..=500).contains(&len) {
            score += 1.0;
        } else if len > 500 {
            // 过长描述通常意味着实现复杂度上升
            score -= 0.5;
        }
        // 可行性正向信号
        let pos = count_any(desc, &[
            "复用", "已有", "简单", "集成", "支持", "成熟", "基于", "现成", "脚手架", "模板",
        ]);
        score += (pos as f64) * 0.5;
        // 复杂度负向信号
        let neg = count_any(desc, &[
            "重构", "迁移", "大规模", "分布式", "重写", "从零", "全新架构", "高并发", "底层",
        ]);
        score -= (neg as f64) * 0.6;
        score.clamp(0.0, 10.0)
    }

    /// 启发式影响力评分（优先级 + 价值信号词 + 标签广度）
    fn heuristic_impact(idea: &Idea) -> f64 {
        let mut score = match idea.priority {
            Priority::Critical => 8.0,
            Priority::High => 6.5,
            Priority::Medium => 5.0,
            Priority::Low => 3.5,
        };
        if !idea.tags.is_empty() {
            score += 0.5;
            // 标签越多影响面越广，上限 +1.0
            score += (idea.tags.len().min(4) as f64) * 0.25;
        }
        let desc = idea.description.trim();
        let value_hits = count_any(desc, &[
            "用户", "增长", "收入", "效率", "体验", "核心", "关键", "痛点", "竞品", "留存",
        ]);
        score += (value_hits as f64) * 0.5;
        if desc.chars().count() > 100 {
            score += 0.5;
        }
        score.clamp(0.0, 10.0)
    }

    /// 启发式紧急度评分（优先级 + 时效信号词）
    fn heuristic_urgency(idea: &Idea) -> f64 {
        let mut score = match idea.priority {
            Priority::Critical => 9.0,
            Priority::High => 7.0,
            Priority::Medium => 5.0,
            Priority::Low => 3.0,
        };
        let desc = idea.description.trim();
        let time_hits = count_any(desc, &[
            "立即", "马上", "紧急", "尽快", "本周", "上线", "deadline", "截止", "先行", "阻塞",
        ]);
        score += (time_hits as f64) * 0.5;
        score.clamp(0.0, 10.0)
    }
}

/// 统计 text 中命中任一关键词的数量（小写匹配，兼顾中英文）
/// 局限:纯子串匹配,不识别"不复用""无用户增长"等否定前缀,接 LLM 后由语义层修正
fn count_any(text: &str, keywords: &[&str]) -> usize {
    let lower = text.to_lowercase();
    keywords.iter().filter(|kw| lower.contains(*kw)).count()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::capture::Idea;
    use df_types::types::{IdeaStatus, Priority};

    /// 辅助工厂：构造测试用 Idea（时间/ID 用默认值，不影响评分）
    fn make_idea(title: &str, desc: &str, priority: Priority, tags: Vec<&str>) -> Idea {
        Idea {
            id: "test-id".to_string(),
            title: title.to_string(),
            description: desc.to_string(),
            status: IdeaStatus::Draft,
            priority,
            scores: None,
            tags: tags.into_iter().map(String::from).collect(),
            source: None,
            related_ids: Vec::new(),
            created_at: chrono::Utc::now(),
            updated_at: chrono::Utc::now(),
        }
    }

    #[test]
    fn s1_empty_idea_baseline() {
        let idea = make_idea("测试想法", "", Priority::Medium, vec![]);
        let s = ScoringEngine::compute_default(&idea);
        println!("\n[s1] 空想法 (Medium / 无描述 / 无标签)");
        println!("  可行性={:.2}  影响力={:.2}  紧急度={:.2}  综合={:.2}", s.feasibility, s.impact, s.urgency, s.overall);
        assert!((s.overall - 5.0).abs() < 0.01, "空想法 overall 应为 5.0, 实际 {:.2}", s.overall);
    }

    #[test]
    fn s2_high_priority_value_desc() {
        let desc = "面向用户的核心功能，带来显著增长，大幅提升效率。集成成熟方案，复用已有组件，快速交付价值。".repeat(3);
        let idea = make_idea("增长引擎", &desc, Priority::Critical, vec!["增长", "核心"]);
        let s = ScoringEngine::compute_default(&idea);
        println!("\n[s2] 高优先级 + 价值描述 (Critical / ~120字 / 含价值词)");
        println!("  可行性={:.2}  影响力={:.2}  紧急度={:.2}  综合={:.2}", s.feasibility, s.impact, s.urgency, s.overall);
        assert!(s.impact >= 7.0, "impact 应≥7.0, 实际 {:.2}", s.impact);
        assert!(s.urgency >= 8.0, "urgency 应≥8.0, 实际 {:.2}", s.urgency);
    }

    #[test]
    fn s3_low_priority_short_desc() {
        let idea = make_idea("小优化", "一句话", Priority::Low, vec![]);
        let s = ScoringEngine::compute_default(&idea);
        println!("\n[s3] 低优先级 + 短描述 (Low / 3字)");
        println!("  可行性={:.2}  影响力={:.2}  紧急度={:.2}  综合={:.2}", s.feasibility, s.impact, s.urgency, s.overall);
        assert!((s.overall - 4.6).abs() < 0.01, "overall 应为 4.6, 实际 {:.2}", s.overall);
    }

    #[test]
    fn s4_feasibility_positive_signals() {
        let idea = make_idea("复用方案", "复用已有组件，简单集成现成脚手架", Priority::Medium, vec![]);
        let s = ScoringEngine::compute_default(&idea);
        println!("\n[s4] 可行性正向信号 (含 复用/已有/简单/集成/现成/脚手架)");
        println!("  可行性={:.2}  (预期 9.5)", s.feasibility);
        assert!((s.feasibility - 9.5).abs() < 0.01, "正向信号 feasibility 应为 9.5, 实际 {:.2}", s.feasibility);
    }

    #[test]
    fn s5_feasibility_negative_signals() {
        let idea = make_idea("大重构", "大规模重构迁移，分布式重写从零开始", Priority::Medium, vec![]);
        let s = ScoringEngine::compute_default(&idea);
        println!("\n[s5] 可行性负向信号 (含 大规模/重构/迁移/分布式/重写/从零)");
        println!("  可行性={:.2}  (预期 ≤5.0)", s.feasibility);
        assert!(s.feasibility <= 5.0, "负向信号 feasibility 应≤5.0, 实际 {:.2}", s.feasibility);
    }

    #[test]
    fn s6_custom_weights() {
        let idea = make_idea("高可行低影响", "复用已有简单集成现成", Priority::Low, vec![]);
        let custom = ScoringWeights { feasibility: 0.7, impact: 0.2, urgency: 0.1 };
        let s_custom = ScoringEngine::compute(&idea, &custom);
        let s_default = ScoringEngine::compute_default(&idea);
        let manual = s_custom.feasibility * 0.7 + s_custom.impact * 0.2 + s_custom.urgency * 0.1;
        println!("\n[s6] 自定义权重 (feas:0.7 / impact:0.2 / urg:0.1)");
        println!("  自定义综合={:.2}  默认综合={:.2}  手算加权={:.2}", s_custom.overall, s_default.overall, manual);
        assert!((s_custom.overall - manual).abs() < 0.01, "overall 应等于手算加权");
        assert!(s_custom.overall > s_default.overall, "高 feas 配高权重应让综合更高");
    }

    #[test]
    fn s7_clamp_upper_bound() {
        let desc = "复用已有简单集成现成成熟基于脚手架模板支持".repeat(20);
        let idea = make_idea("满分想法", &desc, Priority::Critical, vec!["a", "b", "c", "d"]);
        let s = ScoringEngine::compute_default(&idea);
        println!("\n[s7] clamp 上限 (堆正向词 + 超长描述 + Critical)");
        println!("  可行性={:.2}  影响力={:.2}  紧急度={:.2}  综合={:.2}", s.feasibility, s.impact, s.urgency, s.overall);
        assert!(s.feasibility <= 10.0 && s.impact <= 10.0 && s.urgency <= 10.0, "所有维度应≤10");
    }

    #[test]
    fn s8_clamp_lower_bound() {
        let desc = "重构迁移大规模分布式重写从零全新架构高并发底层".repeat(20);
        let idea = make_idea("灾难想法", &desc, Priority::Low, vec![]);
        let s = ScoringEngine::compute_default(&idea);
        println!("\n[s8] clamp 下限 (堆负向词 + Low)");
        println!("  可行性={:.2}  影响力={:.2}  紧急度={:.2}  综合={:.2}", s.feasibility, s.impact, s.urgency, s.overall);
        assert!(s.feasibility >= 0.0 && s.impact >= 0.0 && s.urgency >= 0.0, "所有维度应≥0");
    }
}
