"""SP14 — Payer-Rejected acknowledge endpoint tests.

The endpoint marks payer-rejected claims as acknowledged so the
working-surface lane query filters them out. The original
payer_rejected_* fields stay intact (SP11 audit).
"""
from __future__ import annotations

import pytest

from cyclone import db
from cyclone.db import Claim, ClaimState
from cyclone.audit_log import verify_chain


@pytest.fixture(autouse=True)
def _setup(tmp_path, monkeypatch):
    monkeypatch.setenv("CYCLONE_DB_URL", f"sqlite:///{tmp_path}/test.db")
    db._reset_for_tests()
    db.init_db()


def _make_claim(claim_id: str, *, payer_rejected: bool = True) -> None:
    """Insert a minimal claim with optional payer_rejected markers."""
    from datetime import datetime, timezone
    from cyclone.db import Batch
    with db.SessionLocal()() as session:
        batch = Batch(
            id=f"b-{claim_id}",
            kind="837p",
            input_filename="t.x12",
            parsed_at=datetime.now(timezone.utc),
        )
        session.add(batch)
        session.flush()
        claim = Claim(
            id=claim_id,
            batch_id=batch.id,
            patient_control_number=claim_id,
            state=ClaimState.SUBMITTED,
            charge_amount=100,
        )
        if payer_rejected:
            now = datetime.now(timezone.utc)
            claim.payer_rejected_at = now
            claim.payer_rejected_reason = "invalid diagnosis code"
            claim.payer_rejected_status_code = "A7"
        session.add(claim)
        session.commit()


def test_acknowledge_marks_unacknowledged_claim():
    _make_claim("C1", payer_rejected=True)
    from fastapi.testclient import TestClient
    from cyclone.api import app
    with TestClient(app) as client:
        r = client.post(
            "/api/inbox/payer-rejected/acknowledge",
            json={"claim_ids": ["C1"], "actor": "operator"},
        )
    assert r.status_code == 200, r.text
    body = r.json()
    assert body["ok"] is True
    assert body["transitioned"] == 1
    assert body["already_acked"] == 0
    assert body["not_found"] == 0
    assert body["not_rejected"] == 0

    with db.SessionLocal()() as session:
        # Migration 0014: composite PK — filter-by-id returns the single
        # claim with this id (no cross-batch duplicates in this fixture).
        c = session.query(Claim).filter(Claim.id == "C1").first()
        assert c.payer_rejected_acknowledged_at is not None
        assert c.payer_rejected_acknowledged_actor == "operator"
        # Original fields stay intact for audit.
        assert c.payer_rejected_at is not None
        assert c.payer_rejected_status_code == "A7"
        assert c.payer_rejected_reason == "invalid diagnosis code"


def test_acknowledge_is_idempotent():
    _make_claim("C1", payer_rejected=True)
    from fastapi.testclient import TestClient
    from cyclone.api import app
    with TestClient(app) as client:
        r1 = client.post(
            "/api/inbox/payer-rejected/acknowledge",
            json={"claim_ids": ["C1"]},
        )
        assert r1.json()["transitioned"] == 1
        r2 = client.post(
            "/api/inbox/payer-rejected/acknowledge",
            json={"claim_ids": ["C1"]},
        )
        assert r2.json()["transitioned"] == 0
        assert r2.json()["already_acked"] == 1


def test_acknowledge_skips_non_payer_rejected_claims():
    _make_claim("C1", payer_rejected=False)
    from fastapi.testclient import TestClient
    from cyclone.api import app
    with TestClient(app) as client:
        r = client.post(
            "/api/inbox/payer-rejected/acknowledge",
            json={"claim_ids": ["C1"]},
        )
        assert r.status_code == 200
        body = r.json()
        assert body["transitioned"] == 0
        assert body["not_rejected"] == 1


def test_acknowledge_counts_missing_ids():
    from fastapi.testclient import TestClient
    from cyclone.api import app
    with TestClient(app) as client:
        r = client.post(
            "/api/inbox/payer-rejected/acknowledge",
            json={"claim_ids": ["nope"]},
        )
        assert r.status_code == 200
        body = r.json()
        assert body["transitioned"] == 0
        assert body["not_found"] == 1


def test_acknowledge_empty_claim_ids_400():
    from fastapi.testclient import TestClient
    from cyclone.api import app
    with TestClient(app) as client:
        r = client.post(
            "/api/inbox/payer-rejected/acknowledge",
            json={"claim_ids": []},
        )
        assert r.status_code == 400


def test_acknowledge_writes_audit_event():
    _make_claim("C1", payer_rejected=True)
    from fastapi.testclient import TestClient
    from cyclone.api import app
    with TestClient(app) as client:
        r = client.post(
            "/api/inbox/payer-rejected/acknowledge",
            json={"claim_ids": ["C1"], "actor": "alice"},
        )
        assert r.status_code == 200

    # SP11: the chain is intact and includes the new event.
    with db.SessionLocal()() as session:
        result = verify_chain(session)
        assert result.ok, f"chain broken: {result.reason} at {result.first_bad_id}"

    # And the event itself is queryable.
    import json as _json
    from cyclone.db import AuditLog
    with db.SessionLocal()() as session:
        events = (
            session.query(AuditLog)
            .filter(AuditLog.event_type == "claim.payer_rejected_acknowledged")
            .all()
        )
        assert len(events) == 1
        e = events[0]
        assert e.entity_type == "claim"
        assert e.entity_id == "C1"
        assert e.actor == "alice"
        payload = _json.loads(e.payload_json) if e.payload_json else {}
        assert payload["payer_rejected_status_code"] == "A7"
