"""Tests for the CodeCache persistence layer."""

import pytest
from skilark_cli.code_cache import CodeCache


class TestLoadEmpty:
    def test_load_missing_problem_returns_defaults(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        result = cache.load("cp-1")
        assert result == {"current": None, "attempts": [], "passed": []}

    def test_load_missing_file_returns_defaults(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        result = cache.load("cp-999")
        assert result["current"] is None
        assert result["attempts"] == []
        assert result["passed"] == []


class TestSaveCurrent:
    def test_save_current_persists(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.save_current("cp-1", "def solve():\n    pass\n")
        result = cache.load("cp-1")
        assert result["current"] == "def solve():\n    pass\n"

    def test_save_current_overwrites(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.save_current("cp-1", "first")
        cache.save_current("cp-1", "second")
        result = cache.load("cp-1")
        assert result["current"] == "second"

    def test_save_current_separate_problems(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.save_current("cp-1", "code-a")
        cache.save_current("cp-2", "code-b")
        assert cache.load("cp-1")["current"] == "code-a"
        assert cache.load("cp-2")["current"] == "code-b"

    def test_save_current_creates_directory(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.save_current("cp-1", "code")
        assert (tmp_path / "code_cache" / "python.yaml").exists()

    def test_separate_language_files(self, tmp_path):
        py_cache = CodeCache(language="python", config_dir=tmp_path)
        java_cache = CodeCache(language="java", config_dir=tmp_path)
        py_cache.save_current("cp-1", "python-code")
        java_cache.save_current("cp-1", "java-code")
        assert py_cache.load("cp-1")["current"] == "python-code"
        assert java_cache.load("cp-1")["current"] == "java-code"


class TestClearCurrent:
    def test_clear_current_removes_current(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.save_current("cp-1", "code")
        cache.clear_current("cp-1")
        assert cache.load("cp-1")["current"] is None

    def test_clear_nonexistent_is_noop(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.clear_current("cp-999")  # should not raise

    def test_clear_current_preserves_attempts(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.save_current("cp-1", "current-code")
        cache.record_attempt("cp-1", "attempt-code", passed=False)
        cache.clear_current("cp-1")
        result = cache.load("cp-1")
        assert result["current"] is None
        assert result["attempts"] == ["attempt-code"]


class TestRecordAttempt:
    def test_record_failed_attempt(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.record_attempt("cp-1", "code-v1", passed=False)
        result = cache.load("cp-1")
        assert result["attempts"] == ["code-v1"]
        assert result["passed"] == []

    def test_record_passed_attempt(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.record_attempt("cp-1", "code-v1", passed=True)
        result = cache.load("cp-1")
        assert result["attempts"] == []
        assert result["passed"] == ["code-v1"]

    def test_dedup_same_code(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.record_attempt("cp-1", "same-code", passed=False)
        cache.record_attempt("cp-1", "same-code", passed=False)
        result = cache.load("cp-1")
        assert result["attempts"] == ["same-code"]

    def test_cap_at_five(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        for i in range(7):
            cache.record_attempt("cp-1", f"code-{i}", passed=False)
        result = cache.load("cp-1")
        assert len(result["attempts"]) == 5
        assert result["attempts"] == [f"code-{i}" for i in range(2, 7)]

    def test_cap_at_five_passed(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        for i in range(7):
            cache.record_attempt("cp-1", f"code-{i}", passed=True)
        result = cache.load("cp-1")
        assert len(result["passed"]) == 5
        assert result["passed"] == [f"code-{i}" for i in range(2, 7)]

    def test_preserves_current(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.save_current("cp-1", "wip-code")
        cache.record_attempt("cp-1", "run-code", passed=False)
        result = cache.load("cp-1")
        assert result["current"] == "wip-code"
        assert result["attempts"] == ["run-code"]


class TestIsNewAttempt:
    def test_new_attempt_empty_cache(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        assert cache.is_new_attempt("cp-1", "any-code") is True

    def test_same_as_last_failed(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.record_attempt("cp-1", "same-code", passed=False)
        assert cache.is_new_attempt("cp-1", "same-code") is False

    def test_same_as_last_passed(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.record_attempt("cp-1", "same-code", passed=True)
        assert cache.is_new_attempt("cp-1", "same-code") is False

    def test_different_code_is_new(self, tmp_path):
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.record_attempt("cp-1", "old-code", passed=False)
        assert cache.is_new_attempt("cp-1", "new-code") is True

    def test_matches_older_attempt_but_not_last(self, tmp_path):
        """Code matches an older attempt but not the most recent — still considered new."""
        cache = CodeCache(language="python", config_dir=tmp_path)
        cache.record_attempt("cp-1", "code-a", passed=False)
        cache.record_attempt("cp-1", "code-b", passed=False)
        assert cache.is_new_attempt("cp-1", "code-a") is True
