"""
Skill discovery and registry for GSD-RLM.

This module provides tools for discovering SKILL.md files in a directory
tree and managing a registry of loaded skills.

Requirements: INT-03 (skill discovery and loading)
"""

from __future__ import annotations

import logging
from dataclasses import dataclass, field
from functools import lru_cache
from pathlib import Path
from typing import Optional

from gsd_rlm.skills.base import SkillDefinition

logger = logging.getLogger(__name__)


def discover_skills(skills_dir: Path) -> list[SkillDefinition]:
    """Discover all SKILL.md files in skills directory tree (INT-03).

    Recursively searches for SKILL.md files, loads each one, and returns
    a list of successfully loaded skills. Invalid skills are logged and
    skipped.

    Args:
        skills_dir: Root directory for skills search

    Returns:
        List of successfully loaded SkillDefinition instances

    Example:
        >>> skills = discover_skills(Path("skills"))
        >>> print([s.name for s in skills])
        ['gsd-rlm-plan-phase', 'gsd-rlm-execute-phase']
    """
    skills: list[SkillDefinition] = []

    # Handle missing directory gracefully
    if not skills_dir.exists():
        logger.warning(f"Skills directory not found: {skills_dir}")
        return skills

    # Recursively find all SKILL.md files
    for skill_file in skills_dir.rglob("SKILL.md"):
        try:
            skill = SkillDefinition.from_file(skill_file)
            if skill.validate():
                skills.append(skill)
                logger.debug(f"Loaded skill: {skill.name} from {skill_file}")
            else:
                logger.warning(f"Invalid skill (empty content): {skill_file}")
        except Exception as e:
            # Log but don't fail - invalid skills are skipped
            logger.warning(f"Failed to load skill {skill_file}: {e}")

    return skills


@dataclass
class SkillRegistry:
    """Registry for managing discovered skills (INT-03).

    Provides O(1) lookup by skill name, list operations, and reload
    functionality. Skills are loaded from a directory tree and stored
    in a name-to-skill mapping.

    Attributes:
        skills_dir: Directory to search for SKILL.md files
        skills: Dictionary mapping skill names to definitions

    Example:
        >>> registry = SkillRegistry(Path("skills"))
        >>> count = registry.load()
        >>> print(f"Loaded {count} skills")
        >>> skill = registry.get("gsd-rlm-plan-phase")
        >>> print(skill.description)
    """

    skills_dir: Optional[Path] = None
    skills: dict[str, SkillDefinition] = field(default_factory=dict)

    def load(self) -> int:
        """Load all skills from skills directory.

        Discovers all SKILL.md files and loads them into the registry.
        If skills_dir is not set, returns 0.

        Returns:
            Number of successfully loaded skills
        """
        if self.skills_dir is None:
            logger.warning("No skills directory configured")
            return 0

        discovered = discover_skills(self.skills_dir)

        # Clear existing skills before loading
        self.skills.clear()

        # Track duplicates
        seen_names: set[str] = set()

        for skill in discovered:
            name = skill.name
            if name in seen_names:
                logger.warning(
                    f"Duplicate skill name '{name}', last one wins: {skill.path}"
                )
            seen_names.add(name)
            self.skills[name] = skill

        logger.info(f"Loaded {len(self.skills)} skills from {self.skills_dir}")
        return len(self.skills)

    def get(self, name: str) -> Optional[SkillDefinition]:
        """Get skill by name.

        Args:
            name: Skill name to look up

        Returns:
            SkillDefinition if found, None otherwise
        """
        return self.skills.get(name)

    def list(self) -> list[str]:
        """List all skill names.

        Returns:
            List of registered skill names
        """
        return list(self.skills.keys())

    def reload(self) -> int:
        """Clear and reload all skills.

        Returns:
            Number of successfully loaded skills
        """
        return self.load()

    def __len__(self) -> int:
        """Return number of registered skills."""
        return len(self.skills)

    def __contains__(self, name: str) -> bool:
        """Check if skill is registered."""
        return name in self.skills


@lru_cache(maxsize=1)
def _get_cached_registry(skills_dir_str: str) -> SkillRegistry:
    """Get cached skill registry for a directory path.

    This is an internal function used for caching when the skills_dir
    hasn't changed. The cache is invalidated when skills_dir changes.

    Args:
        skills_dir_str: String representation of skills directory path

    Returns:
        Cached SkillRegistry instance
    """
    registry = SkillRegistry(skills_dir=Path(skills_dir_str))
    registry.load()
    return registry


def get_skill_registry(skills_dir: Path, use_cache: bool = True) -> SkillRegistry:
    """Get skill registry, optionally using cache.

    Args:
        skills_dir: Directory to search for SKILL.md files
        use_cache: Whether to use cached registry (default: True)

    Returns:
        SkillRegistry instance with loaded skills
    """
    if use_cache:
        return _get_cached_registry(str(skills_dir))

    # Create fresh registry without cache
    registry = SkillRegistry(skills_dir=skills_dir)
    registry.load()
    return registry
