"""Tests for passive-feedback.py - TDD RED phase (구현체 없음, 테스트만 작성).

1. test_snapshot_save: snapshot 저장 → 파일 존재 + 내용 + 메타데이터 확인
2. test_detect_diff_normal: diff 정상 추출 + passive-diffs.jsonl 기록 확인
3. test_detect_whitespace_only: 공백만 변경 시 None 반환
4. test_learning_extraction: learning → learnings.jsonl에 jay-feedback source 저장
5. test_history: get_history → passive-diffs.jsonl 정상 반환
6. test_ai_call_mocking: anthropic SDK 모킹 검증
7. test_snapshot_path_auto_creation: 디렉토리 없어도 자동 생성
"""

import json
import os
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

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

import passive_feedback as pf

SKILL = "satori-cardnews"
ORIGINAL = "상담 예약하기\n지금 신청하세요\n"
MODIFIED = "지금 무료 상담\n지금 신청하세요\n"
WHITESPACE_ONLY = "상담 예약하기  \n지금 신청하세요\n"


def _ai_mock(text: str = "CTA는 '무료' 키워드 포함 시 전환율 향상") -> MagicMock:
    content = MagicMock()
    content.text = text
    resp = MagicMock()
    resp.content = [content]
    return resp


def _write(path: Path, text: str) -> Path:
    path.write_text(text, encoding="utf-8")
    return path


class TestSnapshotSave:
    def test_snapshot_save(self, tmp_path: Path) -> None:
        os.environ["WORKSPACE_ROOT"] = str(tmp_path)
        src = _write(tmp_path / "source.txt", ORIGINAL)

        result = pf.save_snapshot(skill_name=SKILL, file_path=str(src), task_id="task-001")

        snap = Path(result["snapshot_path"])
        assert snap.exists()
        assert snap.read_text(encoding="utf-8") == ORIGINAL
        assert result["skill_name"] == SKILL
        assert result["task_id"] == "task-001"
        assert result["timestamp"]


class TestDetectDiffNormal:
    def test_detect_diff_normal(self, tmp_path: Path) -> None:
        os.environ["WORKSPACE_ROOT"] = str(tmp_path)
        orig = _write(tmp_path / "orig.txt", ORIGINAL)
        mod = _write(tmp_path / "mod.txt", MODIFIED)

        with patch("passive_feedback.anthropic") as mock_anth:
            client = MagicMock()
            mock_anth.Anthropic.return_value = client
            client.messages.create.return_value = _ai_mock()
            result = pf.detect_diff(SKILL, str(orig), str(mod), task_id="task-002")

        assert result is not None

        diff_file = tmp_path / "memory" / "skill-feedback" / SKILL / "passive-diffs.jsonl"
        assert diff_file.exists()
        records = [json.loads(l) for l in diff_file.read_text(encoding="utf-8").strip().splitlines()]
        assert len(records) == 1
        rec = records[0]
        assert rec["skill_name"] == SKILL
        assert rec["task_id"] == "task-002"
        assert "timestamp" in rec
        assert "diff_summary" in rec
        assert isinstance(rec["diff_lines"], list)


class TestDetectWhitespaceOnly:
    def test_detect_whitespace_only(self, tmp_path: Path) -> None:
        os.environ["WORKSPACE_ROOT"] = str(tmp_path)
        orig = _write(tmp_path / "orig.txt", ORIGINAL)
        mod = _write(tmp_path / "mod.txt", WHITESPACE_ONLY)

        result = pf.detect_diff(SKILL, str(orig), str(mod))

        assert result is None


class TestLearningExtraction:
    def test_learning_extraction(self, tmp_path: Path) -> None:
        os.environ["WORKSPACE_ROOT"] = str(tmp_path)
        orig = _write(tmp_path / "orig.txt", ORIGINAL)
        mod = _write(tmp_path / "mod.txt", MODIFIED)
        learning_text = "CTA는 '예약' 대신 '무료' 키워드 포함 시 전환율 향상"

        with patch("passive_feedback.anthropic") as mock_anth:
            client = MagicMock()
            mock_anth.Anthropic.return_value = client
            client.messages.create.return_value = _ai_mock(learning_text)
            pf.detect_diff(SKILL, str(orig), str(mod), task_id="task-003")

        learnings_file = tmp_path / "memory" / "skill-learning" / "learnings.jsonl"
        assert learnings_file.exists()
        records = [json.loads(l) for l in learnings_file.read_text(encoding="utf-8").strip().splitlines()]
        jay_records = [r for r in records if r.get("source") == "jay-feedback"]
        assert jay_records, "jay-feedback source 레코드가 있어야 한다"
        rec = jay_records[0]
        assert rec["skill_name"] == SKILL
        assert rec["expires_at"] is None
        assert rec["learning"] == learning_text


class TestHistory:
    def test_history(self, tmp_path: Path) -> None:
        os.environ["WORKSPACE_ROOT"] = str(tmp_path)
        diff_dir = tmp_path / "memory" / "skill-feedback" / SKILL
        diff_dir.mkdir(parents=True, exist_ok=True)
        rows = [
            {
                "timestamp": "2026-03-25T12:00:00",
                "skill_name": SKILL,
                "task_id": "task-001",
                "snapshot_path": "memory/...",
                "diff_summary": "CTA 변경",
                "diff_lines": [],
                "learning_extracted": "무료 효과적",
                "learning_id": "learn-001",
            },
            {
                "timestamp": "2026-03-25T13:00:00",
                "skill_name": SKILL,
                "task_id": "task-002",
                "snapshot_path": "memory/...",
                "diff_summary": "헤드라인 수정",
                "diff_lines": [],
                "learning_extracted": "숫자 포함 시 클릭률 향상",
                "learning_id": "learn-002",
            },
        ]
        (diff_dir / "passive-diffs.jsonl").write_text(
            "\n".join(json.dumps(r, ensure_ascii=False) for r in rows), encoding="utf-8"
        )

        history = pf.get_history(SKILL)

        assert isinstance(history, list)
        assert len(history) == 2
        assert history[0]["task_id"] == "task-001"
        assert history[1]["task_id"] == "task-002"

    def test_history_empty_when_no_file(self, tmp_path: Path) -> None:
        os.environ["WORKSPACE_ROOT"] = str(tmp_path)
        assert pf.get_history(SKILL) == []


class TestAiCallMocking:
    def test_ai_call_mocking(self, tmp_path: Path) -> None:
        os.environ["WORKSPACE_ROOT"] = str(tmp_path)
        orig = _write(tmp_path / "orig.txt", ORIGINAL)
        mod = _write(tmp_path / "mod.txt", MODIFIED)

        with patch("passive_feedback.anthropic") as mock_anth:
            client = MagicMock()
            mock_anth.Anthropic.return_value = client
            client.messages.create.return_value = _ai_mock()
            pf.detect_diff(SKILL, str(orig), str(mod))

            mock_anth.Anthropic.assert_called_once()
            client.messages.create.assert_called_once()
            call_kwargs = client.messages.create.call_args
            kwargs = call_kwargs.kwargs if call_kwargs.kwargs else call_kwargs[1]
            assert "claude-sonnet-4-6" in kwargs.get("model", "")


class TestSnapshotPathAutoCreation:
    def test_snapshot_path_auto_creation(self, tmp_path: Path) -> None:
        os.environ["WORKSPACE_ROOT"] = str(tmp_path)
        snap_dir = tmp_path / "memory" / "skill-feedback" / SKILL / "snapshots"
        assert not snap_dir.exists()

        src = _write(tmp_path / "source.txt", ORIGINAL)
        result = pf.save_snapshot(skill_name=SKILL, file_path=str(src))

        assert snap_dir.exists()
        assert Path(result["snapshot_path"]).exists()
        assert result["task_id"] is None
