//! Active skill awareness — proactive skill retrieval triggered by explicit user intent.
//!
//! The agent proactively loads a specific skill when the user explicitly references it
//! in their message. Patterns include:
//!
//! - Direct slug mention: "用 code-review", "使用 skill:code-review", "@code-review"
//! - Task-based invocation: "帮我 code review", "做一次 security scan"
//! - Intent keywords with skill context: "review 我的 PR", "scan for bugs"
//!
//! This is the highest-priority perception mode — if the user explicitly asks for a
//! skill, it always gets injected regardless of auto/passive scores.

use super::{SkillContext, SkillEntry};
use once_cell::sync::Lazy;
use regex::Regex;

/// Active skill awareness that detects explicit skill invocations in user messages.
#[derive(Debug, Clone, Default)]
pub struct ActiveSkillAwareness;

impl ActiveSkillAwareness {
    pub fn new() -> Self {
        Self
    }

    /// Detect if the user explicitly invoked a skill in their message.
    ///
    /// Returns the first matching skill, or `None` if no explicit invocation is found.
    ///
    /// Matching patterns:
    /// - `用 <slug>` / `使用 <slug>` (Chinese: "use / apply <slug>")
    /// - `skill:<slug>` (explicit namespace)
    /// - `@<slug>` (GitHub-style mention)
    /// - `帮我 <slug>` / `<name> 帮我` (Chinese: "help me <slug>")
    /// - `做一次 <name>` / `进行一次 <name>` (Chinese: "do a <name>")
    pub fn detect(&self, input: &str, skills: &[SkillEntry]) -> Option<SkillContext> {
        let input_lower = input.to_lowercase();

        // Try each matching pattern in priority order.
        if let Some(skill) = self.match_by_prefix_pattern(&input_lower, skills) {
            return Some(skill);
        }

        // Try matching by skill name (for natural language invocations).
        if let Some(skill) = self.match_by_name(&input_lower, skills) {
            return Some(skill);
        }

        // Try matching by slug substring in the message.
        self.match_by_slug_substring(&input_lower, skills)
    }

    /// Pattern: "用 code-review", "使用 skill:xxx", "@xxx", "skill:xxx"
    fn match_by_prefix_pattern(&self, input: &str, skills: &[SkillEntry]) -> Option<SkillContext> {
        // Pattern 1: 英文 slug 前缀 "use ", "using ", "apply ", "with "
        static USE_PAT: Lazy<Regex> =
            Lazy::new(|| Regex::new(r"(?i)^\s*(?:use|using|apply|with)\s+([a-z0-9/_-]+)").unwrap());

        if let Some(caps) = USE_PAT.captures(input) {
            let slug = caps.get(1)?.as_str().trim();
            return self.find_skill_by_slug(slug, skills);
        }

        // Pattern 2: skill:xxx
        static SKILL_COLON_PAT: Lazy<Regex> =
            Lazy::new(|| Regex::new(r"(?i)skill\s*:\s*([a-z0-9/_-]+)").unwrap());

        if let Some(caps) = SKILL_COLON_PAT.captures(input) {
            let slug = caps.get(1)?.as_str().trim();
            return self.find_skill_by_slug(slug, skills);
        }

        // Pattern 3: @xxx (mention style)
        static AT_PAT: Lazy<Regex> =
            Lazy::new(|| Regex::new(r"@([a-z0-9][a-z0-9_/-]*[a-z0-9])").unwrap());

        if let Some(caps) = AT_PAT.captures(input) {
            let slug = caps.get(1)?.as_str().trim();
            return self.find_skill_by_slug(slug, skills);
        }

        // Pattern 4: 帮我 xxx, 做一个 xxx, 进行 xxx, 做 xxx
        static ZH_PAT: Lazy<Regex> = Lazy::new(
            || Regex::new(r"(?ix)[\u4e00-\u9fff]+\s+(?:帮我|做一个|进行一次|做|使用|用)\s+([a-z0-9][a-z0-9_/-]{0,30})")
                .unwrap(),
        );

        if let Some(caps) = ZH_PAT.captures(input) {
            let slug_or_name = caps.get(1)?.as_str().trim();
            return self
                .find_skill_by_slug(slug_or_name, skills)
                .or_else(|| self.find_skill_by_name(slug_or_name, skills));
        }

        None
    }

    /// Match by skill name in natural language (e.g., "code review" → "code-review")
    fn match_by_name(&self, input: &str, skills: &[SkillEntry]) -> Option<SkillContext> {
        for skill in skills {
            // Normalize skill name to a search pattern: "Code Review" -> "code review"
            let name_lower = skill.name.to_lowercase();

            // Direct substring match (the skill name appears in the input).
            if input.contains(&name_lower) {
                return Some(SkillContext {
                    label: format!("Active skill: {}", skill.name),
                    content: format!("# {} (actively invoked)\n\n{}", skill.name, skill.content),
                });
            }

            // Try removing hyphens/underscores: "code-review" contains "code review"
            let normalized_name = name_lower.replace(['-', '_'], " ");
            if input.contains(&normalized_name) {
                return Some(SkillContext {
                    label: format!("Active skill: {}", skill.name),
                    content: format!("# {} (actively invoked)\n\n{}", skill.name, skill.content),
                });
            }
        }
        None
    }

    /// Match by slug substring anywhere in the message.
    fn match_by_slug_substring(&self, input: &str, skills: &[SkillEntry]) -> Option<SkillContext> {
        // Remove common command words to isolate the slug.
        let cleaned = input
            .replace("please ", "")
            .replace("帮我", "")
            .replace("帮我review", "")
            .replace("帮我 code review", "")
            .replace("帮我review", "");

        for skill in skills {
            let slug = skill.slug.to_lowercase();
            // Check if the slug (or any segment of it) appears as a word.
            if cleaned.contains(&slug) || slug.split('/').any(|seg| cleaned.contains(seg) && seg.len() > 3)
            {
                return Some(SkillContext {
                    label: format!("Active skill: {}", skill.name),
                    content: format!("# {} (actively invoked)\n\n{}", skill.name, skill.content),
                });
            }
        }
        None
    }

    fn find_skill_by_slug(&self, slug: &str, skills: &[SkillEntry]) -> Option<SkillContext> {
        let slug_lower = slug.to_lowercase();
        skills.iter().find(|s| s.slug.to_lowercase() == slug_lower).map(|skill| {
            SkillContext {
                label: format!("Active skill: {}", skill.name),
                content: format!("# {} (actively invoked)\n\n{}", skill.name, skill.content),
            }
        })
    }

    fn find_skill_by_name(&self, name: &str, skills: &[SkillEntry]) -> Option<SkillContext> {
        let name_lower = name.to_lowercase();
        skills.iter().find(|s| s.name.to_lowercase() == name_lower).map(|skill| {
            SkillContext {
                label: format!("Active skill: {}", skill.name),
                content: format!("# {} (actively invoked)\n\n{}", skill.name, skill.content),
            }
        })
    }
}
