"""Global Skills Manager - centralized skill management for AI Agent"""

import os
import re
import yaml
from typing import Optional, List, Dict, Any
from dataclasses import dataclass
from enum import Enum


class SkillSource(Enum):
    BUILTIN = "builtin"
    USER = "user"


@dataclass
class SkillMetadata:
    """Skill metadata from YAML frontmatter"""
    name: str
    description: str
    extra: Dict[str, Any]
    source: SkillSource = SkillSource.BUILTIN


class Skill:
    """Skill with metadata and content"""
    def __init__(self, metadata: SkillMetadata, content: str, base_path: str):
        self.metadata = metadata
        self.content = content
        self.base_path = base_path
    
    @property
    def references_dir(self) -> str:
        return os.path.join(self.base_path, "references")
    
    @property
    def scripts_dir(self) -> str:
        return os.path.join(self.base_path, "scripts")
    
    @property
    def assets_dir(self) -> str:
        return os.path.join(self.base_path, "assets")
    
    def get_reference(self, name: str) -> Optional[str]:
        """Get reference content by name"""
        ref_path = os.path.join(self.references_dir, name)
        if os.path.exists(ref_path):
            with open(ref_path, 'r', encoding='utf-8') as f:
                return f.read()
        return None
    
    def list_references(self) -> List[str]:
        """List all reference files"""
        if not os.path.exists(self.references_dir):
            return []
        return [f for f in os.listdir(self.references_dir) 
                if os.path.isfile(os.path.join(self.references_dir, f))]
    
    def get_script(self, name: str) -> Optional[str]:
        """Get script content by name"""
        script_path = os.path.join(self.scripts_dir, name)
        if os.path.exists(script_path):
            with open(script_path, 'r', encoding='utf-8') as f:
                return f.read()
        return None
    
    def list_scripts(self) -> List[str]:
        """List all script files"""
        if not os.path.exists(self.scripts_dir):
            return []
        return [f for f in os.listdir(self.scripts_dir)
                if os.path.isfile(os.path.join(self.scripts_dir, f))]
    
    def check_requirements(self) -> tuple[bool, List[str]]:
        """Check if skill requirements are met
        
        Returns:
            Tuple of (is_satisfied, missing_requirements)
        """
        missing = []
        metadata = self.metadata.extra
        
        if "requires" in metadata:
            requires = metadata["requires"]
            
            if "bins" in requires:
                for bin_name in requires["bins"]:
                    import shutil
                    if not shutil.which(bin_name):
                        missing.append(f"Binary: {bin_name}")
        
        return len(missing) == 0, missing


class GlobalSkills:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance
    
    def __init__(self):
        if self._initialized:
            return
        self._initialized = True
        self._builtin_dir = None
        self._user_dir = None
    
    @property
    def builtin_dir(self) -> str:
        if self._builtin_dir is None:
            import diona.project.agent
            agent_pkg_dir = os.path.dirname(diona.project.agent.__file__)
            self._builtin_dir = os.path.join(agent_pkg_dir, "skills")
        return self._builtin_dir
    
    @property
    def user_dir(self) -> str:
        if self._user_dir is None:
            home = os.path.expanduser("~")
            self._user_dir = os.path.join(home, ".diona", "user", "agent", "skills")
        return self._user_dir
    
    def _parse_frontmatter(self, content: str) -> tuple[Optional[dict], str]:
        """Parse YAML frontmatter from SKILL.md content using regex and yaml
        
        Returns:
            Tuple of (metadata_dict, body_content)
        """
        pattern = r'^---\s*\n(.*?)\n---\s*\n(.*)$'
        match = re.match(pattern, content, re.DOTALL)
        
        if not match:
            return None, content
        
        yaml_content = match.group(1).strip()
        body_content = match.group(2).strip()
        
        try:
            metadata = yaml.safe_load(yaml_content)
            if metadata and isinstance(metadata, dict):
                return metadata, body_content
        except yaml.YAMLError:
            pass
        
        return None, content
    
    def _load_skill(self, skill_dir: str, source: SkillSource) -> Optional[Skill]:
        """Load a skill from directory"""
        skill_name = os.path.basename(skill_dir)
        skill_md = os.path.join(skill_dir, "SKILL.md")
        
        if not os.path.exists(skill_md):
            return None
        
        try:
            with open(skill_md, 'r', encoding='utf-8') as f:
                content = f.read()
        except Exception:
            return None
        
        metadata_dict, body = self._parse_frontmatter(content)
        
        if not metadata_dict:
            metadata = SkillMetadata(
                name=skill_name,
                description="",
                extra={},
                source=source
            )
        else:
            extra = {k: v for k, v in metadata_dict.items() 
                    if k not in ("name", "description")}
            metadata = SkillMetadata(
                name=metadata_dict.get("name", skill_name),
                description=metadata_dict.get("description", ""),
                extra=extra,
                source=source
            )
        
        return Skill(metadata, body, skill_dir)
    
    def _list_skills_in_directory(self, skills_dir: str, source: SkillSource) -> List[SkillMetadata]:
        """List skill metadata in directory"""
        if not os.path.exists(skills_dir):
            return []
        
        results = []
        for item in os.listdir(skills_dir):
            skill_dir = os.path.join(skills_dir, item)
            if os.path.isdir(skill_dir):
                skill = self._load_skill(skill_dir, source)
                if skill:
                    results.append(skill.metadata)
        
        return results
    
    def list_all(self) -> List[SkillMetadata]:
        """List all skill metadata"""
        builtin = self._list_skills_in_directory(self.builtin_dir, SkillSource.BUILTIN)
        user = self._list_skills_in_directory(self.user_dir, SkillSource.USER)
        
        all_skills = builtin + user
        all_skills.sort(key=lambda s: s.name.lower())
        return all_skills
    
    def get(self, name: str) -> Optional[Skill]:
        """Get skill by name"""
        for skills_dir, source in [
            (self.builtin_dir, SkillSource.BUILTIN),
            (self.user_dir, SkillSource.USER)
        ]:
            skill_dir = os.path.join(skills_dir, name)
            if os.path.exists(skill_dir):
                return self._load_skill(skill_dir, source)
        
        return None
    
    def get_reference(self, skill_name: str, reference_name: str) -> Optional[str]:
        """Get skill reference content"""
        skill = self.get(skill_name)
        if skill:
            return skill.get_reference(reference_name)
        return None
    
    def get_script(self, skill_name: str, script_name: str) -> Optional[str]:
        """Get skill script content"""
        skill = self.get(skill_name)
        if skill:
            return skill.get_script(script_name)
        return None
    
    def check_requirements(self, skill_name: str) -> tuple[bool, List[str]]:
        """Check skill requirements"""
        skill = self.get(skill_name)
        if skill:
            return skill.check_requirements()
        return False, [f"Skill '{skill_name}' not found"]


_global_skills: Optional[GlobalSkills] = None


def get_global_skills() -> GlobalSkills:
    global _global_skills
    if _global_skills is None:
        _global_skills = GlobalSkills()
    return _global_skills


def list_all_skills() -> List[SkillMetadata]:
    return get_global_skills().list_all()


def get_skill(name: str) -> Optional[Skill]:
    return get_global_skills().get(name)


def get_skill_reference(skill_name: str, reference_name: str) -> Optional[str]:
    return get_global_skills().get_reference(skill_name, reference_name)


def get_skill_script(skill_name: str, script_name: str) -> Optional[str]:
    return get_global_skills().get_script(skill_name, script_name)


def check_skill_requirements(skill_name: str) -> tuple[bool, List[str]]:
    return get_global_skills().check_requirements(skill_name)
