"""
Skill Activator - Skill 激活控制器

负责协调 Skill 的激活流程：
1. 语义搜索相关 Skill
2. 确定激活模式 (INJECTION only)
3. 执行激活 (获取注入指令)
"""

import re
import unicodedata
from typing import Any

from loom.providers.llm.interface import LLMProvider
from loom.tools.core.sandbox_manager import SandboxToolManager

from .models import ActivationResult, SkillDefinition

# =============================================================================
# Token extraction utilities (CJK + Latin)
# =============================================================================


def _is_cjk(char: str) -> bool:
    """Check if a character is CJK (Chinese/Japanese/Korean)."""
    try:
        return "CJK" in unicodedata.name(char, "")
    except ValueError:
        return False


def _extract_tokens(text: str) -> set[str]:
    """Extract searchable tokens from text.

    - Latin/alphanumeric: word-level tokens (len > 2)
    - CJK: character bigrams + individual characters
    """
    text = text.lower()
    tokens: set[str] = set()

    # Latin words (len > 2)
    words = re.findall(r"[a-z0-9_]+", text)
    tokens.update(w for w in words if len(w) > 2)

    # CJK bigrams + unigrams
    cjk_chars = [c for c in text if _is_cjk(c)]
    for i in range(len(cjk_chars) - 1):
        tokens.add(cjk_chars[i] + cjk_chars[i + 1])
    tokens.update(cjk_chars)

    return tokens


def _token_overlap_score(query_tokens: set[str], target_tokens: set[str]) -> float:
    """Compute overlap score between query and target token sets."""
    if not query_tokens or not target_tokens:
        return 0.0
    overlap = query_tokens & target_tokens
    return len(overlap) / len(query_tokens)


class SkillActivator:
    """
    Skill 激活协调器
    """

    def __init__(
        self,
        llm_provider: LLMProvider,
        tool_registry: Any | None = None,
        tool_manager: SandboxToolManager | None = None,
    ):
        self.llm = llm_provider
        self.tool_registry = tool_registry
        self.tool_manager = tool_manager

    async def find_relevant_skills(
        self,
        task_description: str,
        skill_metadata: list[dict[str, Any]],
        max_skills: int = 5,
    ) -> list[str]:
        """
        查找相关的 Skills（n-gram 匹配，支持 CJK + Latin）

        使用 token 提取 + 重叠度评分。CJK 文本使用字符 bigram，
        Latin 文本使用单词级 token。
        """
        if not task_description:
            return []

        query_tokens = _extract_tokens(task_description)
        if not query_tokens:
            return []

        scored_skills: list[tuple[float, str]] = []
        for m in skill_metadata:
            text = (m.get("skill_id", "") + " " + m.get("description", "")).lower()
            target_tokens = _extract_tokens(text)
            score = _token_overlap_score(query_tokens, target_tokens)
            if score > 0:
                scored_skills.append((score, m["skill_id"]))

        scored_skills.sort(key=lambda x: x[0], reverse=True)
        return [s[1] for s in scored_skills[:max_skills]]

    async def activate(
        self,
        skill: SkillDefinition,
        tool_manager: SandboxToolManager | None = None,
        event_bus: Any | None = None,
    ) -> ActivationResult:
        """
        激活 Skill（统一入口）

        Args:
            skill: Skill 定义
            tool_manager: 工具管理器（用于依赖验证）
            event_bus: 事件总线（可选）

        Returns:
            ActivationResult: 激活结果
        """
        # 1. 验证工具依赖
        missing_tools: list[str] = []
        if skill.required_tools and tool_manager:
            available_tools = set(tool_manager.list_tools())
            for tool_name in skill.required_tools:
                if tool_name not in available_tools:
                    missing_tools.append(tool_name)

        if missing_tools:
            return ActivationResult(
                success=False,
                skill_id=skill.skill_id,
                error=f"Missing required tools: {', '.join(missing_tools)}",
                missing_tools=missing_tools,
            )

        try:
            # 2. 获取注入内容
            content = skill.get_full_instructions()

            # 3. 发布激活事件
            if event_bus:
                await event_bus.publish(
                    "skill.activated",
                    {
                        "skill_id": skill.skill_id,
                        "mode": "INJECTION",
                        "tool_names": list(skill.required_tools) if skill.required_tools else [],
                    },
                )

            return ActivationResult(
                success=True,
                skill_id=skill.skill_id,
                content=content,
                mode="INJECTION",
                tool_names=list(skill.required_tools) if skill.required_tools else None,
            )
        except Exception as e:
            return ActivationResult(
                success=False,
                skill_id=skill.skill_id,
                error=str(e),
            )
