# 中文注释：
# 文件：echobot/roles/__init__.py
# 说明：角色能力定义与角色注册。

"""Role System - 角色系统

支持不同场景下的专业助手，提供角色加载、切换和自动触发功能。

角色按目录动态加载，结构如下：
    roles/<role_name>/
      ├── SKILL.md
      └── config.yaml
"""

from pathlib import Path
from typing import Optional
import re
import yaml

from loguru import logger


class Role:
    """角色类 - 表示一个具体的角色配置"""

    def __init__(self, name: str, role_dir: Path):
        self.name = name
        self.role_dir = role_dir
        self.config = self._load_config()
        self.skill_content = self._load_skill()

    def _load_config(self) -> dict:
        """加载角色配置文件"""
        config_path = self.role_dir / "config.yaml"
        if config_path.exists():
            with open(config_path, "r", encoding="utf-8") as f:
                return yaml.safe_load(f) or {}
        return {}

    def _load_skill(self) -> str:
        """加载角色技能定义"""
        skill_path = self.role_dir / "SKILL.md"
        if skill_path.exists():
            return skill_path.read_text(encoding="utf-8")
        return ""

    @property
    def display_name(self) -> str:
        return self.config.get("meta", {}).get("display_name", self.name)

    @property
    def description(self) -> str:
        return self.config.get("meta", {}).get("description", "")

    @property
    def trigger_keywords(self) -> list[str]:
        return self.config.get("trigger_keywords", [])

    @property
    def trigger_patterns(self) -> list[str]:
        return self.config.get("trigger_patterns", [])

    @property
    def priority(self) -> int:
        return self.config.get("priority", 0)

    @property
    def skills(self) -> dict:
        return self.config.get("skills", {})

    @property
    def tools(self) -> dict:
        return self.config.get("tools", {})

    @property
    def style(self) -> dict:
        return self.config.get("style", {})

    def matches_keyword(self, message: str) -> bool:
        """检查消息是否匹配触发关键词"""
        message_lower = message.lower()
        for keyword in self.trigger_keywords:
            if keyword.lower() in message_lower:
                return True
        return False

    def matches_pattern(self, message: str) -> bool:
        """检查消息是否匹配触发正则"""
        for pattern in self.trigger_patterns:
            if re.search(pattern, message):
                return True
        return False

    def matches(self, message: str) -> bool:
        """检查消息是否匹配此角色的触发条件"""
        return self.matches_keyword(message) or self.matches_pattern(message)


class RoleManager:
    """角色管理器 - 管理所有角色和切换逻辑"""

    def __init__(self, roles_dir: Optional[Path] = None):
        """
        初始化角色管理器

        Args:
            roles_dir: 可选自定义角色目录路径。传入时仅加载该目录；
                不传时默认加载内置 roles + ~/.echobot/roles（用户覆盖优先）。
        """
        # 从当前文件路径推断项目根目录
        self.builtin_roles_dir = Path(__file__).parent.parent.parent / "roles"
        self.user_roles_dir = Path.home() / ".echobot" / "roles"

        if roles_dir is not None:
            # 显式指定目录时，保持旧行为：仅加载该目录。
            self.roles_dir = roles_dir
            self.roles_dirs = [roles_dir]
        else:
            # 默认行为：先加载内置，再加载用户目录（同名覆盖）。
            self.roles_dir = self.builtin_roles_dir
            self.roles_dirs = [self.builtin_roles_dir, self.user_roles_dir]
            self._ensure_user_roles_dir()

        self.roles: dict[str, Role] = {}
        self.current_role: Optional[Role] = None
        self.load_all_roles()

    def _ensure_user_roles_dir(self) -> None:
        """确保 ~/.echobot/roles 目录存在，并写入最小说明文件与默认角色模板。"""
        try:
            self.user_roles_dir.mkdir(parents=True, exist_ok=True)
            readme = self.user_roles_dir / "README.md"
            if not readme.exists():
                readme.write_text(
                    """# Custom Roles

Put your custom roles here:

```
~/.echobot/roles/<role_name>/
  ├── config.yaml
  └── SKILL.md
```

Load order:
1. Built-in roles from project `roles/`
2. User roles from `~/.echobot/roles/` (same name overrides built-in)
""",
                    encoding="utf-8",
                )

            # 首次初始化时，为用户目录种一个可编辑的 default 角色模板。
            # 该模板来自内置 default；若用户已存在同名角色，则不覆盖。
            user_default_dir = self.user_roles_dir / "default"
            if not user_default_dir.exists():
                builtin_default_dir = self.builtin_roles_dir / "default"
                if builtin_default_dir.exists():
                    user_default_dir.mkdir(parents=True, exist_ok=True)
                    for filename in ("config.yaml", "SKILL.md"):
                        src = builtin_default_dir / filename
                        dst = user_default_dir / filename
                        if src.exists() and not dst.exists():
                            dst.write_text(src.read_text(encoding="utf-8"), encoding="utf-8")
        except Exception as e:
            logger.warning(f"Failed to prepare user roles directory: {e}")

    def load_all_roles(self) -> None:
        """加载所有角色配置"""
        self.roles = {}
        for base_dir in self.roles_dirs:
            if not base_dir.exists():
                logger.debug(f"Roles directory not found: {base_dir}")
                continue

            for role_dir in base_dir.iterdir():
                # 跳过隐藏目录和 pycache
                if not role_dir.is_dir():
                    continue
                if role_dir.name.startswith("__") or role_dir.name.startswith("."):
                    continue

                role = Role(role_dir.name, role_dir)
                if role.name in self.roles:
                    logger.debug(f"Overriding role '{role.name}' from {role_dir}")
                self.roles[role.name] = role
                logger.debug(f"Loaded role: {role.name} ({role.display_name}) from {role_dir}")

        # 设置默认角色为当前角色
        if "default" in self.roles:
            self.current_role = self.roles["default"]
            logger.info(f"Default role set to: {self.current_role.display_name}")

    def get_role(self, name: str) -> Optional[Role]:
        """获取指定角色"""
        return self.roles.get(name)

    def switch_role(self, name: str) -> bool:
        """
        切换到指定角色

        Args:
            name: 角色名称

        Returns:
            是否切换成功
        """
        role = self.get_role(name)
        if role:
            self.current_role = role
            logger.info(f"Switched to role: {role.display_name}")
            return True
        logger.warning(f"Role not found: {name}")
        return False

    def find_matching_role(self, message: str) -> Optional[Role]:
        """
        根据消息内容自动匹配角色

        Args:
            message: 用户消息

        Returns:
            匹配的角色，如果没有匹配则返回 None
        """
        matches = []
        for role in self.roles.values():
            if role.matches(message):
                matches.append(role)

        # 按优先级排序，优先级高的在前
        matches.sort(key=lambda r: r.priority, reverse=True)

        if matches:
            return matches[0]
        return None

    def get_current_role(self) -> Role:
        """获取当前角色"""
        if not self.current_role:
            # 如果没有当前角色，尝试使用 default
            if "default" in self.roles:
                self.current_role = self.roles["default"]
            else:
                raise RuntimeError("No role available and no default role found")
        return self.current_role

    def get_skill_content(self) -> str:
        """获取当前角色的技能定义"""
        return self.get_current_role().skill_content

    def get_tools(self) -> dict:
        """获取当前角色的工具配置"""
        return self.get_current_role().tools

    def get_skill_names(self) -> list[str]:
        """获取当前角色的技能名称列表"""
        role = self.get_current_role()
        skills = role.skills
        default_skills = skills.get("default", [])
        exclusive_skills = skills.get("exclusive", [])
        return default_skills + exclusive_skills

    def get_role_info(self, name: str) -> Optional[dict]:
        """获取角色信息"""
        role = self.get_role(name)
        if not role:
            return None
        return {
            "name": role.name,
            "display_name": role.display_name,
            "description": role.description,
            "priority": role.priority,
            "trigger_keywords": role.trigger_keywords,
            "tools": role.tools,
            "style": role.style,
        }

    def list_roles(self) -> list[dict]:
        """列出所有角色"""
        return [self.get_role_info(name) for name in self.roles.keys()]

    def auto_select_role(self, message: str) -> Role:
        """
        自动选择角色

        优先级：
        1. 如果消息匹配某个角色，切换到该角色
        2. 否则使用 default 角色

        Args:
            message: 用户消息

        Returns:
            选中的角色
        """
        matched_role = self.find_matching_role(message)
        if matched_role:
            self.current_role = matched_role
            logger.info(f"Auto-matched role: {matched_role.display_name}")
        else:
            # 未匹配到任何角色时，使用 default 角色
            default_role = self.get_role("default")
            if default_role and self.current_role.name != "default":
                self.current_role = default_role
                logger.info("Auto-selected default role (no match)")
        return self.get_current_role()
