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

frontmatter 파싱 및 메타데이터 추출 함수를 검증한다.
"""

import sys
from pathlib import Path

import pytest

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

from utils.skill_parser import (
    extract_category,
    extract_description,
    extract_requires,
    extract_triggers,
    parse_frontmatter,
)

# ---------------------------------------------------------------------------
# parse_frontmatter
# ---------------------------------------------------------------------------


class TestParseFrontmatter:
    """parse_frontmatter(content) -> tuple[dict, str] 테스트"""

    def test_returns_tuple_of_dict_and_str(self) -> None:
        """반환 타입은 (dict, str)"""
        content = "---\nname: skill\n---\n본문"
        fm, body = parse_frontmatter(content)
        assert isinstance(fm, dict)
        assert isinstance(body, str)

    def test_parses_simple_key_value(self) -> None:
        """단순 key: value YAML 파싱"""
        content = "---\nname: my-skill\ndescription: 설명\n---\n본문"
        fm, body = parse_frontmatter(content)
        assert fm.get("name") == "my-skill"
        assert fm.get("description") == "설명"

    def test_body_excludes_frontmatter(self) -> None:
        """body에서 frontmatter 블록 제거됨"""
        content = "---\nname: s\n---\n# 제목\n본문"
        fm, body = parse_frontmatter(content)
        assert "---" not in body
        assert "# 제목" in body
        assert "본문" in body

    def test_no_frontmatter_returns_empty_dict(self) -> None:
        """frontmatter 없으면 빈 dict 반환"""
        content = "# 헤딩만 있는 파일\n본문 내용"
        fm, body = parse_frontmatter(content)
        assert fm == {}
        assert body == content

    def test_empty_string_returns_empty_dict(self) -> None:
        """빈 문자열 입력 시 빈 dict 반환"""
        fm, body = parse_frontmatter("")
        assert fm == {}
        assert body == ""

    def test_parses_yaml_list(self) -> None:
        """YAML 리스트 값 파싱"""
        content = "---\ntriggers:\n  - ship\n  - deploy\n---\n본문"
        fm, body = parse_frontmatter(content)
        assert isinstance(fm.get("triggers"), list)
        assert "ship" in fm["triggers"]
        assert "deploy" in fm["triggers"]

    def test_invalid_yaml_returns_empty_dict(self) -> None:
        """잘못된 YAML은 빈 dict 반환 (예외 없음)"""
        content = "---\n: invalid: [unclosed\n---\n본문"
        fm, body = parse_frontmatter(content)
        assert isinstance(fm, dict)
        assert isinstance(body, str)

    def test_frontmatter_only_file(self) -> None:
        """frontmatter만 있고 본문 없는 파일"""
        content = "---\nname: only-fm\n---\n"
        fm, body = parse_frontmatter(content)
        assert fm.get("name") == "only-fm"
        assert body.strip() == ""


# ---------------------------------------------------------------------------
# extract_description
# ---------------------------------------------------------------------------


class TestExtractDescription:
    """extract_description(fm, body) -> str 테스트"""

    def test_returns_frontmatter_description(self) -> None:
        """frontmatter의 description 우선 반환"""
        fm = {"description": "fm 설명"}
        body = "# 헤딩 제목\n본문"
        assert extract_description(fm, body) == "fm 설명"

    def test_falls_back_to_heading(self) -> None:
        """frontmatter description 없을 때 # 헤딩 사용"""
        fm: dict = {}
        body = "# 헤딩 설명\n\n본문 내용"
        assert extract_description(fm, body) == "헤딩 설명"

    def test_returns_empty_if_no_description(self) -> None:
        """description도 헤딩도 없으면 빈 문자열"""
        fm: dict = {}
        body = "그냥 본문만 있음\n줄 두 번째"
        assert extract_description(fm, body) == ""

    def test_strips_whitespace(self) -> None:
        """앞뒤 공백 제거"""
        fm = {"description": "  공백 설명  "}
        body = ""
        assert extract_description(fm, body) == "공백 설명"

    def test_first_heading_only(self) -> None:
        """여러 헤딩 중 첫 번째만 사용"""
        fm: dict = {}
        body = "# 첫 번째 헤딩\n## 두 번째\n# 세 번째"
        result = extract_description(fm, body)
        assert result == "첫 번째 헤딩"


# ---------------------------------------------------------------------------
# extract_triggers
# ---------------------------------------------------------------------------


class TestExtractTriggers:
    """extract_triggers(fm, body) -> list[str] 테스트"""

    def test_returns_list_from_frontmatter_array(self) -> None:
        """frontmatter triggers 배열 반환"""
        fm = {"triggers": ["ship", "deploy", "release"]}
        body = ""
        result = extract_triggers(fm, body)
        assert result == ["ship", "deploy", "release"]

    def test_returns_list_from_frontmatter_string(self) -> None:
        """frontmatter triggers 문자열 → 단일 항목 리스트"""
        fm = {"trigger": "ship it"}
        body = ""
        result = extract_triggers(fm, body)
        assert result == ["ship it"]

    def test_extracts_from_body_trigger_line(self) -> None:
        """본문에서 'trigger' 포함 줄 추출"""
        fm: dict = {}
        body = "## Trigger\n- Use when deploying\n\n다른 내용"
        result = extract_triggers(fm, body)
        assert len(result) > 0

    def test_extracts_use_when_from_body(self) -> None:
        """본문의 'Use when' 패턴 추출"""
        fm: dict = {}
        body = "Use when you need to ship code\n다른 줄"
        result = extract_triggers(fm, body)
        assert any("ship" in t for t in result)

    def test_empty_fm_and_body_returns_empty(self) -> None:
        """frontmatter도 본문도 없으면 빈 리스트"""
        fm: dict = {}
        body = "그냥 평범한 본문\n트리거 없음"
        result = extract_triggers(fm, body)
        assert isinstance(result, list)

    def test_frontmatter_takes_priority_over_body(self) -> None:
        """frontmatter triggers가 있으면 본문 무시"""
        fm = {"triggers": ["fm-trigger"]}
        body = "Use when something happens"
        result = extract_triggers(fm, body)
        assert result == ["fm-trigger"]


# ---------------------------------------------------------------------------
# extract_requires
# ---------------------------------------------------------------------------


class TestExtractRequires:
    """extract_requires(fm) -> list[str] 테스트"""

    def test_returns_list_from_requires_array(self) -> None:
        """requires 배열 반환"""
        fm = {"requires": ["dep-a", "dep-b"]}
        result = extract_requires(fm)
        assert result == ["dep-a", "dep-b"]

    def test_returns_list_from_string(self) -> None:
        """requires 문자열 → 단일 항목 리스트"""
        fm = {"requires": "single-dep"}
        result = extract_requires(fm)
        assert result == ["single-dep"]

    def test_prerequisites_key_also_works(self) -> None:
        """prerequisites 키도 지원"""
        fm = {"prerequisites": ["pre-a"]}
        result = extract_requires(fm)
        assert result == ["pre-a"]

    def test_empty_fm_returns_empty_list(self) -> None:
        """requires 없으면 빈 리스트"""
        fm: dict = {}
        result = extract_requires(fm)
        assert result == []

    def test_none_value_returns_empty_list(self) -> None:
        """requires 값이 None이면 빈 리스트"""
        fm = {"requires": None}
        result = extract_requires(fm)
        assert result == []


# ---------------------------------------------------------------------------
# extract_category
# ---------------------------------------------------------------------------


class TestExtractCategory:
    """extract_category(fm, body, skill_name) -> str 테스트"""

    def test_returns_frontmatter_category(self) -> None:
        """frontmatter category 우선 반환"""
        fm = {"category": "dev"}
        body = ""
        assert extract_category(fm, body, "my-skill") == "dev"

    def test_extracts_from_body_pattern(self) -> None:
        """본문의 'category: xxx' 패턴 추출"""
        fm: dict = {}
        body = "category: testing\n다른 내용"
        assert extract_category(fm, body, "my-skill") == "testing"

    def test_returns_general_as_default(self) -> None:
        """category 없으면 'general' 반환"""
        fm: dict = {}
        body = "그냥 본문"
        assert extract_category(fm, body, "my-skill") == "general"

    def test_strips_quotes_from_body_category(self) -> None:
        """본문 category 값의 따옴표 제거"""
        fm: dict = {}
        body = 'category: "quoted-cat"'
        result = extract_category(fm, body, "my-skill")
        assert result == "quoted-cat"
