"""SP37 Task 2: add_record populates Batch.transaction_set_control_number.

The column should mirror the source 837 envelope's
``transaction_set_control_number`` (ST02) so the join-key update in
Task 3 can resolve 999 ``set_control_number`` (AK201) values back to
the right batch. The 835 path leaves the column NULL because the
field is an 837P-specific join key (835 remittances don't need to
resolve back to a source 837 — they're the response side of the loop).
"""
from __future__ import annotations

from datetime import date, datetime, timezone
from pathlib import Path

from cyclone import db as db_mod
from cyclone.db import Batch
from cyclone.parsers.parse_837 import parse as parse_837_text
from cyclone.parsers.payer import PayerConfig
from cyclone.store import store as cycl_store
from cyclone.store.records import BatchRecord837, BatchRecord835

FIXTURE_837 = Path(__file__).parent / "fixtures" / "minimal_837p.txt"
FIXTURE_835 = Path(__file__).parent / "fixtures" / "minimal_835.txt"


def test_add_record_populates_transaction_set_control_number_for_837():
    """parse_837 → add_record writes the parsed ST02 onto the Batch row.

    The fixture's ST02 (``991102977``) must round-trip into the new
    ``batches.transaction_set_control_number`` column. This is the
    join key Task 3's 999-ingest Pass 1 update relies on.
    """
    text = FIXTURE_837.read_text()
    parsed = parse_837_text(text, PayerConfig.co_medicaid())

    record = BatchRecord837(
        id="b-txn-cn-1",
        input_filename="minimal_837p.txt",
        parsed_at=datetime.now(timezone.utc),
        result=parsed,
    )
    cycl_store.add(record)

    with db_mod.SessionLocal()() as s:
        row = s.get(Batch, "b-txn-cn-1")
        assert row is not None
        assert row.transaction_set_control_number == parsed.envelope.transaction_set_control_number
        # Sanity: the fixture's ST02 must be the parsed-and-stored value.
        assert row.transaction_set_control_number == "991102977"


def test_envelope_model_exposes_transaction_set_control_number():
    """The Envelope Pydantic model carries the new field with a None default.

    Guards against a regression where someone removes the field from
    the model — the rest of the chain (parser + ORM + write path)
    silently degrades to None if the model loses the attribute.
    """
    from cyclone.parsers.models import Envelope

    env = Envelope(
        sender_id="S",
        receiver_id="R",
        control_number="000000001",
        transaction_date=date(2026, 1, 1),
    )
    assert env.transaction_set_control_number is None

    env2 = env.model_copy(update={"transaction_set_control_number": "0001"})
    assert env2.transaction_set_control_number == "0001"


def test_add_record_leaves_835_column_null():
    """835 batches don't carry an ST02 join key; column stays NULL.

    The shared ``Envelope`` class is used by both 837P and 835 parsers,
    but only ``parse_837`` populates ``transaction_set_control_number``.
    For 835 records the field is None and ``add_record`` writes NULL
    to the column — verified end-to-end via a real 835 ingest.
    """
    text = FIXTURE_835.read_text()
    from cyclone.parsers.parse_835 import parse as parse_835_text
    parsed835 = parse_835_text(text, payer_config=None)  # type: ignore[arg-type]

    # Sanity: the 835 envelope also has the field (shared model class),
    # but the parser doesn't populate it — so it must be None.
    assert parsed835.envelope.transaction_set_control_number is None

    record = BatchRecord835(
        id="b-txn-cn-835-1",
        input_filename="minimal_835.txt",
        parsed_at=datetime.now(timezone.utc),
        result=parsed835,
    )
    cycl_store.add(record)

    with db_mod.SessionLocal()() as s:
        row = s.get(Batch, "b-txn-cn-835-1")
        assert row is not None
        assert row.transaction_set_control_number is None
        # And the kind/kind round-trip is correct (sanity check that
        # we did exercise the 835 path, not the 837 path).
        assert row.kind == "835"