#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
skill-quality-check.py — 전체 스킬 품질 검증 스크립트

검증 항목:
  1. SKILL.md 존재 여부       (FAIL if missing)
  2. Frontmatter 유효성       (WARN if invalid)
  3. evals/ 디렉토리 존재 여부 (INFO if missing)
  4. skill-registry.json 동기화 (WARN if not synced)
  5. eval-axes.json 동기화    (INFO if missing)

판정 기준:
  PASS  — SKILL.md 존재 + frontmatter 유효 + registry 동기화
  WARN  — SKILL.md 존재하지만 frontmatter 불완전 또는 registry 미동기화
  FAIL  — SKILL.md 없음

exit code:
  0 — FAIL 없음 (PASS 또는 WARN만)
  1 — FAIL 하나 이상 존재
"""

import argparse
import json
import sys
from pathlib import Path

SKILLS_DIR = Path("/home/jay/workspace/skills")
REGISTRY_PATH = SKILLS_DIR / "shared" / "skill-registry.json"
EVAL_AXES_PATH = SKILLS_DIR / "shared" / "eval-axes.json"

# ──────────────────────────────────────────────
# YAML frontmatter 파싱 (PyYAML 없이 수동 파싱)
# ──────────────────────────────────────────────

def parse_frontmatter(text: str) -> dict | None:
    """
    SKILL.md 내용에서 YAML frontmatter를 파싱한다.
    첫 줄이 '---'로 시작하고 두 번째 '---' 로 끝나는 블록을 추출.
    반환값: dict (필드명 → 값) 또는 None (frontmatter 없음)
    """
    lines = text.splitlines()
    if not lines or lines[0].strip() != "---":
        return None

    end_idx = None
    for i, line in enumerate(lines[1:], start=1):
        if line.strip() == "---":
            end_idx = i
            break

    if end_idx is None:
        return None

    fm_lines = lines[1:end_idx]
    result: dict = {}
    current_key = None
    current_value_lines: list[str] = []

    def flush_current():
        nonlocal current_key, current_value_lines
        if current_key is not None:
            raw = "\n".join(current_value_lines).strip()
            # 따옴표 제거
            if (raw.startswith('"') and raw.endswith('"')) or \
               (raw.startswith("'") and raw.endswith("'")):
                raw = raw[1:-1]
            result[current_key] = raw
        current_key = None
        current_value_lines = []

    for line in fm_lines:
        # 키: 값 패턴
        if ": " in line and not line.startswith(" ") and not line.startswith("\t"):
            flush_current()
            key, _, val = line.partition(": ")
            current_key = key.strip()
            current_value_lines = [val.strip()]
        elif line.startswith("  ") or line.startswith("\t"):
            # 들여쓰기된 값의 연속
            if current_key is not None:
                current_value_lines.append(line.strip())
        else:
            # ': ' 없는 단독 키 (불리언 플래그 등)
            if line.strip():
                flush_current()
                current_key = line.strip().rstrip(":")
                current_value_lines = []

    flush_current()
    return result


# ──────────────────────────────────────────────
# 공유 데이터 로드
# ──────────────────────────────────────────────

def load_json(path: Path) -> dict:
    try:
        with open(path, encoding="utf-8") as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        return {}


# ──────────────────────────────────────────────
# 단일 스킬 검증
# ──────────────────────────────────────────────

class SkillResult:
    def __init__(self, name: str):
        self.name = name
        self.has_skill_md: bool = False
        self.frontmatter_valid: bool = False
        self.frontmatter_issues: list[str] = []
        self.has_evals: bool = False
        self.registry_synced: bool = False
        self.registry_issues: list[str] = []
        self.axes_synced: bool = False

    @property
    def verdict(self) -> str:
        if not self.has_skill_md:
            return "FAIL"
        if not self.frontmatter_valid or not self.registry_synced:
            return "WARN"
        return "PASS"

    def format_line(self) -> str:
        verdict = self.verdict
        parts = []

        # 1. SKILL.md
        if self.has_skill_md:
            parts.append("SKILL.md ✓")
        else:
            parts.append("SKILL.md ✗ (파일 없음)")
            return f"[{verdict}] {self.name} — " + ", ".join(parts)

        # 2. frontmatter
        if self.frontmatter_valid:
            parts.append("frontmatter ✓")
        else:
            issue_str = " / ".join(self.frontmatter_issues) if self.frontmatter_issues else "유효하지 않음"
            parts.append(f"frontmatter ✗ ({issue_str})")

        # 3. evals
        if self.has_evals:
            parts.append("evals ✓")
        else:
            parts.append("evals ✗")

        # 4. registry
        if self.registry_synced:
            parts.append("registry ✓")
        else:
            issue_str = " / ".join(self.registry_issues) if self.registry_issues else "미동기화"
            parts.append(f"registry ✗ ({issue_str})")

        # 5. axes
        if self.axes_synced:
            parts.append("axes ✓")
        else:
            parts.append("axes ✗ (평가 축 미정의)")

        return f"[{verdict}] {self.name} — " + ", ".join(parts)


def check_skill(
    skill_name: str,
    skill_dir: Path,
    registry: dict,
    eval_axes: dict,
) -> SkillResult:
    result = SkillResult(skill_name)

    # 1. SKILL.md 존재 여부
    skill_md_path = skill_dir / "SKILL.md"
    result.has_skill_md = skill_md_path.is_file()

    if result.has_skill_md:
        # 2. Frontmatter 유효성
        try:
            content = skill_md_path.read_text(encoding="utf-8")
        except OSError:
            content = ""

        fm = parse_frontmatter(content)
        if fm is None:
            result.frontmatter_valid = False
            result.frontmatter_issues.append("frontmatter 없음")
        elif "description" not in fm or not fm["description"].strip():
            result.frontmatter_valid = False
            result.frontmatter_issues.append("description 누락")
        else:
            result.frontmatter_valid = True

    # 3. evals/ 디렉토리
    result.has_evals = (skill_dir / "evals").is_dir()

    # 4. skill-registry.json 동기화
    if skill_name in registry:
        result.registry_synced = True
    else:
        result.registry_synced = False
        result.registry_issues.append("미등록")

    # 5. eval-axes.json 동기화
    result.axes_synced = skill_name in eval_axes

    return result


# ──────────────────────────────────────────────
# 요약 출력
# ──────────────────────────────────────────────

def print_summary(results: list[SkillResult]) -> None:
    total = len(results)
    pass_count = sum(1 for r in results if r.verdict == "PASS")
    warn_count = sum(1 for r in results if r.verdict == "WARN")
    fail_count = sum(1 for r in results if r.verdict == "FAIL")

    has_skill_md = sum(1 for r in results if r.has_skill_md)
    valid_fm = sum(1 for r in results if r.has_skill_md and r.frontmatter_valid)
    has_evals = sum(1 for r in results if r.has_evals)
    registry_ok = sum(1 for r in results if r.registry_synced)
    axes_ok = sum(1 for r in results if r.axes_synced)

    def pct(num: int, den: int) -> str:
        if den == 0:
            return "0.0%"
        return f"{num / den * 100:.1f}%"

    print()
    print("=== 스킬 품질 검증 요약 ===")
    print(f"전체: {total}개 | PASS: {pass_count} | WARN: {warn_count} | FAIL: {fail_count}")
    print(f"SKILL.md 보유율: {has_skill_md}/{total} ({pct(has_skill_md, total)})")
    print(f"Frontmatter 유효율: {valid_fm}/{has_skill_md} ({pct(valid_fm, has_skill_md)})")
    print(f"evals/ 보유율: {has_evals}/{total} ({pct(has_evals, total)})")
    print(f"Registry 동기화율: {registry_ok}/{total} ({pct(registry_ok, total)})")
    print(f"Eval-axes 동기화율: {axes_ok}/{total} ({pct(axes_ok, total)})")


# ──────────────────────────────────────────────
# 메인
# ──────────────────────────────────────────────

def collect_skill_dirs() -> list[Path]:
    """shared/ 제외한 스킬 디렉토리 목록 반환 (정렬)"""
    return sorted(
        d for d in SKILLS_DIR.iterdir()
        if d.is_dir() and d.name != "shared"
    )


def main() -> int:
    parser = argparse.ArgumentParser(
        description="스킬 품질 검증 스크립트",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
예시:
  python3 skill-quality-check.py                   # 전체 상세 결과
  python3 skill-quality-check.py --summary          # 요약만 출력
  python3 skill-quality-check.py --skill ad-creative  # 특정 스킬만 검증
""",
    )
    parser.add_argument(
        "--summary",
        action="store_true",
        help="요약만 출력 (개별 스킬 결과 생략)",
    )
    parser.add_argument(
        "--skill",
        metavar="SKILL_NAME",
        help="특정 스킬만 검증",
    )
    args = parser.parse_args()

    # 공유 데이터 로드
    registry = load_json(REGISTRY_PATH)
    eval_axes = load_json(EVAL_AXES_PATH)

    # 유령 등록(registry에는 있지만 디렉토리 없는 경우) 추적용
    existing_skill_names: set[str] = set()

    # 검증할 디렉토리 결정
    if args.skill:
        target_dir = SKILLS_DIR / args.skill
        if not target_dir.is_dir():
            print(f"오류: 스킬 디렉토리를 찾을 수 없습니다 — {target_dir}", file=sys.stderr)
            return 1
        skill_dirs = [target_dir]
    else:
        skill_dirs = collect_skill_dirs()

    results: list[SkillResult] = []

    for skill_dir in skill_dirs:
        skill_name = skill_dir.name
        existing_skill_names.add(skill_name)
        result = check_skill(skill_name, skill_dir, registry, eval_axes)
        results.append(result)

    # 유령 등록 처리 (--skill 옵션 없을 때만 전체 검사)
    ghost_results: list[SkillResult] = []
    if not args.skill:
        for reg_name in sorted(registry.keys()):
            if reg_name not in existing_skill_names:
                ghost = SkillResult(reg_name)
                ghost.has_skill_md = False
                ghost.registry_synced = False
                ghost.registry_issues.append("유령 등록")
                ghost_results.append(ghost)

    # 출력
    if not args.summary:
        for r in results:
            print(r.format_line())
        for g in ghost_results:
            print(f"[WARN] {g.name} — registry ✗ (유령 등록 — 디렉토리 없음)")

    # 유령 등록을 요약에 포함하려면 results에 추가
    # 단, 유령 등록은 디렉토리가 없으므로 WARN으로 처리 (SKILL.md도 없지만 실제 스킬이 아님)
    # 요약에서는 실제 스킬 디렉토리 기준으로만 집계한다

    print_summary(results)

    if ghost_results and not args.summary:
        print(f"\n※ 유령 등록 (registry에는 있지만 디렉토리 없음): {len(ghost_results)}개")
        for g in ghost_results:
            print(f"   - {g.name}")

    # exit code 결정
    has_fail = any(r.verdict == "FAIL" for r in results)
    return 1 if has_fail else 0


if __name__ == "__main__":
    sys.exit(main())
