from __future__ import annotations

from pathlib import Path

import pytest

import gateway.platforms.telegram_customer_bootstrap as bootstrap_module
from gateway.platforms.telegram_customer_bootstrap import (
    BootstrapError,
    RoomBootstrapStore,
)
from .new_customer_consent_testkit import awaiting_consent, consent_handoff


def test_bootstrap_append_journal_rejects_rewritten_snapshot(
    tmp_path: Path,
) -> None:
    store, session = awaiting_consent(tmp_path)
    journal_path = store.state_dir / "events.jsonl"
    assert journal_path.is_file()
    assert len(journal_path.read_text(encoding="utf-8").splitlines()) > 1

    current = bootstrap_module.json.loads(store.ledger_path.read_text(encoding="utf-8"))
    sessions = current["sessions"]
    sessions[0]["state"] = "FAILED"
    sessions[0]["generation"] += 99
    payload: dict[str, object] = {
        "schema": current["schema"],
        "sessions": sessions,
    }
    rewritten = {
        **payload,
        "digest": bootstrap_module._sha256(bootstrap_module._canonical_bytes(payload)),
    }
    store.ledger_path.write_bytes(bootstrap_module._canonical_bytes(rewritten))
    store.ledger_path.chmod(0o600)

    with pytest.raises(BootstrapError, match="terminal"):
        _ = RoomBootstrapStore(store.state_dir)


def test_bootstrap_append_journal_rejects_history_rewrite(
    tmp_path: Path,
) -> None:
    store, _ = awaiting_consent(tmp_path)
    journal_path = store.state_dir / "events.jsonl"
    rows = journal_path.read_text(encoding="utf-8").splitlines()
    rows[-1] = rows[-1].replace(
        '"sequence":',
        '"sequence":999,"ignored":',
        1,
    )
    journal_path.write_text("\n".join(rows) + "\n", encoding="utf-8")
    journal_path.chmod(0o600)

    with pytest.raises(BootstrapError, match="journal"):
        _ = RoomBootstrapStore(store.state_dir)


def test_bootstrap_append_journal_rejects_valid_tail_truncation(
    tmp_path: Path,
) -> None:
    store, _ = awaiting_consent(tmp_path)
    journal_path = store.state_dir / "events.jsonl"
    rows = journal_path.read_text(encoding="utf-8").splitlines()
    assert len(rows) > 1
    journal_path.write_text("\n".join(rows[:-1]) + "\n", encoding="utf-8")
    journal_path.chmod(0o600)

    with pytest.raises(BootstrapError, match="terminal"):
        _ = RoomBootstrapStore(store.state_dir)


def test_bootstrap_append_journal_recovers_forward_from_projection_crash(
    tmp_path: Path,
) -> None:
    store, session = awaiting_consent(tmp_path)
    rows = (store.state_dir / "events.jsonl").read_text(encoding="utf-8").splitlines()
    previous_document = bootstrap_module.json.loads(rows[-2])["ledger"]
    store.ledger_path.write_bytes(bootstrap_module._canonical_bytes(previous_document))
    store.ledger_path.chmod(0o600)

    recovered = RoomBootstrapStore(store.state_dir)
    assert recovered.get(session.session_id) == session


def test_bootstrap_append_journal_restores_missing_projection(
    tmp_path: Path,
) -> None:
    store, session = awaiting_consent(tmp_path)
    store.ledger_path.unlink()

    recovered = RoomBootstrapStore(store.state_dir)
    assert recovered.get(session.session_id) == session


def test_legacy_ledger_only_state_migrates_on_next_mutation(
    tmp_path: Path,
) -> None:
    source, session = awaiting_consent(tmp_path / "source")
    legacy_dir = tmp_path / "legacy"
    legacy_dir.mkdir(mode=0o700)
    (legacy_dir / "ledger.json").write_bytes(source.ledger_path.read_bytes())
    (legacy_dir / "ledger.json").chmod(0o600)
    (legacy_dir / "ledger.lock").touch(mode=0o600)

    legacy = RoomBootstrapStore(legacy_dir)
    assert legacy.get(session.session_id) == session
    _ = legacy.bind_consent_handoff(
        session.session_id,
        expected_generation=session.generation,
        handoff=consent_handoff(session),
    )

    assert legacy.journal_path.is_file()
    assert legacy.journal_head_path.is_file()
    assert len(legacy.journal_path.read_text(encoding="utf-8").splitlines()) == 2


def test_bootstrap_terminal_head_rejects_deleted_journal(
    tmp_path: Path,
) -> None:
    store, _ = awaiting_consent(tmp_path)
    store.journal_path.unlink()

    with pytest.raises(BootstrapError, match="required state"):
        _ = RoomBootstrapStore(store.state_dir)


def test_bootstrap_terminal_head_rejects_deleted_journal_and_projection(
    tmp_path: Path,
) -> None:
    store, _ = awaiting_consent(tmp_path)
    store.journal_path.unlink()
    store.ledger_path.unlink()

    with pytest.raises(BootstrapError, match="required state"):
        _ = RoomBootstrapStore(store.state_dir)


def test_bootstrap_migration_marker_rejects_deleted_journal_and_head(
    tmp_path: Path,
) -> None:
    store, _ = awaiting_consent(tmp_path)
    store.journal_path.unlink()
    store.journal_head_path.unlink()

    with pytest.raises(BootstrapError, match="required state"):
        _ = RoomBootstrapStore(store.state_dir)


def test_bootstrap_migration_marker_rejects_total_state_reset(
    tmp_path: Path,
) -> None:
    store, _ = awaiting_consent(tmp_path)
    store.journal_path.unlink()
    store.journal_head_path.unlink()
    store.ledger_path.unlink()

    with pytest.raises(BootstrapError, match="required state"):
        _ = RoomBootstrapStore(store.state_dir)


def test_current_targeted_session_without_first_claim_field_still_loads(
    tmp_path: Path,
) -> None:
    _store, session = awaiting_consent(tmp_path)
    persisted = bootstrap_module._session_to_dict(session)
    persisted.pop("first_claim")

    loaded = bootstrap_module._session_from_mapping(persisted)

    assert loaded.first_claim is False
    assert loaded.customer_draft == session.customer_draft


