//! 人设系统(P0) — AgentPersona 结构体 + PersonaRegistry 注册表
//!
//! 构成 AI Native 的基础:角色划分是分工的前提。
//! - 每名人设包含:能力标签、工具白名单、system prompt 模板
//! - Registry 提供按场景/意图选人设、按 id 查人设能力
//!
//! 设计依据:docs/02-架构设计/构想审查/AI-Native方向与路线图-2026-06-29.md

use std::collections::HashMap;

/// 内置人设 ID 常量
pub const PERSONA_CODER: &str = "coder";
pub const PERSONA_REVIEWER: &str = "reviewer";
pub const PERSONA_ARCHITECT: &str = "architect";
pub const PERSONA_TESTER: &str = "tester";
pub const PERSONA_ANALYST: &str = "analyst";

/// 人设能力标签枚举
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PersonaCapability {
    /// 代码生成与修改
    CodeGeneration,
    /// Code Review
    CodeReview,
    /// 架构设计与分析
    ArchitectureDesign,
    /// 测试编写与执行
    Testing,
    /// 数据分析与搜索
    Analysis,
    /// 文件读写
    FileOperation,
    /// 命令执行
    CommandExecution,
    /// Git 操作
    GitOperation,
    /// 知识库操作
    KnowledgeOperation,
    /// 项目管理
    ProjectManagement,
}

impl PersonaCapability {
    pub fn label(&self) -> &'static str {
        match self {
            Self::CodeGeneration => "代码生成",
            Self::CodeReview => "代码审查",
            Self::ArchitectureDesign => "架构设计",
            Self::Testing => "测试",
            Self::Analysis => "分析",
            Self::FileOperation => "文件操作",
            Self::CommandExecution => "命令执行",
            Self::GitOperation => "Git 操作",
            Self::KnowledgeOperation => "知识库",
            Self::ProjectManagement => "项目管理",
        }
    }
}

/// 人设定义
#[derive(Debug, Clone)]
pub struct AgentPersona {
    /// 唯一标识(如 "coder", "reviewer")
    pub id: String,
    /// 人设名称(如 "程序员", "审查员")
    pub name: String,
    /// 人设描述(LLM 理解用)
    pub description: String,
    /// 能力标签集
    pub capabilities: Vec<PersonaCapability>,
    /// 工具白名单(空=不限,非空=仅允许列出的工具)
    pub tool_whitelist: Vec<String>,
    /// system prompt 模板(注入 {context} {goals} 等占位符)
    pub system_prompt_template: String,
    /// 优先级(选人设时冲突排名,0=最高)
    pub priority: u8,
}

impl AgentPersona {
    /// 检查此人设是否具备某项能力
    pub fn has_capability(&self, cap: &PersonaCapability) -> bool {
        self.capabilities.contains(cap)
    }

    /// 检查某工具是否在此人设的 whitelist 中
    pub fn is_tool_allowed(&self, tool_name: &str) -> bool {
        self.tool_whitelist.is_empty() || self.tool_whitelist.iter().any(|t| t == tool_name)
    }

    /// 构建带上下文的 system prompt
    pub fn build_prompt(&self, context: &str, goals: &[String]) -> String {
        let goals_text = if goals.is_empty() {
            String::new()
        } else {
            format!("\n## 当前目标\n{}", goals.iter().enumerate().map(|(i, g)| format!("{}. {}", i + 1, g)).collect::<Vec<_>>().join("\n"))
        };
        self.system_prompt_template
            .replace("{context}", context)
            .replace("{goals}", &goals_text)
    }
}

/// 人设注册表 — 管理内置 + 自定义人设
#[derive(Debug, Clone)]
pub struct PersonaRegistry {
    personae: HashMap<String, AgentPersona>,
    /// 默认人设 id(兜底)
    default_id: String,
}

impl PersonaRegistry {
    /// 构造默认注册表(含 5 内置人设)
    pub fn new() -> Self {
        let mut reg = Self {
            personae: HashMap::new(),
            default_id: PERSONA_CODER.to_string(),
        };
        reg.register(Self::builtin_coder());
        reg.register(Self::builtin_reviewer());
        reg.register(Self::builtin_architect());
        reg.register(Self::builtin_tester());
        reg.register(Self::builtin_analyst());
        reg
    }

    // ── 5 内置人设 ──

    fn builtin_coder() -> AgentPersona {
        AgentPersona {
            id: PERSONA_CODER.to_string(),
            name: "程序员".to_string(),
            description: "负责代码生成、修改、重构。专注于实现功能、修复 bug、优化性能，不关注大范围架构变更".to_string(),
            capabilities: vec![
                PersonaCapability::CodeGeneration,
                PersonaCapability::FileOperation,
                PersonaCapability::CommandExecution,
            ],
            tool_whitelist: vec![], // 不限(全部工具可用)
            system_prompt_template: "你是 DevFlow 的**程序员**(Coder)。\n\n你的职责是编写、修改和重构代码。专注于具体实现，不擅自改变整体架构。\n\n{goals}\n\n{context}".to_string(),
            priority: 1,
        }
    }

    fn builtin_reviewer() -> AgentPersona {
        AgentPersona {
            id: PERSONA_REVIEWER.to_string(),
            name: "审查员".to_string(),
            description: "负责 Code Review、质量审计、安全检查。审查代码正确性、性能、安全性，返回审查意见不直接改代码".to_string(),
            capabilities: vec![
                PersonaCapability::CodeReview,
                PersonaCapability::Analysis,
            ],
            tool_whitelist: vec![
                "read_file".into(), "search_files".into(), "list_directory".into(),
                "git_diff".into(), "git_log".into(), "git_status".into(),
            ],
            system_prompt_template: "你是 DevFlow 的**审查员**(Reviewer)。\n\n你的职责是审查代码质量、安全性和性能。**你不直接修改代码**，只输出审查意见和修改建议。\n\n{goals}\n\n{context}".to_string(),
            priority: 3,
        }
    }

    fn builtin_architect() -> AgentPersona {
        AgentPersona {
            id: PERSONA_ARCHITECT.to_string(),
            name: "架构师".to_string(),
            description: "负责架构设计、技术选型、模块划分。分析整体结构，制定技术方案，不关注具体代码实现".to_string(),
            capabilities: vec![
                PersonaCapability::ArchitectureDesign,
                PersonaCapability::Analysis,
            ],
            tool_whitelist: vec![
                "read_file".into(), "search_files".into(), "list_directory".into(),
                "write_file".into(), // 写架构文档
            ],
            system_prompt_template: "你是 DevFlow 的**架构师**(Architect)。\n\n你的职责是架构设计、技术选型和模块划分。分析系统整体结构，制定技术方案。**不编写业务代码**，输出架构文档和设计方案。\n\n{goals}\n\n{context}".to_string(),
            priority: 2,
        }
    }

    fn builtin_tester() -> AgentPersona {
        AgentPersona {
            id: PERSONA_TESTER.to_string(),
            name: "测试员".to_string(),
            description: "负责编写单元测试、集成测试、执行测试。验证代码正确性，不修改生产代码".to_string(),
            capabilities: vec![
                PersonaCapability::Testing,
                PersonaCapability::CommandExecution,
                PersonaCapability::FileOperation,
            ],
            tool_whitelist: vec![], // 不限
            system_prompt_template: "你是 DevFlow 的**测试员**(Tester)。\n\n你的职责是编写测试、执行测试、报告测试结果。**不修改生产代码**，只修改测试代码。\n\n{goals}\n\n{context}".to_string(),
            priority: 3,
        }
    }

    fn builtin_analyst() -> AgentPersona {
        AgentPersona {
            id: PERSONA_ANALYST.to_string(),
            name: "分析师".to_string(),
            description: "负责数据分析、日志排查、性能分析。搜索信息、分析数据，输出分析报告".to_string(),
            capabilities: vec![
                PersonaCapability::Analysis,
                PersonaCapability::CommandExecution,
                PersonaCapability::KnowledgeOperation,
            ],
            tool_whitelist: vec![
                "read_file".into(), "search_files".into(), "grep_search".into(),
                "run_command".into(), "git_log".into(),
                "knowledge_search".into(), "knowledge_inject".into(),
            ],
            system_prompt_template: "你是 DevFlow 的**分析师**(Analyst)。\n\n你的职责是分析数据、排查问题、搜索信息。输出分析报告和结论。**不修改代码**。\n\n{goals}\n\n{context}".to_string(),
            priority: 4,
        }
    }

    // ── 注册表操作 ──

    /// 注册/覆盖人设
    pub fn register(&mut self, persona: AgentPersona) {
        self.personae.insert(persona.id.clone(), persona);
    }

    /// 按 id 查人设
    pub fn get(&self, id: &str) -> Option<&AgentPersona> {
        self.personae.get(id)
    }

    /// 取默认人设
    pub fn default(&self) -> &AgentPersona {
        self.personae.get(&self.default_id).expect("默认人设必存在")
    }

    /// 设置默认人设 id
    pub fn set_default(&mut self, id: &str) {
        if self.personae.contains_key(id) {
            self.default_id = id.to_string();
        }
    }

    /// 按意图推荐人设(启发式规则)
    pub fn recommend_for_intent(&self, intent: &str) -> &AgentPersona {
        let lower = intent.to_lowercase();
        if lower.contains("review") || lower.contains("审查") || lower.contains("审计") {
            self.get(PERSONA_REVIEWER).unwrap_or_else(|| self.default())
        } else if lower.contains("架构") || lower.contains("设计") || lower.contains("architect") {
            self.get(PERSONA_ARCHITECT).unwrap_or_else(|| self.default())
        } else if lower.contains("测试") || lower.contains("test") {
            self.get(PERSONA_TESTER).unwrap_or_else(|| self.default())
        } else if lower.contains("分析") || lower.contains("分析") || lower.contains("排查") || lower.contains("search") {
            self.get(PERSONA_ANALYST).unwrap_or_else(|| self.default())
        } else {
            // 默认走 coder(代码生成是最常见场景)
            self.default()
        }
    }

    /// 获取全部人设列表
    pub fn all(&self) -> Vec<&AgentPersona> {
        let mut list: Vec<_> = self.personae.values().collect();
        list.sort_by_key(|p| p.priority);
        list
    }

    /// 人设数量
    pub fn len(&self) -> usize {
        self.personae.len()
    }

    pub fn is_empty(&self) -> bool {
        self.personae.is_empty()
    }
}

impl Default for PersonaRegistry {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn registry_has_5_builtin() {
        let reg = PersonaRegistry::new();
        assert_eq!(reg.len(), 5);
        assert!(reg.get(PERSONA_CODER).is_some());
        assert!(reg.get(PERSONA_REVIEWER).is_some());
        assert!(reg.get(PERSONA_ARCHITECT).is_some());
        assert!(reg.get(PERSONA_TESTER).is_some());
        assert!(reg.get(PERSONA_ANALYST).is_some());
    }

    #[test]
    fn default_is_coder() {
        let reg = PersonaRegistry::new();
        assert_eq!(reg.default().id, PERSONA_CODER);
    }

    #[test]
    fn recommend_reviewer() {
        let reg = PersonaRegistry::new();
        let p = reg.recommend_for_intent("review the code changes");
        assert_eq!(p.id, PERSONA_REVIEWER);
    }

    #[test]
    fn recommend_architect() {
        let reg = PersonaRegistry::new();
        let p = reg.recommend_for_intent("设计新的模块架构");
        assert_eq!(p.id, PERSONA_ARCHITECT);
    }

    #[test]
    fn recommend_tester() {
        let reg = PersonaRegistry::new();
        let p = reg.recommend_for_intent("write tests for this module");
        assert_eq!(p.id, PERSONA_TESTER);
    }

    #[test]
    fn recommend_default_to_coder() {
        let reg = PersonaRegistry::new();
        let p = reg.recommend_for_intent("implement a new feature");
        assert_eq!(p.id, PERSONA_CODER);
    }

    #[test]
    fn tool_whitelist_filters() {
        let reviewer = AgentPersona {
            id: "test".into(),
            name: "test".into(),
            description: "".into(),
            capabilities: vec![],
            tool_whitelist: vec!["read_file".into(), "search_files".into()],
            system_prompt_template: "".into(),
            priority: 5,
        };
        assert!(reviewer.is_tool_allowed("read_file"));
        assert!(!reviewer.is_tool_allowed("write_file"));
    }

    #[test]
    fn empty_whitelist_allows_all() {
        let coder = AgentPersona {
            id: "test".into(),
            name: "test".into(),
            description: "".into(),
            capabilities: vec![],
            tool_whitelist: vec![],
            system_prompt_template: "".into(),
            priority: 5,
        };
        assert!(coder.is_tool_allowed("run_command"));
        assert!(coder.is_tool_allowed("write_file"));
    }

    #[test]
    fn build_prompt_replaces_placeholders() {
        let reg = PersonaRegistry::new();
        let coder = reg.get(PERSONA_CODER).unwrap();
        let prompt = coder.build_prompt("some context", &["goal1".into()]);
        assert!(prompt.contains("程序员"));
        assert!(prompt.contains("goal1"));
        assert!(prompt.contains("some context"));
    }

    #[test]
    fn register_custom_persona() {
        let mut reg = PersonaRegistry::new();
        let custom = AgentPersona {
            id: "custom".into(),
            name: "自定义".into(),
            description: "test".into(),
            capabilities: vec![],
            tool_whitelist: vec![],
            system_prompt_template: "You are custom".into(),
            priority: 0,
        };
        reg.register(custom);
        assert_eq!(reg.len(), 6);
        assert!(reg.get("custom").is_some());
    }
}
