//! Passive skill awareness — reactive skill retrieval triggered by events.
//!
//! The agent passively activates a skill when its slug or name appears in:
//!
//! - Tool call arguments (e.g., a tool is called with a repository name that matches a "git" skill)
//! - Tool call results / observations (e.g., a linter reports issues matching a "code-review" skill)
//! - System events emitted during the agent loop (e.g., "PR opened" → "pr-review" skill)
//!
//! This is lower-priority than active but higher than auto — it's triggered by
//! specific events rather than ambient relevance scoring.

use super::{SkillContext, SkillEntry, ToolCallEvent};

/// Passive skill awareness triggered by tool-call and event context.
#[derive(Debug, Clone, Default)]
pub struct PassiveSkillAwareness;

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

    /// Detect skill activation from tool-call events.
    ///
    /// The agent can passively "wake up" a skill when:
    /// - A tool call's name or arguments contain a skill slug or keyword
    /// - A tool call result mentions a skill name
    ///
    /// This is primarily driven by tool naming conventions and argument patterns.
    /// For example, a tool named `git_diff` might passively activate a `git` skill.
    pub fn detect(&self, event: &ToolCallEvent, skills: &[SkillEntry]) -> Option<SkillContext> {
        let tool_name = event.tool_name.to_lowercase();
        let args = event.arguments.to_lowercase();

        for skill in skills {
            let slug = skill.slug.to_lowercase();
            let name = skill.name.to_lowercase();

            // Trigger 1: Tool name contains skill slug segment.
            // e.g., tool "git_blame" → skill "git/*" activates
            if Self::slug_in_text(&tool_name, &slug) {
                return Some(Self::context_from_skill(skill, "tool invocation"));
            }

            // Trigger 2: Tool arguments contain skill slug or name keywords.
            // e.g., arguments mention "security" → "security/scan" skill
            if Self::slug_in_text(&args, &slug) || Self::keyword_match(&args, &name) {
                return Some(Self::context_from_skill(skill, "tool arguments"));
            }

            // Trigger 3: Common tool prefixes that map to skill categories.
            if let Some(cat_skill) = Self::match_tool_category(&tool_name, skills) {
                return Some(cat_skill);
            }
        }

        None
    }

    /// Detect skill activation from a raw text observation (e.g., tool result text).
    pub fn detect_from_text(&self, text: &str, skills: &[SkillEntry]) -> Option<SkillContext> {
        let text_lower = text.to_lowercase();

        for skill in skills {
            let slug = skill.slug.to_lowercase();
            let name = skill.name.to_lowercase();

            if Self::slug_in_text(&text_lower, &slug) || Self::keyword_match(&text_lower, &name) {
                return Some(Self::context_from_skill(skill, "observation match"));
            }
        }

        None
    }

    /// Match common tool name prefixes to skill categories.
    fn match_tool_category(tool_name: &str, skills: &[SkillEntry]) -> Option<SkillContext> {
        let category_map = [
            ("git_", "git"),
            ("repo_", "repo"),
            ("issue_", "issue"),
            ("pr_", "pull_request"),
            ("pull_request_", "pull_request"),
            ("code_review", "code-review"),
            ("security_scan", "security"),
            ("linter", "linter"),
            ("test_", "testing"),
            ("deploy_", "deployment"),
            ("docker_", "docker"),
            ("k8s_", "kubernetes"),
            ("db_", "database"),
            ("sql_", "database"),
        ];

        for (prefix, category) in category_map {
            if tool_name.starts_with(prefix) {
                if let Some(skill) = skills.iter().find(|s| {
                    s.slug.to_lowercase().contains(category)
                        || s.name.to_lowercase().contains(category)
                }) {
                    return Some(Self::context_from_skill(skill, "tool category match"));
                }
            }
        }

        None
    }

    /// True if the slug (or a significant segment of it) appears in the text.
    fn slug_in_text(text: &str, slug: &str) -> bool {
        text.contains(slug)
            || slug
                .split('/')
                .filter(|seg| seg.len() >= 3)
                .any(|seg| text.contains(seg))
    }

    /// Match skill name keywords against the text (handles multi-word names).
    fn keyword_match(text: &str, name: &str) -> bool {
        // For multi-word names, require all significant words to appear.
        let significant: Vec<_> = name
            .split(|c: char| !c.is_alphanumeric())
            .filter(|w| w.len() >= 3)
            .collect();

        if significant.len() >= 2 {
            significant.iter().all(|w| text.contains(*w))
        } else {
            significant.first().map_or(false, |w| text.contains(w))
        }
    }

    fn context_from_skill(skill: &SkillEntry, trigger: &str) -> SkillContext {
        SkillContext {
            label: format!("Passive skill: {} ({})", skill.name, trigger),
            content: format!(
                "# {} (passive — {})\n\n{}",
                skill.name,
                trigger,
                skill.content
            ),
        }
    }
}
