"""Tests for fw_server.persistence — auto-save/restore/rotation."""

from __future__ import annotations

import json
from pathlib import Path

import pytest

from hcp.protocol import HCPProtocol
from fw_server.persistence import (
    MAX_CHECKPOINTS,
    STATE_FILENAME,
    get_persistence_info,
    restore_hcp_state,
    save_hcp_state,
    _rotate,
)


SAMPLE_TEXT = "Bismillahirrahmanirrahim.  Her şey Allah'ın isimlerine dayanır."


@pytest.fixture()
def state_dir(tmp_path: Path) -> Path:
    d = tmp_path / ".hcp_state"
    d.mkdir()
    return d


@pytest.fixture()
def proto() -> HCPProtocol:
    return HCPProtocol()


@pytest.fixture()
def seeded_proto() -> HCPProtocol:
    p = HCPProtocol()
    p.ingest(SAMPLE_TEXT)
    return p


# ── save ──────────────────────────────────────────────────────────────

class TestSave:
    def test_save_returns_none_when_not_ready(self, state_dir, proto):
        """Empty protocol has nothing to save."""
        assert save_hcp_state(state_dir, proto) is None

    def test_save_writes_json(self, state_dir, seeded_proto):
        path = save_hcp_state(state_dir, seeded_proto)
        assert path is not None
        assert path.exists()
        data = json.loads(path.read_text(encoding="utf-8"))
        assert "schema_version" in data
        assert "chunks" in data

    def test_save_creates_directory(self, tmp_path, seeded_proto):
        """state_dir doesn't need to pre-exist."""
        nested = tmp_path / "deep" / "nested" / ".hcp_state"
        path = save_hcp_state(nested, seeded_proto)
        assert path is not None
        assert path.exists()

    def test_save_overwrites_previous(self, state_dir, seeded_proto):
        save_hcp_state(state_dir, seeded_proto)
        path = save_hcp_state(state_dir, seeded_proto)
        assert path is not None
        # Exactly one canonical file + one backup
        backups = list(state_dir.glob("hcp_state.*.json"))
        assert len(backups) == 1


# ── restore ───────────────────────────────────────────────────────────

class TestRestore:
    def test_restore_returns_false_no_checkpoint(self, state_dir, proto):
        assert restore_hcp_state(state_dir, proto) is False

    def test_restore_roundtrip(self, state_dir, seeded_proto):
        save_hcp_state(state_dir, seeded_proto)
        n_original = len(seeded_proto._chunks)

        fresh = HCPProtocol()
        assert restore_hcp_state(state_dir, fresh) is True
        assert len(fresh._chunks) == n_original
        assert fresh.is_ready

    def test_restore_corrupt_json(self, state_dir, proto):
        (state_dir / STATE_FILENAME).write_text("NOT JSON", encoding="utf-8")
        assert restore_hcp_state(state_dir, proto) is False

    def test_restore_bad_schema(self, state_dir, proto):
        bad = json.dumps({"schema_version": 999})
        (state_dir / STATE_FILENAME).write_text(bad, encoding="utf-8")
        assert restore_hcp_state(state_dir, proto) is False


# ── rotation ──────────────────────────────────────────────────────────

class TestRotation:
    def test_rotate_creates_backup(self, state_dir):
        canonical = state_dir / STATE_FILENAME
        canonical.write_text("{}", encoding="utf-8")
        _rotate(canonical)
        backups = list(state_dir.glob("hcp_state.*.json"))
        assert len(backups) == 1

    def test_rotate_prunes_beyond_max(self, state_dir):
        canonical = state_dir / STATE_FILENAME
        # Create MAX_CHECKPOINTS + 3 extra backups
        for i in range(MAX_CHECKPOINTS + 3):
            canonical.write_text(f'{{"i": {i}}}', encoding="utf-8")
            _rotate(canonical)
        backups = list(state_dir.glob("hcp_state.*.json"))
        assert len(backups) <= MAX_CHECKPOINTS

    def test_rotate_noop_when_no_file(self, state_dir):
        canonical = state_dir / STATE_FILENAME
        _rotate(canonical)  # should not raise


# ── diagnostics ───────────────────────────────────────────────────────

class TestPersistenceInfo:
    def test_info_disabled(self):
        info = get_persistence_info(None)
        assert info["persistence_enabled"] is False

    def test_info_no_checkpoint(self, state_dir):
        info = get_persistence_info(state_dir)
        assert info["persistence_enabled"] is True
        assert info["checkpoint_exists"] is False

    def test_info_with_checkpoint(self, state_dir, seeded_proto):
        save_hcp_state(state_dir, seeded_proto)
        info = get_persistence_info(state_dir)
        assert info["checkpoint_exists"] is True
        assert info["checkpoint_size_bytes"] > 0
        assert "checkpoint_modified" in info
        assert info["n_backups"] == 0  # first save has no backup yet
