"""HCPF X12 File Naming Standards helpers.

SP9. Source-of-truth spec:
https://hcpf.colorado.gov/tp-x12-filenaming (HCPF X12 File Naming Standards Quick Guide)

Outbound (we send):
    {tpid}-{transaction_type}-{yyyymmddhhmmssSSS_MT}-1of1.{ext}
    Example: 11525703-837P-20260620132243505-1of1.x12

Inbound (HPE sends to our ToHPE):
    TP{tpid}-{orig_tx}_M{tracking}-{ts}-1of1_{file_type}.x12
    Example: TP11525703-837P_M019048402-20260520231513488-1of1_999.x12

Both use Mountain Time (MT) timestamps with 17-digit millisecond precision
(yyyymmddhhmmssSSS = 4+2+2+2+2+2+3 = 17 digits). Sequence is always "1of1"
(the only accepted value per HCPF).
"""

from __future__ import annotations

import re
from datetime import datetime
from zoneinfo import ZoneInfo

from cyclone.providers import InboundFilename

# ---------------------------------------------------------------------------
# Regexes
# ---------------------------------------------------------------------------

# Outbound: 11525703-837P-20260620132243505-1of1.x12
#   - tpid:   1+ digits
#   - tx:     1+ alnum
#   - ts:     17 digits (yyyymmddhhmmssSSS)
#   - seq:    literal "1of1"
#   - ext:    1+ alnum
OUTBOUND_RE = re.compile(
    r"^(?P<tpid>\d+)-(?P<tx>[A-Z0-9]+)-(?P<ts>\d{17})-1of1\.(?P<ext>[A-Za-z0-9]+)$"
)

# Inbound: TP11525703-837P_M019048402-20260520231513488-1of1_999.x12
#   - tpid:    1+ digits (inside TP<...>)
#   - orig_tx: 1+ alnum
#   - track:   M + 1+ alnum (e.g. M019048402) — the M is part of the
#     tracking value, not a separator.
#   - ts:      17 digits
#   - seq:     literal "1of1"
#   - ft:      1+ alnum (e.g. 999, TA1, 271, 277, 277CA, 820, 834, 835, ENCR)
INBOUND_RE = re.compile(
    r"^TP(?P<tpid>\d+)-(?P<orig_tx>[A-Z0-9]+)_(?P<tracking>M[A-Z0-9]+)"
    r"-(?P<ts>\d{17})-1of1_(?P<file_type>[A-Z0-9]+)\.(?P<ext>x12)$"
)

ALLOWED_FILE_TYPES = frozenset({
    "999", "TA1", "270", "271", "276", "277", "277CA", "278",
    "820", "834", "835", "ENCR",
})

# ---------------------------------------------------------------------------
# Outbound
# ---------------------------------------------------------------------------


def build_outbound_filename(
    tpid: str,
    tx: str,
    *,
    ext: str = "x12",
    now_mt: datetime | None = None,
) -> str:
    """Build an outbound HCPF filename.

    Args:
        tpid: Trading Partner ID (e.g. "11525703"). Must be digits.
        tx: Transaction type (e.g. "837P", "835").
        ext: File extension (default "x12").
        now_mt: Override for the timestamp (must be timezone-aware, MT or
            anything ZoneInfo can normalize to MT). When None, the current
            time in ``America/Denver`` is used.

    Returns:
        Filename like "11525703-837P-20260620132243505-1of1.x12"

    Raises:
        ValueError: If tpid is non-numeric, tx contains invalid chars, or
            now_mt is naive.
    """
    if not tpid.isdigit():
        raise ValueError(f"tpid must be digits, got {tpid!r}")
    if not re.match(r"^[A-Z0-9]+$", tx):
        raise ValueError(f"tx must be uppercase alnum, got {tx!r}")
    if now_mt is None:
        now_mt = datetime.now(ZoneInfo("America/Denver"))
    elif now_mt.tzinfo is None:
        raise ValueError("now_mt must be timezone-aware")
    else:
        now_mt = now_mt.astimezone(ZoneInfo("America/Denver"))

    # Format: yyyymmddhhmmssSSS — 17 digits total
    ts = now_mt.strftime("%Y%m%d%H%M%S") + f"{now_mt.microsecond // 1000:03d}"
    assert len(ts) == 17
    return f"{tpid}-{tx}-{ts}-1of1.{ext}"


# ---------------------------------------------------------------------------
# Inbound
# ---------------------------------------------------------------------------


def parse_inbound_filename(name: str) -> InboundFilename:
    """Parse an inbound HCPF filename.

    Args:
        name: Filename like "TP11525703-837P_M019048402-20260520231513488-1of1_999.x12"

    Returns:
        InboundFilename with tpid, orig_tx, tracking, ts, file_type, ext.

    Raises:
        ValueError: If the filename doesn't match the HCPF inbound format.
    """
    m = INBOUND_RE.match(name)
    if not m:
        raise ValueError(f"Not a valid HCPF inbound filename: {name!r}")
    file_type = m.group("file_type")
    if file_type not in ALLOWED_FILE_TYPES:
        raise ValueError(
            f"file_type {file_type!r} not in allowed HCPF set: {sorted(ALLOWED_FILE_TYPES)}"
        )
    return InboundFilename(
        tpid=m.group("tpid"),
        orig_tx=m.group("orig_tx"),
        tracking=m.group("tracking"),
        ts=m.group("ts"),
        file_type=file_type,
        ext=m.group("ext"),
    )


# ---------------------------------------------------------------------------
# Validation helpers
# ---------------------------------------------------------------------------


def is_outbound_filename(name: str) -> bool:
    """True if the given string matches the HCPF outbound filename regex."""
    return OUTBOUND_RE.match(name) is not None


def is_inbound_filename(name: str) -> bool:
    """True if the given string matches the HCPF inbound filename regex."""
    return INBOUND_RE.match(name) is not None
