"""
SkillLoader - Load and manage skills from SKILL.md files

Skills are loaded from:
1. Project directory: .groknroll/skill/<name>/SKILL.md
2. Global directory: ~/.config/groknroll/skill/<name>/SKILL.md

Search order: project → global (first match wins)
"""

from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional


@dataclass
class Skill:
    """
    Represents a loaded skill

    Attributes:
        name: Unique skill identifier (directory name)
        description: Human-readable description (from YAML frontmatter)
        content: Full content of SKILL.md (including frontmatter)
        path: Path to the SKILL.md file
        metadata: Additional metadata from YAML frontmatter
    """

    name: str
    description: str
    content: str
    path: Path
    metadata: dict = field(default_factory=dict)

    def __str__(self) -> str:
        return f"Skill(name={self.name})"

    def __repr__(self) -> str:
        return f"Skill(name='{self.name}', description='{self.description}', path='{self.path}')"


class SkillLoader:
    """
    Load and cache skills from filesystem

    Skills are loaded from SKILL.md files in:
    1. .groknroll/skill/<name>/SKILL.md (project-level)
    2. ~/.config/groknroll/skill/<name>/SKILL.md (global)

    First match wins (project takes precedence over global).

    Example:
        loader = SkillLoader(project_path=Path.cwd())
        skill = loader.load("git-release")
        if skill:
            print(skill.content)

        # List all available skills
        for skill in loader.list_skills():
            print(f"{skill.name}: {skill.description}")
    """

    SKILL_FILENAME = "SKILL.md"
    PROJECT_SKILL_DIR = ".groknroll/skill"
    GLOBAL_SKILL_DIR = "~/.config/groknroll/skill"

    def __init__(self, project_path: Optional[Path] = None):
        """
        Initialize SkillLoader

        Args:
            project_path: Project root directory (defaults to cwd)
        """
        self.project_path = project_path or Path.cwd()
        self._cache: dict[str, Skill] = {}
        self._discovered = False

    @property
    def project_skill_dir(self) -> Path:
        """Get project-level skill directory"""
        return self.project_path / self.PROJECT_SKILL_DIR

    @property
    def global_skill_dir(self) -> Path:
        """Get global skill directory"""
        return Path(self.GLOBAL_SKILL_DIR).expanduser()

    def load(self, name: str) -> Optional[Skill]:
        """
        Load a skill by name

        Args:
            name: Skill name (directory name)

        Returns:
            Skill if found, None otherwise

        Example:
            skill = loader.load("git-release")
            if skill:
                print(skill.content)
        """
        # Check cache first
        if name in self._cache:
            return self._cache[name]

        # Search project directory first, then global
        for skill_dir in [self.project_skill_dir, self.global_skill_dir]:
            skill_path = skill_dir / name / self.SKILL_FILENAME

            if skill_path.exists():
                skill = self._load_skill_file(name, skill_path)
                if skill:
                    self._cache[name] = skill
                    return skill

        return None

    def list_skills(self) -> list[Skill]:
        """
        List all available skills

        Discovers and loads all skills from project and global directories.
        Project skills take precedence over global skills with same name.

        Returns:
            List of all available Skill instances

        Example:
            for skill in loader.list_skills():
                print(f"{skill.name}: {skill.description}")
        """
        if not self._discovered:
            self._discover_all()
            self._discovered = True

        return list(self._cache.values())

    def get_skill_names(self) -> list[str]:
        """
        Get list of all available skill names

        Returns:
            List of skill names
        """
        if not self._discovered:
            self._discover_all()
            self._discovered = True

        return sorted(self._cache.keys())

    def reload(self) -> None:
        """
        Clear cache and reload all skills

        Example:
            loader.reload()  # Refresh skills from disk
        """
        self._cache.clear()
        self._discovered = False

    def clear_cache(self) -> None:
        """
        Clear the skill cache

        Example:
            loader.clear_cache()
        """
        self._cache.clear()
        self._discovered = False

    def _discover_all(self) -> None:
        """Discover all skills in project and global directories"""
        # Global first, then project (project overwrites global with same name)
        for skill_dir in [self.global_skill_dir, self.project_skill_dir]:
            if not skill_dir.exists():
                continue

            for skill_subdir in skill_dir.iterdir():
                if not skill_subdir.is_dir():
                    continue

                skill_path = skill_subdir / self.SKILL_FILENAME
                if skill_path.exists():
                    skill = self._load_skill_file(skill_subdir.name, skill_path)
                    if skill:
                        self._cache[skill.name] = skill

    def _load_skill_file(self, name: str, path: Path) -> Optional[Skill]:
        """
        Load a skill from a SKILL.md file

        Args:
            name: Skill name
            path: Path to SKILL.md file

        Returns:
            Skill instance or None if loading fails
        """
        try:
            content = path.read_text(encoding="utf-8")
            description, metadata = self._parse_frontmatter(content)

            return Skill(
                name=name,
                description=description,
                content=content,
                path=path,
                metadata=metadata,
            )
        except Exception:
            # Skip skills that can't be loaded
            return None

    def _parse_frontmatter(self, content: str) -> tuple[str, dict]:
        """
        Parse YAML frontmatter from skill content

        Args:
            content: Full SKILL.md content

        Returns:
            Tuple of (description, metadata dict)
        """
        description = ""
        metadata: dict = {}

        # Check for YAML frontmatter (between --- markers)
        if content.startswith("---"):
            lines = content.split("\n")
            end_index = -1

            # Find closing ---
            for i, line in enumerate(lines[1:], start=1):
                if line.strip() == "---":
                    end_index = i
                    break

            if end_index > 0:
                # Extract frontmatter lines
                frontmatter_lines = lines[1:end_index]

                # Simple YAML parsing (key: value pairs)
                for line in frontmatter_lines:
                    if ":" in line:
                        key, _, value = line.partition(":")
                        key = key.strip()
                        value = value.strip().strip("\"'")

                        if key == "description":
                            description = value
                        elif key == "name":
                            # name from frontmatter stored in metadata
                            metadata["name"] = value
                        else:
                            metadata[key] = value

        # If no description in frontmatter, use first non-empty line after frontmatter
        if not description:
            lines = content.split("\n")
            in_frontmatter = content.startswith("---")
            frontmatter_ended = not in_frontmatter

            for line in lines:
                if in_frontmatter:
                    if line.strip() == "---" and frontmatter_ended:
                        in_frontmatter = False
                    elif line.strip() == "---":
                        frontmatter_ended = True
                    continue

                stripped = line.strip()
                if stripped and not stripped.startswith("#"):
                    description = stripped[:100]  # First 100 chars
                    break
                elif stripped.startswith("#"):
                    # Use heading as description
                    description = stripped.lstrip("#").strip()[:100]
                    break

        return description or "No description", metadata

    def __len__(self) -> int:
        """Return number of cached skills"""
        if not self._discovered:
            self._discover_all()
            self._discovered = True
        return len(self._cache)

    def __contains__(self, name: str) -> bool:
        """Check if skill exists (supports 'in' operator)"""
        return self.load(name) is not None

    def __str__(self) -> str:
        """String representation"""
        return f"SkillLoader(project={self.project_path})"

    def __repr__(self) -> str:
        """Detailed representation"""
        return f"SkillLoader(project_path='{self.project_path}', cached={len(self._cache)})"
