from __future__ import annotations

import json
from pathlib import Path

import pytest

from recoil.pipeline.tools.autonomy import claim_ledger


def _isolate(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> Path:
    ledger = tmp_path / "state" / "claim-ledger.jsonl"
    monkeypatch.setattr(claim_ledger, "CLAIM_LEDGER", ledger)
    return ledger


def _read_jsonl(path: Path) -> list[dict]:
    return [
        json.loads(line)
        for line in path.read_text(encoding="utf-8").splitlines()
        if line.strip()
    ]


def test_first_claim_ok_and_second_active_claim_refused(monkeypatch, tmp_path):
    ledger = _isolate(monkeypatch, tmp_path)

    first = claim_ledger.claim("issue-1", "REC-1", "run-1", "2026-06-06-evening")
    second = claim_ledger.claim("issue-1", "REC-1", "run-2", "2026-06-06-evening")

    assert first is not None
    assert first["record_id"] == "issue-1:1:run-1"
    assert first["attempt"] == 1
    assert first["state"] == "active"
    assert first["issue_identifier"] == "REC-1"
    assert first["night_id"] == "2026-06-06-evening"
    assert first["host"]
    assert second is None
    assert claim_ledger.active_claim("issue-1") == first
    assert _read_jsonl(ledger) == [first]


def test_release_then_reclaim_increments_attempt(monkeypatch, tmp_path):
    ledger = _isolate(monkeypatch, tmp_path)

    first = claim_ledger.claim("issue-1", "REC-1", "run-1", "night")
    released = claim_ledger.release("issue-1", "run-1", "completed")
    second = claim_ledger.claim("issue-1", "REC-1", "run-2", "night")

    assert released is not None
    assert released["state"] == "completed"
    assert released["record_id"] == "issue-1:1:run-1"
    assert released["released_at"]
    assert second is not None
    assert second["attempt"] == 2
    assert second["record_id"] == "issue-1:2:run-2"
    assert claim_ledger.active_claim("issue-1") == second
    assert [row["state"] for row in _read_jsonl(ledger)] == [
        "active",
        "completed",
        "active",
    ]
    assert first["attempt"] == 1


def test_release_requires_active_matching_run(monkeypatch, tmp_path):
    _isolate(monkeypatch, tmp_path)
    assert claim_ledger.claim("issue-1", "REC-1", "run-1", "night")

    assert claim_ledger.release("issue-1", "wrong-run", "failed", "sig") is None

    with pytest.raises(ValueError):
        claim_ledger.release("issue-1", "run-1", "active")


def test_consecutive_failures_counts_same_signature_streak(monkeypatch, tmp_path):
    _isolate(monkeypatch, tmp_path)

    assert claim_ledger.claim("issue-1", "REC-1", "run-1", "night")
    assert claim_ledger.release("issue-1", "run-1", "failed", "same")
    assert claim_ledger.claim("issue-1", "REC-1", "run-2", "night")
    assert claim_ledger.release("issue-1", "run-2", "failed", "same")
    assert claim_ledger.consecutive_failures("issue-1") == 2

    assert claim_ledger.claim("issue-1", "REC-1", "run-3", "night")
    assert claim_ledger.release("issue-1", "run-3", "failed", "different")
    assert claim_ledger.consecutive_failures("issue-1") == 1

    assert claim_ledger.claim("issue-1", "REC-1", "run-4", "night")
    assert claim_ledger.release("issue-1", "run-4", "released")
    assert claim_ledger.consecutive_failures("issue-1") == 0
