"""Tests for the TA1 (Interchange Acknowledgment) parser orchestrator."""
from __future__ import annotations

from datetime import date

import pytest

from cyclone.parsers.exceptions import CycloneParseError
from cyclone.parsers.parse_ta1 import parse_ta1_text


# A minimal accepted TA1 in canonical CCYYMMDD form (per X12 spec).
# ISA must be exactly 106 chars (15-char sender/receiver IDs).
ACCEPTED_CCYYMMDD = (
    "ISA*00*          *00*          *ZZ*SENDER         *ZZ*RECEIVER       "
    "*260520*1750*^*00501*000000001*0*P*:~"
    "TA1*000000001*20260520*1750*A*000*20260520~"
    "IEA*1*000000001~"
)

# A rejected TA1 with Colorado's YYMMDD date format (6 digits instead of 8).
# This is what FromHPE/tp11525703-837P_M019044596-…_TA1.x12 looks like in
# production — same shape but YYMMDD and ack_code=R, note_code=006.
REJECTED_YYMMDD = (
    "ISA*00*          *00*          *ZZ*COMEDASSISTPROG*ZZ*TP11525703     "
    "*260520*1750*^*00501*000000001*0*P*:~"
    "TA1*320293557*260520*2338*R*006~"
    "IEA*0*000000001~"
)

# Accepted variant with errors (E) — uncommon but spec-legal.
ACCEPTED_WITH_ERRORS = (
    "ISA*00*          *00*          *ZZ*SENDER         *ZZ*RECEIVER       "
    "*260520*1750*^*00501*000000001*0*P*:~"
    "TA1*000000001*20260520*1750*E*007*20260520~"
    "IEA*1*000000001~"
)


def test_parse_accepted_ccyymmdd_returns_a():
    """Spec-compliant TA1 with CCYYMMDD dates surfaces ack_code='A'."""
    result = parse_ta1_text(ACCEPTED_CCYYMMDD, input_file="accepted.ta1")
    assert result.ta1.ack_code == "A"
    assert result.ta1.control_number == "000000001"
    assert result.ta1.interchange_date == date(2026, 5, 20)
    assert result.ta1.interchange_time == "1750"
    assert result.ta1.note_code == "000"
    assert result.ta1.ack_generated_date == date(2026, 5, 20)
    # Envelope mirrors the ISA
    assert result.envelope.sender_id == "SENDER"
    assert result.envelope.receiver_id == "RECEIVER"
    assert result.envelope.control_number == "000000001"
    # Summary reflects the single ack
    assert result.summary.total_claims == 1
    assert result.summary.passed == 1
    assert result.summary.failed == 0


def test_parse_rejected_yymmdd_from_production():
    """Colorado's YYMMDD date format (6 digits) must still parse."""
    result = parse_ta1_text(REJECTED_YYMMDD, input_file="rejected.ta1")
    assert result.ta1.ack_code == "R"
    assert result.ta1.note_code == "006"
    assert result.ta1.interchange_date == date(2026, 5, 20), (
        "YYMMDD must be inferred as 20YY-MM-DD"
    )
    assert result.ta1.interchange_time == "2338"
    assert result.summary.passed == 0
    assert result.summary.failed == 1


def test_parse_accepted_with_errors_returns_e():
    """TA1 ack_code='E' (Accepted with errors) is preserved as the literal."""
    result = parse_ta1_text(ACCEPTED_WITH_ERRORS, input_file="with_errors.ta1")
    assert result.ta1.ack_code == "E"
    assert result.ta1.note_code == "007"


def test_source_batch_id_format():
    """source_batch_id must be 'TA1-<ISA13>' so the FK target is stable."""
    result = parse_ta1_text(ACCEPTED_CCYYMMDD, input_file="x.ta1")
    assert result.source_batch_id == "TA1-000000001"


def test_missing_isa_raises():
    """No ISA envelope at file start → CycloneParseError (caught by tokenize)."""
    text = "TA1*000000001*20260520*1750*A*000*20260520~"
    with pytest.raises(CycloneParseError, match="ISA"):
        parse_ta1_text(text, input_file="bad.ta1")


def test_missing_ta1_raises():
    """ISA but no TA1 segment → CycloneParseError."""
    text = (
        "ISA*00*          *00*          *ZZ*SENDER         *ZZ*RECEIVER       "
        "*260520*1750*^*00501*000000001*0*P*:~"
        "IEA*0*000000001~"
    )
    with pytest.raises(CycloneParseError, match="No TA1 segment"):
        parse_ta1_text(text, input_file="no_ta1.ta1")


def test_short_ta1_segment_uses_defaults():
    """A TA1 with fewer than 6 elements still parses with sensible defaults."""
    text = (
        "ISA*00*          *00*          *ZZ*SENDER         *ZZ*RECEIVER       "
        "*260520*1750*^*00501*000000001*0*P*:~"
        "TA1*000000001~"
        "IEA*0*000000001~"
    )
    result = parse_ta1_text(text, input_file="short.ta1")
    assert result.ta1.ack_code == "R"  # default to rejected when missing
    assert result.ta1.note_code is None
    assert result.ta1.interchange_date is None
    assert result.ta1.interchange_time is None


def test_garbage_date_returns_none_not_raises():
    """A garbage date in TA102 doesn't raise — it surfaces as None."""
    text = (
        "ISA*00*          *00*          *ZZ*SENDER         *ZZ*RECEIVER       "
        "*260520*1750*^*00501*000000001*0*P*:~"
        "TA1*000000001*not-a-date*1750*A*000~"
        "IEA*0*000000001~"
    )
    result = parse_ta1_text(text, input_file="bad_date.ta1")
    assert result.ta1.interchange_date is None
    assert result.ta1.ack_code == "A"  # the rest still parses