"""Tests for abreka.persistence."""

from abreka.models import BacklogItem, BacklogStatus, Experiment, ExperimentStatus
from abreka.persistence import (
    create_experiment_dir,
    delete_backlog_item,
    load_backlog_item,
    load_experiment,
    next_backlog_id,
    next_experiment_id,
    normalize_backlog_id,
    normalize_exp_id,
    rebuild_backlog_index,
    rebuild_index,
    reserve_experiment,
    save_backlog_item,
    save_experiment,
    update_backlog_index_entry,
    update_index_entry,
)
from abreka.config import load_index


class TestExperimentId:
    def test_normalize(self):
        assert normalize_exp_id("1") == "0001"
        assert normalize_exp_id("42") == "0042"
        assert normalize_exp_id("0001") == "0001"

    def test_next_id_empty(self, abreka_project):
        abreka_dir = abreka_project / "abreka"
        assert next_experiment_id(abreka_dir) == "0001"

    def test_next_id_sequential(self, abreka_project):
        abreka_dir = abreka_project / "abreka"
        (abreka_dir / "experiments" / "0001").mkdir(parents=True)
        (abreka_dir / "experiments" / "0002").mkdir()
        assert next_experiment_id(abreka_dir) == "0003"


class TestExperimentReservation:
    def test_reserve_experiment_creates_proposing_stub_and_updates_index(self, abreka_project):
        abreka_dir = abreka_project / "abreka"

        exp = reserve_experiment(abreka_dir)

        assert exp.id == "0001"
        assert exp.status == ExperimentStatus.PROPOSING
        assert (abreka_dir / "experiments" / "0001" / "experiment.toml").is_file()

        loaded = load_experiment(abreka_dir, "0001")
        assert loaded.status == ExperimentStatus.PROPOSING

        index = load_index(abreka_dir)
        assert index["0001"]["status"] == "proposing"

    def test_reserve_experiment_skips_existing_experiment_ids(self, abreka_project):
        abreka_dir = abreka_project / "abreka"

        first = reserve_experiment(abreka_dir)
        assert first.id == "0001"

        manual = Experiment(id="0002", hypothesis="manual")
        create_experiment_dir(abreka_dir, "0002")
        save_experiment(manual, abreka_dir)
        update_index_entry(abreka_dir, manual)

        third = reserve_experiment(abreka_dir)
        assert third.id == "0003"


class TestExperimentPersistence:
    def test_create_and_load(self, abreka_project):
        abreka_dir = abreka_project / "abreka"
        exp = Experiment(id="0001", hypothesis="Test hypothesis")

        create_experiment_dir(abreka_dir, "0001")
        save_experiment(exp, abreka_dir)

        loaded = load_experiment(abreka_dir, "0001")
        assert loaded.id == "0001"
        assert loaded.hypothesis == "Test hypothesis"
        assert loaded.status == ExperimentStatus.PROPOSED

    def test_update_and_reload(self, abreka_project):
        abreka_dir = abreka_project / "abreka"
        exp = Experiment(id="0001", hypothesis="Test")

        create_experiment_dir(abreka_dir, "0001")
        save_experiment(exp, abreka_dir)

        exp.metrics = {"val": {"accuracy": 0.95}}
        exp.status = ExperimentStatus.COMPLETED
        save_experiment(exp, abreka_dir)

        loaded = load_experiment(abreka_dir, "0001")
        assert loaded.metrics["val"]["accuracy"] == 0.95
        assert loaded.status == ExperimentStatus.COMPLETED


class TestIndex:
    def test_update_entry(self, abreka_project):
        abreka_dir = abreka_project / "abreka"
        exp = Experiment(id="0001", hypothesis="Test")
        create_experiment_dir(abreka_dir, "0001")
        save_experiment(exp, abreka_dir)
        update_index_entry(abreka_dir, exp)

        index = load_index(abreka_dir)
        assert "0001" in index
        assert index["0001"]["hypothesis"] == "Test"

    def test_rebuild_index(self, abreka_project):
        abreka_dir = abreka_project / "abreka"

        for i in range(1, 4):
            eid = f"{i:04d}"
            exp = Experiment(id=eid, hypothesis=f"Experiment {i}")
            create_experiment_dir(abreka_dir, eid)
            save_experiment(exp, abreka_dir)

        # Wipe the index
        (abreka_dir / "index.json").write_text("{}\n")

        index = rebuild_index(abreka_dir)
        assert len(index) == 3
        assert "0001" in index
        assert "0003" in index


class TestBacklogId:
    def test_normalize(self):
        assert normalize_backlog_id("1") == "0001"
        assert normalize_backlog_id("42") == "0042"
        assert normalize_backlog_id("0001") == "0001"

    def test_normalize_strips_b_prefix(self):
        assert normalize_backlog_id("B1") == "0001"
        assert normalize_backlog_id("B0002") == "0002"
        assert normalize_backlog_id("b42") == "0042"

    def test_next_id_empty(self, abreka_project):
        abreka_dir = abreka_project / "abreka"
        assert next_backlog_id(abreka_dir) == "0001"

    def test_next_id_sequential(self, abreka_project):
        abreka_dir = abreka_project / "abreka"
        bl_dir = abreka_dir / "backlog"
        bl_dir.mkdir(exist_ok=True)
        (bl_dir / "0001.toml").write_text("")
        (bl_dir / "0002.toml").write_text("")
        assert next_backlog_id(abreka_dir) == "0003"


class TestBacklogPersistence:
    def test_create_and_load(self, abreka_project):
        abreka_dir = abreka_project / "abreka"
        item = BacklogItem(id="0001", title="Test idea")

        save_backlog_item(item, abreka_dir)
        loaded = load_backlog_item(abreka_dir, "0001")
        assert loaded.id == "0001"
        assert loaded.title == "Test idea"
        assert loaded.status == BacklogStatus.OPEN

    def test_update_and_reload(self, abreka_project):
        abreka_dir = abreka_project / "abreka"
        item = BacklogItem(id="0001", title="Test idea")

        save_backlog_item(item, abreka_dir)

        item.priority = 5
        item.status = BacklogStatus.RESOLVED
        item.resolved_exp = "0010"
        save_backlog_item(item, abreka_dir)

        loaded = load_backlog_item(abreka_dir, "0001")
        assert loaded.priority == 5
        assert loaded.status == BacklogStatus.RESOLVED
        assert loaded.resolved_exp == "0010"

    def test_delete(self, abreka_project):
        abreka_dir = abreka_project / "abreka"
        item = BacklogItem(id="0001", title="Test idea")
        save_backlog_item(item, abreka_dir)
        update_backlog_index_entry(abreka_dir, item)

        delete_backlog_item(abreka_dir, "0001")
        assert not (abreka_dir / "backlog" / "0001.toml").exists()


class TestBacklogIndex:
    def test_update_entry(self, abreka_project):
        abreka_dir = abreka_project / "abreka"
        item = BacklogItem(id="0001", title="Test idea", priority=3)
        save_backlog_item(item, abreka_dir)
        update_backlog_index_entry(abreka_dir, item)

        import json
        index = json.loads((abreka_dir / "backlog_index.json").read_text())
        assert "0001" in index
        assert index["0001"]["title"] == "Test idea"
        assert index["0001"]["priority"] == 3

    def test_rebuild_index(self, abreka_project):
        abreka_dir = abreka_project / "abreka"

        for i in range(1, 4):
            bid = f"{i:04d}"
            item = BacklogItem(id=bid, title=f"Idea {i}")
            save_backlog_item(item, abreka_dir)

        # Wipe the index
        (abreka_dir / "backlog_index.json").write_text("{}\n")

        index = rebuild_backlog_index(abreka_dir)
        assert len(index) == 3
        assert "0001" in index
        assert "0003" in index
