"""utils.feature_flags 단위 테스트."""

import json
import os
import time
from pathlib import Path

import pytest

from utils.feature_flags import FeatureFlagLoader

# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def tmp_flags_file(tmp_path):
    """기본 6개 플래그가 모두 False인 임시 feature_flags.json 반환."""
    flags_file = tmp_path / "feature_flags.json"
    data = {
        "schema_version": "1.0",
        "updated_at": "2026-04-04T00:00:00+09:00",
        "flags": {
            "progressive_disclosure_enabled": False,
            "rw_isolation_enabled": False,
            "hooks_enforcement_enabled": False,
            "trust5_tagging_enabled": False,
            "model_map_enabled": False,
            "haiku_ab_enabled": False,
        },
    }
    flags_file.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
    return str(flags_file)


def make_loader(path: str) -> FeatureFlagLoader:
    """절대 경로를 받아 FeatureFlagLoader 인스턴스 생성."""
    return FeatureFlagLoader(path=path)


# ---------------------------------------------------------------------------
# 1. 기본 로딩: 6개 플래그 모두 False 확인
# ---------------------------------------------------------------------------


class TestBasicLoading:
    def test_all_six_flags_are_false(self, tmp_flags_file):
        loader = make_loader(tmp_flags_file)
        expected_flags = [
            "progressive_disclosure_enabled",
            "rw_isolation_enabled",
            "hooks_enforcement_enabled",
            "trust5_tagging_enabled",
            "model_map_enabled",
            "haiku_ab_enabled",
        ]
        for flag in expected_flags:
            assert loader.is_enabled(flag) is False, f"{flag} should be False"

    def test_get_all_flags_returns_six_entries(self, tmp_flags_file):
        loader = make_loader(tmp_flags_file)
        all_flags = loader.get_all_flags()
        assert len(all_flags) == 6
        assert all(v is False for v in all_flags.values())


# ---------------------------------------------------------------------------
# 2. mtime 캐시 테스트: 파일 미변경 시 재파싱 안 함
# ---------------------------------------------------------------------------


class TestMtimeCache:
    def test_no_reload_when_mtime_unchanged(self, tmp_flags_file):
        loader = make_loader(tmp_flags_file)
        # _load 호출 횟수 추적
        load_call_count = {"count": 0}
        original_load = loader._load

        def counting_load():
            load_call_count["count"] += 1
            original_load()

        loader._load = counting_load

        # reload 여러 번 호출해도 mtime이 같으면 _load 미호출
        loader.reload()
        loader.reload()
        loader.reload()

        assert load_call_count["count"] == 0, "mtime 미변경 시 _load가 호출되면 안 됨"

    def test_reload_triggered_when_mtime_changes(self, tmp_flags_file):
        loader = make_loader(tmp_flags_file)

        # 파일 내용 변경 후 mtime 강제 갱신
        time.sleep(0.01)  # mtime 차이를 위한 최소 대기
        data = json.loads(Path(tmp_flags_file).read_text(encoding="utf-8"))
        data["flags"]["progressive_disclosure_enabled"] = True
        Path(tmp_flags_file).write_text(json.dumps(data, indent=2), encoding="utf-8")

        # reload 후 변경값 반영 확인
        loader.reload()
        assert loader.is_enabled("progressive_disclosure_enabled") is True


# ---------------------------------------------------------------------------
# 3. atomic write 테스트: set_flag 후 값 확인
# ---------------------------------------------------------------------------


class TestAtomicWrite:
    def test_set_flag_persists_to_file(self, tmp_flags_file):
        loader = make_loader(tmp_flags_file)
        loader.set_flag("rw_isolation_enabled", True)

        # 파일에서 직접 읽어 확인
        on_disk = json.loads(Path(tmp_flags_file).read_text(encoding="utf-8"))
        assert on_disk["flags"]["rw_isolation_enabled"] is True

    def test_set_flag_updates_in_memory(self, tmp_flags_file):
        loader = make_loader(tmp_flags_file)
        loader.set_flag("hooks_enforcement_enabled", True)
        assert loader.is_enabled("hooks_enforcement_enabled") is True

    def test_set_flag_updates_updated_at(self, tmp_flags_file):
        loader = make_loader(tmp_flags_file)
        loader.set_flag("model_map_enabled", True)

        on_disk = json.loads(Path(tmp_flags_file).read_text(encoding="utf-8"))
        assert "updated_at" in on_disk
        # 2026-04-04T00:00:00+09:00 보다 이후여야 함
        assert on_disk["updated_at"] != "2026-04-04T00:00:00+09:00"

    def test_set_flag_no_tmp_files_left(self, tmp_flags_file):
        loader = make_loader(tmp_flags_file)
        dir_path = os.path.dirname(tmp_flags_file)
        loader.set_flag("haiku_ab_enabled", True)

        tmp_files = [f for f in os.listdir(dir_path) if f.endswith(".tmp")]
        assert tmp_files == [], f".tmp 파일이 남아있음: {tmp_files}"


# ---------------------------------------------------------------------------
# 4. JSONDecodeError 복구: 잘못된 JSON 작성 후 이전 캐시 유지 확인
# ---------------------------------------------------------------------------


class TestJSONDecodeErrorRecovery:
    def test_invalid_json_keeps_previous_cache(self, tmp_flags_file):
        loader = make_loader(tmp_flags_file)
        # set_flag로 캐시에 True 값 설정
        loader.set_flag("trust5_tagging_enabled", True)
        assert loader.is_enabled("trust5_tagging_enabled") is True

        # 파일을 잘못된 JSON으로 덮어쓰고 mtime 갱신
        time.sleep(0.01)
        Path(tmp_flags_file).write_text("{ invalid json !!!", encoding="utf-8")

        # reload 시도 — JSONDecodeError → 이전 캐시 유지
        loader.reload()
        assert loader.is_enabled("trust5_tagging_enabled") is True, "JSONDecodeError 후 이전 캐시를 유지해야 함"


# ---------------------------------------------------------------------------
# 5. 존재하지 않는 플래그: False 반환 확인
# ---------------------------------------------------------------------------


class TestUnknownFlag:
    def test_unknown_flag_returns_false(self, tmp_flags_file):
        loader = make_loader(tmp_flags_file)
        result = loader.is_enabled("nonexistent_flag_xyz")
        assert result is False

    def test_unknown_flag_does_not_raise(self, tmp_flags_file):
        loader = make_loader(tmp_flags_file)
        # 예외 없이 False 반환해야 함
        try:
            result = loader.is_enabled("totally_made_up_flag")
            assert result is False
        except Exception as e:
            pytest.fail(f"존재하지 않는 플래그 조회 시 예외 발생: {e}")


# ---------------------------------------------------------------------------
# 6. FileNotFoundError 처리: 파일 없을 때 빈 플래그 딕셔너리
# ---------------------------------------------------------------------------


class TestFileNotFoundHandling:
    def test_missing_file_returns_false_for_any_flag(self, tmp_path):
        non_existent = str(tmp_path / "does_not_exist.json")
        loader = make_loader(non_existent)
        assert loader.is_enabled("progressive_disclosure_enabled") is False

    def test_missing_file_get_all_flags_returns_empty(self, tmp_path):
        non_existent = str(tmp_path / "does_not_exist.json")
        loader = make_loader(non_existent)
        assert loader.get_all_flags() == {}


# ---------------------------------------------------------------------------
# 7. 실제 workspace 파일 로딩 (통합 검증)
# ---------------------------------------------------------------------------


class TestRealWorkspaceFile:
    def test_progressive_disclosure_is_false_in_real_file(self):
        """실제 .claude/feature_flags.json 기준 검증."""
        real_path = "/home/jay/workspace/.claude/feature_flags.json"
        if not os.path.exists(real_path):
            pytest.skip("실제 feature_flags.json 파일이 없음")
        loader = make_loader(real_path)
        assert loader.is_enabled("progressive_disclosure_enabled") is True
