import re
from pathlib import Path
from typing import List, Optional

import yaml

from pygeai.skills.models import Skill


class SkillValidator:
    """Validates Agent Skills standard compliance (agentskills.io)."""

    REQUIRED_FIELDS = ["name", "description"]
    OPTIONAL_FIELDS = ["license", "compatibility", "metadata", "allowed-tools"]
    NAME_PATTERN = r"^[a-z0-9-]{1,64}$"
    MAX_DESCRIPTION_LENGTH = 1024

    def parse_skill_md(self, content: str, skill_path: Path) -> Optional[Skill]:
        """
        Parse SKILL.md content into Skill object.

        Format:
        ---
        name: skill-name
        description: Description text
        license: Apache-2.0
        metadata:
          author: pygeai
          version: "1.0.0"
          category: cli
        allowed-tools: pygeai
        ---

        # Markdown content
        Instructions for using the skill...
        """
        if not content.startswith("---"):
            raise ValueError("SKILL.md must start with YAML frontmatter")

        parts = content.split("---", 2)
        if len(parts) < 3:
            raise ValueError("Invalid SKILL.md format")

        frontmatter = yaml.safe_load(parts[1])
        markdown_content = parts[2].strip()

        for field in self.REQUIRED_FIELDS:
            if field not in frontmatter:
                raise ValueError(f"Missing required field: {field}")

        if not re.match(self.NAME_PATTERN, frontmatter["name"]):
            raise ValueError(
                f"Invalid skill name. Must be lowercase, alphanumeric, "
                f"and hyphens only (max 64 chars)"
            )

        if len(frontmatter["description"]) > self.MAX_DESCRIPTION_LENGTH:
            raise ValueError(
                f"Description too long (max {self.MAX_DESCRIPTION_LENGTH} chars)"
            )

        allowed_tools_str = frontmatter.get("allowed-tools", "")
        allowed_tools = (
            allowed_tools_str.split() if isinstance(allowed_tools_str, str) else []
        )

        categories = frontmatter.get("categories", [])
        if isinstance(categories, str):
            categories = [categories]

        tags = frontmatter.get("tags", [])
        if isinstance(tags, str):
            tags = [tags]

        skill = Skill(
            name=frontmatter["name"],
            description=frontmatter["description"],
            version=frontmatter.get("version", "1.0.0"),
            categories=categories,
            tags=tags,
            author=frontmatter.get("author", "pygeai"),
            license=frontmatter.get("license"),
            compatibility=frontmatter.get("compatibility"),
            metadata=frontmatter.get("metadata", {}),
            allowed_tools=allowed_tools,
            instructions=markdown_content,
            path=skill_path,
        )

        return skill

    def validate(self, skill_path: Path) -> tuple[bool, List[str]]:
        """
        Validate complete skill structure.

        Checks:
        - SKILL.md exists and is valid
        - Optional directories (scripts/, references/, assets/)
        - Script file types
        """
        errors = []

        skill_md = skill_path / "SKILL.md"
        if not skill_md.exists():
            errors.append("Missing SKILL.md file")
            return False, errors

        try:
            content = skill_md.read_text()
            skill = self.parse_skill_md(content, skill_path)
        except Exception as e:
            errors.append(f"Invalid SKILL.md: {e}")
            return False, errors

        for dir_name in ["scripts", "references", "assets"]:
            dir_path = skill_path / dir_name
            if dir_path.exists() and not dir_path.is_dir():
                errors.append(f"{dir_name} exists but is not a directory")

        scripts_dir = skill_path / "scripts"
        if scripts_dir.exists():
            for script in scripts_dir.iterdir():
                if script.is_file() and script.suffix not in [
                    ".py",
                    ".sh",
                    ".js",
                    ".md",
                ]:
                    errors.append(f"Script {script.name} has unexpected extension")

        return len(errors) == 0, errors
