"""Handle a 277CA Claim Acknowledgment file (SP27 Task 4).

Lifted verbatim from ``scheduler.py:_handle_277ca``. The handler owns
its own DB session, dispatches to ``parse_277ca_text``, persists the
277CA ack row, applies 277CA rejections to matched claims via
``inbox_state.apply_277ca_rejections``, and emits
``claim.payer_rejected`` audit events for each newly-stamped claim.

The actor tag (``"277ca-parser-scheduler"``) is preserved so the
audit log keeps tracing back to the same source after extraction.
Both the FastAPI endpoint and the scheduler path (re)use this module
— the API migration drops the inline copy in Task 6.

``claim.rejected_after_remit`` audit emission (when a 277CA rejects
a claim that already has ``matched_remittance_id`` set) is deferred
to SP27 Task 13. Today the handler only emits ``claim.payer_rejected``.
"""
from __future__ import annotations

import json
import logging

from cyclone import db
from cyclone.audit_log import AuditEvent, append_event
from cyclone.handlers._ack_id import two77ca_synthetic_source_batch_id
from cyclone.inbox_state_277ca import apply_277ca_rejections
from cyclone.parsers.exceptions import CycloneParseError
from cyclone.parsers.parse_277ca import parse_277ca_text
from cyclone.store import store as cycl_store

log = logging.getLogger(__name__)


def handle(
    text: str,
    source_file: str,
) -> tuple[str, int]:
    """Parse a 277CA, persist ack + stamp payer-rejected claims.

    Args:
        text: Raw 277CA document bytes (decoded).
        source_file: Filename the 277CA came from.

    Returns:
        ``(parser_used, claim_count)`` tuple where ``claim_count`` is
        the number of STC statuses in the file (one per claim).

    Raises:
        ValueError: on parser-level failure (wraps CycloneParseError).

    SP25: the store (``cyclone.store.acks.add_277ca_ack``) owns the
    publish-from-store contract; the handler no longer needs an
    ``event_bus`` kwarg.
    """
    try:
        result = parse_277ca_text(text, input_file=source_file)
    except CycloneParseError as exc:
        raise ValueError(f"277CA parse error: {exc}") from exc

    icn = result.envelope.control_number
    synthetic_id = two77ca_synthetic_source_batch_id(icn)
    accepted = sum(
        1 for s in result.claim_statuses if s.classification == "accepted"
    )
    paid = sum(
        1 for s in result.claim_statuses if s.classification == "paid"
    )
    rejected = sum(
        1 for s in result.claim_statuses if s.classification == "rejected"
    )
    pended = sum(
        1 for s in result.claim_statuses if s.classification == "pended"
    )

    with db.SessionLocal()() as session:
        row = cycl_store.add_277ca_ack(
            source_batch_id=synthetic_id,
            control_number=icn,
            accepted_count=accepted,
            rejected_count=rejected,
            paid_count=paid,
            pended_count=pended,
            raw_json=json.loads(result.model_dump_json()),
        )
        def _lookup(pcn: str):
            return (
                session.query(db.Claim)
                .filter(db.Claim.patient_control_number == pcn)
                .first()
            )
        apply_result = apply_277ca_rejections(
            session, result, claim_lookup=_lookup, two77ca_id=row.id,
        )
        if apply_result.matched:
            for cid in apply_result.matched:
                append_event(session, AuditEvent(
                    event_type="claim.payer_rejected",
                    entity_type="claim",
                    entity_id=cid,
                    payload={"source_batch_id": synthetic_id, "277ca_id": row.id},
                    actor="277ca-parser-scheduler",
                ))
        session.commit()

    return ("parse_277ca", len(result.claim_statuses))
