#!/usr/bin/env python3
"""utils/skill_loader.py 테스트 스위트"""

import sys
import tempfile
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).parent.parent.parent))

from utils.skill_loader import (
    SkillDetail,
    SkillSummary,
    list_skills,
    load_skill,
    view_skill,
)

SKILL_DIR = Path("/home/jay/workspace/skills")


def make_skill_dir(tmp_path: Path, name: str, content: str) -> Path:
    """테스트용 스킬 디렉토리 생성 헬퍼"""
    skill_dir = tmp_path / name
    skill_dir.mkdir(parents=True)
    (skill_dir / "SKILL.md").write_text(content, encoding="utf-8")
    return skill_dir


class TestSkillSummary:
    """SkillSummary dataclass 구조 테스트"""

    def test_fields_exist(self) -> None:
        """필수 필드 존재 확인"""
        s = SkillSummary(name="test", description="설명", category="general")
        assert s.name == "test"
        assert s.description == "설명"
        assert s.category == "general"


class TestSkillDetail:
    """SkillDetail dataclass 구조 테스트"""

    def test_fields_exist(self) -> None:
        """필수 필드 존재 확인"""
        d = SkillDetail(
            name="test",
            description="설명",
            triggers=["trigger1"],
            requires=[],
            full_path="/path/to/SKILL.md",
        )
        assert d.name == "test"
        assert d.triggers == ["trigger1"]
        assert d.requires == []
        assert d.full_path == "/path/to/SKILL.md"


class TestListSkills:
    """list_skills() 테스트"""

    def test_returns_list(self, tmp_path: Path) -> None:
        """반환 타입은 list"""
        make_skill_dir(tmp_path, "skill-a", "---\nname: skill-a\ndescription: 설명A\n---\n# skill-a\n")
        result = list_skills(tmp_path)
        assert isinstance(result, list)

    def test_finds_skills_with_skill_md(self, tmp_path: Path) -> None:
        """SKILL.md 있는 디렉토리만 포함"""
        make_skill_dir(tmp_path, "skill-a", "---\nname: skill-a\ndescription: 설명A\n---\n")
        make_skill_dir(tmp_path, "skill-b", "---\nname: skill-b\ndescription: 설명B\n---\n")
        # SKILL.md 없는 디렉토리
        (tmp_path / "no-skill").mkdir()
        result = list_skills(tmp_path)
        names = [s.name for s in result]
        assert "skill-a" in names
        assert "skill-b" in names
        assert "no-skill" not in names

    def test_summary_has_name_and_description(self, tmp_path: Path) -> None:
        """name, description 올바르게 파싱"""
        make_skill_dir(
            tmp_path, "my-skill",
            "---\nname: my-skill\ndescription: 내 스킬 설명\n---\n# 제목\n"
        )
        result = list_skills(tmp_path)
        assert len(result) >= 1
        skill = next(s for s in result if s.name == "my-skill")
        assert skill.description == "내 스킬 설명"

    def test_description_fallback_to_heading(self, tmp_path: Path) -> None:
        """YAML frontmatter 없을 때 # 헤딩에서 설명 추출"""
        make_skill_dir(tmp_path, "plain-skill", "# 플레인 스킬 설명\n\n본문 내용\n")
        result = list_skills(tmp_path)
        skill = next((s for s in result if s.name == "plain-skill"), None)
        assert skill is not None
        assert "플레인 스킬 설명" in skill.description

    def test_empty_dir_returns_empty_list(self, tmp_path: Path) -> None:
        """빈 디렉토리는 빈 리스트"""
        result = list_skills(tmp_path)
        assert result == []

    def test_nonexistent_dir_returns_empty_list(self) -> None:
        """존재하지 않는 디렉토리는 빈 리스트"""
        result = list_skills("/nonexistent/skills/dir")
        assert result == []

    def test_real_skills_dir(self) -> None:
        """실제 스킬 디렉토리에서 로드 성공"""
        if not SKILL_DIR.exists():
            pytest.skip("스킬 디렉토리 없음")
        result = list_skills(SKILL_DIR)
        assert len(result) > 0
        for s in result:
            assert isinstance(s.name, str)
            assert len(s.name) > 0

    def test_returns_skill_summary_instances(self, tmp_path: Path) -> None:
        """반환 항목이 SkillSummary 인스턴스"""
        make_skill_dir(tmp_path, "s1", "---\nname: s1\ndescription: desc\n---\n")
        result = list_skills(tmp_path)
        for item in result:
            assert isinstance(item, SkillSummary)


class TestViewSkill:
    """view_skill() 테스트"""

    def test_returns_skill_detail(self, tmp_path: Path) -> None:
        """SkillDetail 반환"""
        content = (
            "---\nname: my-skill\ndescription: 설명\n"
            "triggers:\n  - trigger1\n  - trigger2\n---\n# my-skill\n"
        )
        make_skill_dir(tmp_path, "my-skill", content)
        detail = view_skill(tmp_path, "my-skill")
        assert isinstance(detail, SkillDetail)

    def test_parses_triggers_from_frontmatter(self, tmp_path: Path) -> None:
        """YAML frontmatter의 triggers 파싱"""
        content = (
            "---\nname: t-skill\ndescription: 설명\n"
            "triggers:\n  - ship\n  - deploy\n---\n"
        )
        make_skill_dir(tmp_path, "t-skill", content)
        detail = view_skill(tmp_path, "t-skill")
        assert "ship" in detail.triggers
        assert "deploy" in detail.triggers

    def test_full_path_is_absolute(self, tmp_path: Path) -> None:
        """full_path가 절대 경로"""
        make_skill_dir(tmp_path, "abs-skill", "---\nname: abs-skill\ndescription: d\n---\n")
        detail = view_skill(tmp_path, "abs-skill")
        assert Path(detail.full_path).is_absolute()

    def test_raises_for_nonexistent_skill(self, tmp_path: Path) -> None:
        """존재하지 않는 스킬은 FileNotFoundError"""
        with pytest.raises(FileNotFoundError):
            view_skill(tmp_path, "nonexistent-skill")

    def test_description_extracted(self, tmp_path: Path) -> None:
        """description 필드 추출"""
        content = "---\nname: d-skill\ndescription: 상세 설명 텍스트\n---\n"
        make_skill_dir(tmp_path, "d-skill", content)
        detail = view_skill(tmp_path, "d-skill")
        assert detail.description == "상세 설명 텍스트"

    def test_real_retro_skill(self) -> None:
        """실제 retro 스킬 상세 뷰"""
        if not (SKILL_DIR / "retro").exists():
            pytest.skip("retro 스킬 없음")
        detail = view_skill(SKILL_DIR, "retro")
        assert detail.name == "retro"
        assert len(detail.triggers) > 0
        assert detail.full_path.endswith("SKILL.md")


class TestLoadSkill:
    """load_skill() 테스트"""

    def test_returns_string(self, tmp_path: Path) -> None:
        """str 반환"""
        content = "# 스킬 내용\n\n본문.\n"
        make_skill_dir(tmp_path, "full-skill", content)
        result = load_skill(tmp_path, "full-skill")
        assert isinstance(result, str)

    def test_returns_full_content(self, tmp_path: Path) -> None:
        """SKILL.md 전체 내용 반환"""
        content = "---\nname: full\ndescription: d\n---\n# 제목\n\n본문 전체 내용.\n"
        make_skill_dir(tmp_path, "full", content)
        result = load_skill(tmp_path, "full")
        assert "본문 전체 내용" in result

    def test_raises_for_nonexistent_skill(self, tmp_path: Path) -> None:
        """존재하지 않는 스킬은 FileNotFoundError"""
        with pytest.raises(FileNotFoundError):
            load_skill(tmp_path, "no-such-skill")

    def test_real_pdf_skill(self) -> None:
        """실제 pdf 스킬 전체 로드"""
        if not (SKILL_DIR / "pdf").exists():
            pytest.skip("pdf 스킬 없음")
        content = load_skill(SKILL_DIR, "pdf")
        assert len(content) > 100
        assert "PDF" in content
