"""스킬 사용 추적 시스템 테스트."""

import json
import subprocess
import sys
from datetime import datetime, timedelta
from pathlib import Path

import pytest

# ── 헬퍼: 임시 workspace 생성 ──────────────────────────────────────────────


def _make_workspace(tmp_path: Path) -> Path:
    """테스트용 임시 workspace 디렉토리를 생성한다."""
    (tmp_path / "memory").mkdir(parents=True)
    (tmp_path / "dashboard" / "data").mkdir(parents=True)
    (tmp_path / "skills").mkdir(parents=True)
    return tmp_path


def _make_skill_dir(skills_root: Path, dir_name: str, frontmatter: str) -> Path:
    """스킬 디렉토리와 SKILL.md를 생성한다."""
    skill_dir = skills_root / dir_name
    skill_dir.mkdir(parents=True, exist_ok=True)
    (skill_dir / "SKILL.md").write_text(
        f"---\n{frontmatter}\n---\n\n# {dir_name}\n",
        encoding="utf-8",
    )
    return skill_dir


def _make_data_loader(tmp_path: Path):
    """DataLoader 인스턴스를 반환한다."""
    import sys

    sys.path.insert(0, str(Path(__file__).parents[3] / "dashboard"))
    from server import DataLoader  # type: ignore[import]

    loader = DataLoader(tmp_path)
    return loader


# ── 테스트 1: 스킬 레지스트리 기본 로드 ───────────────────────────────────


def test_load_skill_registry_basic(tmp_path: Path) -> None:
    """SKILL.md frontmatter에서 필드를 올바르게 파싱한다."""
    ws = _make_workspace(tmp_path)
    skills_root = ws / "skills"
    _make_skill_dir(
        skills_root,
        "my-skill",
        "name: my-skill\ndescription: 유용한 스킬\ncategory: seo\ntriggers:\n  - foo\n  - bar",
    )

    loader = _make_data_loader(ws)
    result = loader.load_skill_registry()

    assert len(result) >= 1
    skill = next((s for s in result if s["name"] == "my-skill"), None)
    assert skill is not None
    assert skill["description"] == "유용한 스킬"
    assert skill["category"] == "seo"
    assert "foo" in skill["triggers"]
    assert "bar" in skill["triggers"]
    assert skill["status"] == "active"
    assert loader._skill_registry_loaded is True


# ── 테스트 2: deprecated 스킬 감지 ────────────────────────────────────────


def test_load_skill_registry_deprecated(tmp_path: Path) -> None:
    """description에 DEPRECATED가 포함되면 status='deprecated'로 설정된다."""
    ws = _make_workspace(tmp_path)
    skills_root = ws / "skills"
    _make_skill_dir(
        skills_root,
        "old-skill",
        "name: old-skill\ndescription: DEPRECATED: 더 이상 사용하지 않음",
    )

    loader = _make_data_loader(ws)
    result = loader.load_skill_registry()

    skill = next((s for s in result if s["name"] == "old-skill"), None)
    assert skill is not None
    assert skill["status"] == "deprecated"


# ── 테스트 3: 빈 사용 로그 처리 ───────────────────────────────────────────


def test_get_skill_usage_stats_empty(tmp_path: Path) -> None:
    """skill-usage.jsonl 파일이 없으면 빈 결과를 반환한다."""
    ws = _make_workspace(tmp_path)
    # jsonl 파일이 없는 상태로 테스트
    (ws / "dashboard" / "data").rmdir()
    loader = _make_data_loader(ws)

    stats = loader.get_skill_usage_stats("1d")

    assert stats["period"] == "1d"
    assert stats["total_calls"] == 0
    assert stats["unique_skills"] == 0
    assert stats["by_skill"] == []
    assert stats["by_team"] == []
    assert stats["by_date"] == []
    assert stats["top_skills"] == []


# ── 테스트 4: 데이터 있는 경우 집계 ──────────────────────────────────────


def test_get_skill_usage_stats_with_data(tmp_path: Path) -> None:
    """skill-usage.jsonl에 레코드가 있으면 올바르게 집계한다."""
    ws = _make_workspace(tmp_path)
    loader = _make_data_loader(ws)

    now = datetime.now(tz=None)
    records = [
        {
            "skill_id": "ship",
            "timestamp": (now - timedelta(hours=1)).isoformat(),
            "team_id": "dev1-team",
            "task_id": "task-1",
            "session_type": "task",
        },
        {
            "skill_id": "ship",
            "timestamp": (now - timedelta(hours=2)).isoformat(),
            "team_id": "dev2-team",
            "task_id": "task-2",
            "session_type": "task",
        },
        {
            "skill_id": "retro",
            "timestamp": (now - timedelta(hours=3)).isoformat(),
            "team_id": "dev1-team",
            "task_id": "task-3",
            "session_type": "task",
        },
    ]

    jsonl_path = ws / "dashboard" / "data" / "skill-usage.jsonl"
    jsonl_path.write_text(
        "\n".join(json.dumps(r) for r in records),
        encoding="utf-8",
    )

    stats = loader.get_skill_usage_stats("1d")

    assert stats["period"] == "1d"
    assert stats["total_calls"] == 3
    assert stats["unique_skills"] == 2

    by_skill_map = {s["skill_id"]: s["count"] for s in stats["by_skill"]}
    assert by_skill_map["ship"] == 2
    assert by_skill_map["retro"] == 1

    by_team_map = {t["team_id"]: t["count"] for t in stats["by_team"]}
    assert by_team_map["dev1-team"] == 2
    assert by_team_map["dev2-team"] == 1

    assert len(stats["top_skills"]) <= 10
    assert stats["top_skills"][0]["skill_id"] == "ship"


# ── 테스트 5: 스킬 사용 로깅 ──────────────────────────────────────────────


def test_log_skill_usage(tmp_path: Path) -> None:
    """log_skill_usage가 JSONL 파일에 레코드를 추가한다."""
    ws = _make_workspace(tmp_path)
    loader = _make_data_loader(ws)

    data = {
        "skill_id": "pdf",
        "team_id": "mkt-team",
        "task_id": "task-999",
        "session_type": "task",
    }

    result = loader.log_skill_usage(data)

    assert result["status"] == "ok"
    assert "logged_at" in result

    jsonl_path = ws / "dashboard" / "data" / "skill-usage.jsonl"
    assert jsonl_path.exists()
    lines = [l for l in jsonl_path.read_text(encoding="utf-8").splitlines() if l.strip()]
    assert len(lines) == 1
    record = json.loads(lines[0])
    assert record["skill_id"] == "pdf"
    assert record["team_id"] == "mkt-team"
    assert record["task_id"] == "task-999"
    assert "timestamp" in record


# ── 테스트 6: 활성 스킬 조회 ──────────────────────────────────────────────


def test_get_active_skills(tmp_path: Path) -> None:
    """status='running' 태스크가 있으면 active_skills로 반환한다."""
    ws = _make_workspace(tmp_path)
    loader = _make_data_loader(ws)

    task_data = {
        "tasks": {
            "task-100": {
                "task_id": "task-100",
                "team_id": "dev1-team",
                "description": "/ship 배포 워크플로우 실행",
                "start_time": datetime.now(tz=None).isoformat(),
                "status": "running",
            },
            "task-101": {
                "task_id": "task-101",
                "team_id": "dev2-team",
                "description": "일반 코딩 작업",
                "start_time": datetime.now(tz=None).isoformat(),
                "status": "completed",
            },
        }
    }

    memory_dir = ws / "memory"
    (memory_dir / "task-timers.json").write_text(json.dumps(task_data, ensure_ascii=False), encoding="utf-8")

    loader.load_tasks()
    active = loader.get_active_skills()

    assert isinstance(active, list)
    task_ids = [a["task_id"] for a in active]
    assert "task-100" in task_ids
    assert "task-101" not in task_ids


# ── 테스트 7: 매핑 파일 기반 카테고리 자동 분류 ─────────────────────────


def test_load_skill_registry_auto_category_from_mapping(tmp_path: Path) -> None:
    """skill-agent-mapping.json이 있으면 스킬명 기반으로 카테고리가 자동 분류된다."""
    ws = _make_workspace(tmp_path)
    skills_root = ws / "skills"

    # category 없는 스킬 → 매핑에서 'development'
    _make_skill_dir(skills_root, "ship", "name: ship\ndescription: 배포 워크플로우")
    # category='general' 스킬 → 매핑에서 'marketing'으로 오버라이드
    _make_skill_dir(
        skills_root,
        "seo-audit",
        "name: seo-audit\ndescription: SEO 감사\ncategory: general",
    )

    # 매핑 파일 생성
    mapping = {
        "_comment": "test mapping",
        "categories": {
            "development": ["ship", "tdd-enforcement"],
            "marketing": ["seo-audit", "copywriting"],
        },
        "default_category": "shared",
    }
    mapping_path = ws / "dashboard" / "data" / "skill-agent-mapping.json"
    mapping_path.write_text(json.dumps(mapping), encoding="utf-8")

    loader = _make_data_loader(ws)
    result = loader.load_skill_registry()

    ship_skill = next((s for s in result if s["name"] == "ship"), None)
    assert ship_skill is not None
    assert ship_skill["category"] == "development"

    seo_skill = next((s for s in result if s["name"] == "seo-audit"), None)
    assert seo_skill is not None
    assert seo_skill["category"] == "marketing"


# ── 테스트 8: 명시적 카테고리는 매핑을 무시하고 보존 ─────────────────────


def test_load_skill_registry_explicit_category_preserved(tmp_path: Path) -> None:
    """frontmatter에 'general'이 아닌 명시적 카테고리가 있으면 매핑을 무시한다."""
    ws = _make_workspace(tmp_path)
    skills_root = ws / "skills"

    # frontmatter에 category='seo' → 매핑에 ship이 있어도 무시
    _make_skill_dir(
        skills_root,
        "ship",
        "name: ship\ndescription: 배포 스킬\ncategory: seo",
    )

    mapping = {
        "categories": {"development": ["ship"]},
        "default_category": "shared",
    }
    mapping_path = ws / "dashboard" / "data" / "skill-agent-mapping.json"
    mapping_path.write_text(json.dumps(mapping), encoding="utf-8")

    loader = _make_data_loader(ws)
    result = loader.load_skill_registry()

    ship_skill = next((s for s in result if s["name"] == "ship"), None)
    assert ship_skill is not None
    assert ship_skill["category"] == "seo"


# ── 테스트 9: 매핑에 없는 스킬은 default_category 사용 ──────────────────


def test_load_skill_registry_unmapped_uses_default(tmp_path: Path) -> None:
    """매핑 파일에 없는 스킬은 default_category인 'shared'를 할당받는다."""
    ws = _make_workspace(tmp_path)
    skills_root = ws / "skills"

    _make_skill_dir(skills_root, "mystery-skill", "name: mystery-skill\ndescription: 알 수 없는 스킬")

    mapping = {
        "categories": {"development": ["ship"]},
        "default_category": "shared",
    }
    mapping_path = ws / "dashboard" / "data" / "skill-agent-mapping.json"
    mapping_path.write_text(json.dumps(mapping), encoding="utf-8")

    loader = _make_data_loader(ws)
    result = loader.load_skill_registry()

    mystery = next((s for s in result if s["name"] == "mystery-skill"), None)
    assert mystery is not None
    assert mystery["category"] == "shared"


# ── 테스트 10: skill-usage-logger.sh JSONL 출력 포맷 검증 ────────────────


def test_skill_usage_logger_output_format(tmp_path: Path) -> None:
    """skill-usage-logger.sh가 올바른 JSONL 필드를 포함하는 레코드를 기록한다."""
    logger_script = Path("/home/jay/workspace/hooks/skill-usage-logger.sh")
    if not logger_script.exists():
        pytest.skip("skill-usage-logger.sh가 아직 구현되지 않음")

    # 출력 파일을 tmp_path로 지정하기 위해 스크립트가 환경변수를 지원해야 하므로
    # 실제 JSONL 경로를 임시 경로로 패치하지 않고, 실제 경로에 쓰되 기존 내용 보존
    output_jsonl = tmp_path / "skill-usage-test.jsonl"

    # stdin에 전달할 JSON
    hook_input = json.dumps(
        {
            "tool_name": "Skill",
            "tool_input": {"skill": "ship", "args": ""},
            "cwd": "/home/jay/workspace/teams/dev1/tests",
            "session_id": "test-session-001",
        }
    )

    # 스크립트를 실행하고 OUTPUT_JSONL 환경변수로 경로를 주입
    result = subprocess.run(
        ["bash", str(logger_script)],
        input=hook_input,
        capture_output=True,
        text=True,
        env={
            **__import__("os").environ,
            "SKILL_USAGE_JSONL_OVERRIDE": str(output_jsonl),
        },
    )

    # 훅은 항상 exit 0이어야 함
    assert result.returncode == 0, f"스크립트가 실패함: {result.stderr}"

    # JSONL 파일 존재 확인
    assert output_jsonl.exists(), "JSONL 파일이 생성되지 않음"

    lines = [line for line in output_jsonl.read_text(encoding="utf-8").splitlines() if line.strip()]
    assert len(lines) >= 1, "JSONL 레코드가 없음"

    record = json.loads(lines[-1])
    assert record.get("skill_id") == "ship", f"skill_id 불일치: {record}"
    assert "team_id" in record, "team_id 필드 없음"
    assert "timestamp" in record, "timestamp 필드 없음"
    assert "session_type" in record, "session_type 필드 없음"
    assert record.get("session_type") == "hook", f"session_type 불일치: {record}"
