#!/usr/bin/env python3
"""Validate skill directory structure and SKILL.md frontmatter.

Zero external dependencies — uses only Python standard library.
Exit code 0: all checks passed (warnings are OK).
Exit code 1: at least one ERROR found.

Usage:
    python validate_skills.py                    # scan default path (skills/)
    python validate_skills.py --path some/dir    # scan specific directory
"""

import argparse
import os
import re
import sys


# ---------------------------------------------------------------------------
# Minimal frontmatter parser
# ---------------------------------------------------------------------------
def extract_frontmatter(text):
    """Extract YAML frontmatter string between --- markers. Returns None if not found."""
    stripped = text.lstrip("\ufeff")
    if not stripped.startswith("---"):
        return None
    end = stripped.find("---", 3)
    if end == -1:
        return None
    return stripped[3:end]


def parse_frontmatter_fields(fm_text):
    """Parse top-level scalar fields from frontmatter text.

    Returns dict of {field_name: value_string}. Nested keys under a mapping
    are ignored — we only need top-level presence checks.
    """
    fields = {}
    lines = fm_text.splitlines()
    i = 0
    while i < len(lines):
        line = lines[i]
        if not line.strip() or line.strip().startswith("#"):
            i += 1
            continue
        m = re.match(r"^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.*)", line)
        if m:
            key = m.group(1)
            rest = m.group(2).strip()
            if rest in ("|", ">", "|+", "|-", ">+", ">-"):
                block_lines = []
                i += 1
                while i < len(lines) and (lines[i].startswith("  ") or lines[i].startswith("\t") or lines[i].strip() == ""):
                    block_lines.append(lines[i])
                    i += 1
                fields[key] = "\n".join(block_lines).strip()
                continue
            elif rest == "":
                block_lines = []
                i += 1
                while i < len(lines) and (lines[i].startswith("  ") or lines[i].startswith("\t")):
                    block_lines.append(lines[i])
                    i += 1
                fields[key] = "\n".join(block_lines).strip() if block_lines else ""
                continue
            else:
                fields[key] = rest.strip("\"'")
        i += 1
    return fields


# ---------------------------------------------------------------------------
# Secret scanning
# ---------------------------------------------------------------------------
SECRET_PATTERNS = [
    (r"sk-[a-zA-Z0-9]{20,}", "OpenAI-style API key"),
    (r"AKIA[0-9A-Z]{16}", "AWS access key"),
    (r"Bearer\s+[a-zA-Z0-9_\-\.]{50,}", "Hardcoded bearer token"),
]

SCAN_EXTENSIONS = {".md", ".py", ".sh", ".js", ".ts", ".json", ".yaml", ".yml", ".txt", ".toml", ".cfg", ".ini"}

def scan_secrets(filepath):
    """Scan a file for hardcoded secrets. Returns list of (line_no, pattern_desc, matched_text)."""
    try:
        with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
            content = f.read()
    except Exception:
        return []

    findings = []
    for line_no, line in enumerate(content.splitlines(), 1):
        for pattern, desc in SECRET_PATTERNS:
            for match in re.finditer(pattern, line):
                findings.append((line_no, desc, match.group(0)[:60]))
    return findings


# ---------------------------------------------------------------------------
# Skill discovery and validation
# ---------------------------------------------------------------------------
def find_skill_dirs(base_path):
    """Find directories that contain a SKILL.md."""
    skill_dirs = []
    for root, dirs, files in os.walk(base_path):
        dirs[:] = [d for d in dirs if not d.startswith(".")]
        if "SKILL.md" in files:
            skill_dirs.append(root)
    return sorted(skill_dirs)


def validate_skill(skill_dir):
    """Validate a single skill directory. Returns (errors, warnings) lists."""
    errors = []
    warnings = []
    dir_name = os.path.basename(skill_dir)
    skill_md = os.path.join(skill_dir, "SKILL.md")

    if not os.path.isfile(skill_md):
        errors.append("SKILL.md not found")
        return errors, warnings

    with open(skill_md, "r", encoding="utf-8", errors="ignore") as f:
        content = f.read()

    fm_text = extract_frontmatter(content)
    if fm_text is None:
        errors.append("SKILL.md has no valid YAML frontmatter (missing --- markers)")
        return errors, warnings

    fields = parse_frontmatter_fields(fm_text)

    name = fields.get("name", "").strip()
    if not name:
        errors.append("Missing required field: name")
    elif name != dir_name:
        errors.append(f"name '{name}' does not match directory name '{dir_name}'")

    desc = fields.get("description", "").strip()
    if not desc:
        errors.append("Missing required field: description")

    if "license" not in fields or not fields["license"].strip():
        warnings.append("Missing recommended field: license")

    if "metadata" not in fields or not fields["metadata"].strip():
        warnings.append("Missing recommended field: metadata")

    for root, dirs, files in os.walk(skill_dir):
        dirs[:] = [d for d in dirs if not d.startswith(".")]
        for fname in files:
            _, ext = os.path.splitext(fname)
            if ext not in SCAN_EXTENSIONS:
                continue
            fpath = os.path.join(root, fname)
            for line_no, sdesc, matched in scan_secrets(fpath):
                rel = os.path.relpath(fpath, skill_dir)
                errors.append(f"Potential secret in {rel}:{line_no} ({sdesc}): {matched}...")

    return errors, warnings


def main():
    parser = argparse.ArgumentParser(description="Validate MiniMax Skills structure")
    parser.add_argument("--path", default="skills", help="Directory to scan (default: skills/)")
    args = parser.parse_args()

    scan_path = os.path.abspath(args.path)

    skill_dirs = find_skill_dirs(scan_path)
    if not skill_dirs:
        print("No skill directories found.")
        sys.exit(0)

    print(f"\nValidating {len(skill_dirs)} skill(s)...\n")

    total_errors = 0
    total_warnings = 0

    for sd in skill_dirs:
        rel = os.path.relpath(sd)
        errors, warnings = validate_skill(sd)

        if errors:
            status = "FAIL"
        elif warnings:
            status = "WARN"
        else:
            status = "PASS"

        print(f"  [{status}]  {rel}")
        for msg in errors:
            print(f"           ERROR  {msg}")
        for msg in warnings:
            print(f"           WARN   {msg}")

        total_errors += len(errors)
        total_warnings += len(warnings)

    print()
    if total_errors:
        print(f"  {total_errors} error(s), {total_warnings} warning(s)")
        print("  Validation FAILED.\n")
        sys.exit(1)
    elif total_warnings:
        print(f"  0 errors, {total_warnings} warning(s)")
        print("  Validation PASSED.\n")
    else:
        print("  All checks passed.\n")


if __name__ == "__main__":
    main()
