#!/usr/bin/env python3
"""
Validate a skill directory against agentskills.io spec + quality checks.
Usage: validate-skill <path-to-skill-directory>
Exit 0 = pass, 1 = errors, 2 = warnings only.
Zero external dependencies — stdlib only.
"""

import re
import sys
from pathlib import Path


def parse_frontmatter(text: str) -> tuple[dict, str]:
    """Parse simple YAML frontmatter (key: value / key: "value" only).
    Returns (dict, body_text).
    Raises ValueError on unsupported YAML features or parse errors.
    """
    parts = text.split("---\n", 2)
    if len(parts) < 3:
        raise ValueError("Could not find frontmatter delimiters (---)")
    fm_raw = parts[1]
    body = parts[2]

    result = {}
    for i, line in enumerate(fm_raw.splitlines(), 1):
        stripped = line.strip()
        if not stripped:
            continue

        # Detect indented continuation (folded scalars, block scalars, etc.)
        if stripped and line[0] in (" ", "\t") and not result:
            raise ValueError(
                f"Unsupported frontmatter format on line {i}: indented content. "
                f"Use simple key: value or key: \"value\"."
            )

        colon = stripped.find(":")
        if colon == -1:
            raise ValueError(
                f"Unsupported frontmatter format on line {i}: no key: value pair. "
                f"Use simple key: value or key: \"value\"."
            )

        key = stripped[:colon].strip()
        val = stripped[colon + 1:].strip()

        if not val:
            raise ValueError(
                f"Unsupported frontmatter format on line {i}: empty value. "
                f"Use simple key: value or key: \"value\"."
            )

        # Indented continuation lines after a key (e.g. YAML folded >)
        if line[0] in (" ", "\t") and result:
            raise ValueError(
                f"Unsupported frontmatter format on line {i}: indented continuation. "
                f"Use simple key: value or key: \"value\"."
            )

        # Detect YAML special indicators
        if val in (">", "|", "|-", "|+", ">-", ">+"):
            raise ValueError(
                f"Unsupported YAML feature on line {i}: '{val}' scalar. "
                f"Use key: \"value\" instead."
            )

        is_quoted = (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'"))

        # YAML plain scalars cannot contain an unquoted colon followed by whitespace/end.
        # Agentskills clients use real YAML parsers, so accepting this here creates
        # false PASS results that later fail during skill loading.
        if not is_quoted and re.search(r":(\s|$)", val):
            errors_at = re.search(r":(\s|$)", val).start() + 1
            raise ValueError(
                f"Invalid YAML frontmatter on line {i}: unquoted ':' in value near column {colon + 1 + errors_at}. "
                f"Quote the entire value, e.g. {key}: \"{val}\"."
            )

        # Quoted string
        if val.startswith('"') and val.endswith('"'):
            val = val[1:-1]
        elif val.startswith("'") and val.endswith("'"):
            val = val[1:-1]

        result[key] = val

    return result, body


def extract_frontmatter(skill_md: Path) -> tuple[dict, str]:
    """Return (parsed frontmatter dict, body text) from a SKILL.md file."""
    return parse_frontmatter(skill_md.read_text())


def check_spec(skill_dir: Path, skill_name: str, errors: list, warnings: list):
    skill_md = skill_dir / "SKILL.md"
    if not skill_md.exists():
        skill_md = skill_dir / "skill.md"
    if not skill_md.exists():
        errors.append("Missing required file: SKILL.md")
        return

    try:
        fm, _ = extract_frontmatter(skill_md)
    except ValueError as e:
        errors.append(str(e))
        return

    # name
    name = fm.get("name", "")
    if not name:
        errors.append("Missing required field: name")
    else:
        if name != name.lower():
            errors.append(f"name must be lowercase: {name}")
        if name.startswith("-") or name.endswith("-"):
            errors.append(f"name cannot start/end with hyphen: {name}")
        if "--" in name:
            errors.append(f"name cannot have consecutive hyphens: {name}")
        if len(name) > 64:
            errors.append(f"name exceeds 64 chars ({len(name)})")
        if not re.fullmatch(r"[a-z0-9_-]+", name):
            errors.append(f"name contains invalid characters: {name}")
        if name != skill_name:
            warnings.append(f"name '{name}' doesn't match directory '{skill_name}'")

    # description
    desc = fm.get("description", "")
    if not desc:
        errors.append("Missing required field: description")
    else:
        if len(desc) > 1024:
            errors.append(f"description exceeds 1024 chars ({len(desc)})")
        if len(desc) < 20:
            warnings.append(f"description is very short ({len(desc)} chars) — likely missing WHEN/KEYWORDS")

    # unknown fields
    allowed = {"name", "description", "license", "compatibility", "metadata", "allowed-tools"}
    for key in fm:
        if key not in allowed:
            warnings.append(f"Unknown frontmatter field: {key}")


def check_quality(skill_dir: Path, errors: list, warnings: list):
    skill_md = skill_dir / "SKILL.md"
    if not skill_md.exists():
        skill_md = skill_dir / "skill.md"
    if not skill_md.exists():
        return

    try:
        fm, body = extract_frontmatter(skill_md)
    except Exception:
        return  # already reported in spec check

    # Body length
    body_lines = len(body.splitlines())
    if body_lines > 300:
        warnings.append(f"Body is {body_lines} lines (recommended < 300)")

    # Description should have trigger hints
    desc = str(fm.get("description", ""))
    if not re.search(r"use when|trigger|use for", desc, re.IGNORECASE):
        warnings.append("Description lacks WHEN triggers — agent may not activate skill correctly")

    # NEVER rules: each needs WHY + INSTEAD
    never_rules = len(re.findall(r"\*\*NEVER\b", body))
    if never_rules == 0:
        warnings.append("No NEVER rules found — consider adding anti-patterns")
    else:
        instead_count = len(re.findall(r"\*\*Instead", body, re.IGNORECASE))
        why_count = len(re.findall(r"\*\*Why", body, re.IGNORECASE))
        if instead_count < never_rules:
            warnings.append(f"Some NEVER rules missing INSTEAD ({instead_count}/{never_rules})")
        if why_count < never_rules:
            warnings.append(f"Some NEVER rules missing WHY ({why_count}/{never_rules})")

    # Auxiliary files that shouldn't exist
    banned = ["README.md", "CHANGELOG.md", "INSTALLATION_GUIDE.md", "QUICK_REFERENCE.md", "CONTRIBUTING.md"]
    for f in banned:
        if (skill_dir / f).exists():
            errors.append(f"Auxiliary file should not exist: {f}")

    # Referenced files must exist
    for ref in re.findall(r"references/[^\s\")\]`]+", body):
        if not (skill_dir / ref).exists():
            errors.append(f"Referenced file missing: {ref}")

    # references/ dir needs MANDATORY READ triggers
    refs_dir = skill_dir / "references"
    if refs_dir.is_dir():
        ref_count = sum(1 for _ in refs_dir.rglob("*") if _.is_file())
        mandatory_count = len(re.findall(r"MANDATORY.*READ", body, re.IGNORECASE))
        if ref_count > 0 and mandatory_count == 0:
            warnings.append(f"references/ has {ref_count} files but no MANDATORY READ triggers in body")


def main():
    if len(sys.argv) < 2:
        print("Usage: validate-skill <path-to-skill-directory>", file=sys.stderr)
        sys.exit(1)

    skill_dir = Path(sys.argv[1]).resolve()
    skill_name = skill_dir.name
    errors: list[str] = []
    warnings: list[str] = []

    check_spec(skill_dir, skill_name, errors, warnings)
    check_quality(skill_dir, errors, warnings)

    if errors:
        print(f"FAIL {skill_name}\n")
        print("Errors:")
        for e in errors:
            print(f"  ✗ {e}")
        if warnings:
            print("\nWarnings:")
            for w in warnings:
                print(f"  ⚠ {w}")
        sys.exit(1)
    elif warnings:
        print(f"WARN {skill_name}\n")
        print("Warnings:")
        for w in warnings:
            print(f"  ⚠ {w}")
        sys.exit(2)
    else:
        print(f"PASS {skill_name}")
        sys.exit(0)


if __name__ == "__main__":
    main()
