"""Tests for the /api/parse-ta1 endpoint and the ta1_acks list/detail endpoints."""
from __future__ import annotations

from pathlib import Path

import pytest
from fastapi.testclient import TestClient

from cyclone.api import app
from cyclone.store import store as global_store
from cyclone import db


ACCEPTED = (
    "ISA*00*          *00*          *ZZ*SENDER         *ZZ*RECEIVER       "
    "*260520*1750*^*00501*000000001*0*P*:~"
    "TA1*000000001*20260520*1750*A*000*20260520~"
    "IEA*1*000000001~"
)

REJECTED = (
    "ISA*00*          *00*          *ZZ*COMEDASSISTPROG*ZZ*TP11525703     "
    "*260520*1750*^*00501*000000001*0*P*:~"
    "TA1*320293557*260520*2338*R*006~"
    "IEA*0*000000001~"
)


@pytest.fixture(autouse=True)
def clear_store():
    """Reset the module-level store before and after each test."""
    with global_store._lock:
        global_store._batches.clear()
    yield
    with global_store._lock:
        global_store._batches.clear()


@pytest.fixture
def client() -> TestClient:
    return TestClient(app)


def test_parse_ta1_endpoint_happy_path(client: TestClient):
    """Accepted TA1 → 200 with parsed envelope + persisted ta1 row."""
    resp = client.post(
        "/api/parse-ta1",
        files={"file": ("accepted.ta1", ACCEPTED, "text/plain")},
        headers={"Accept": "application/json"},
    )
    assert resp.status_code == 200, resp.text
    body = resp.json()
    ta1 = body["ta1"]
    assert ta1["ack_code"] == "A"
    assert ta1["control_number"] == "000000001"
    assert ta1["note_code"] == "000"
    assert ta1["interchange_date"] == "2026-05-20"
    assert ta1["source_batch_id"] == "TA1-000000001"
    # Round-trip text present and well-formed.
    assert ta1["raw_ta1_text"].startswith("TA1*")
    assert ta1["raw_ta1_text"].endswith("~")
    # Parsed shape mirrors the model.
    assert body["parsed"]["ta1"]["ack_code"] == "A"


def test_parse_ta1_endpoint_rejected_persists(client: TestClient):
    """Rejected TA1 (Colorado YYMMDD format) → 200 + R ack_code in DB."""
    resp = client.post(
        "/api/parse-ta1",
        files={"file": ("rejected.ta1", REJECTED, "text/plain")},
        headers={"Accept": "application/json"},
    )
    assert resp.status_code == 200, resp.text
    body = resp.json()
    assert body["ta1"]["ack_code"] == "R"
    assert body["ta1"]["note_code"] == "006"
    # Persisted to DB
    list_resp = client.get("/api/ta1-acks?limit=10", headers={"Accept": "application/json"})
    assert list_resp.status_code == 200, list_resp.text
    list_body = list_resp.json()
    assert list_body["total"] == 1
    assert list_body["items"][0]["ack_code"] == "R"


def test_parse_ta1_endpoint_empty_file_returns_400(client: TestClient):
    """Empty upload → 400 (never 500)."""
    resp = client.post(
        "/api/parse-ta1",
        files={"file": ("empty.ta1", b"", "text/plain")},
        headers={"Accept": "application/json"},
    )
    assert resp.status_code == 400


def test_parse_ta1_endpoint_malformed_returns_400(client: TestClient):
    """No TA1 segment → 400 with parse-error detail."""
    text = (
        "ISA*00*          *00*          *ZZ*SENDER         *ZZ*RECEIVER       "
        "*260520*1750*^*00501*000000001*0*P*:~"
        "IEA*0*000000001~"
    )
    resp = client.post(
        "/api/parse-ta1",
        files={"file": ("bad.ta1", text, "text/plain")},
        headers={"Accept": "application/json"},
    )
    assert resp.status_code == 400
    body = resp.json()
    assert "TA1" in body["detail"]


def test_parse_ta1_endpoint_rejects_missing_file(client: TestClient):
    """FastAPI File(...) with no default → 422."""
    resp = client.post("/api/parse-ta1")
    assert resp.status_code == 422


def test_get_ta1_ack_detail_regenerates_segment(client: TestClient):
    """GET /api/ta1-acks/{id} includes the raw TA1 segment rebuilt from the row."""
    resp = client.post(
        "/api/parse-ta1",
        files={"file": ("accepted.ta1", ACCEPTED, "text/plain")},
        headers={"Accept": "application/json"},
    )
    assert resp.status_code == 200
    ta1_id = resp.json()["ta1"]["id"]

    detail_resp = client.get(f"/api/ta1-acks/{ta1_id}", headers={"Accept": "application/json"})
    assert detail_resp.status_code == 200, detail_resp.text
    body = detail_resp.json()
    assert body["id"] == ta1_id
    assert body["raw_ta1_text"].startswith("TA1*000000001*")
    assert body["raw_ta1_text"].rstrip().endswith("~")
    # raw_json carries the full ParseResultTa1 for the detail view.
    assert body["raw_json"]["ta1"]["ack_code"] == "A"


def test_get_ta1_ack_404_for_missing(client: TestClient):
    """GET /api/ta1-acks/{id} returns 404 for a missing id."""
    resp = client.get("/api/ta1-acks/9999", headers={"Accept": "application/json"})
    assert resp.status_code == 404


def test_list_ta1_acks_empty(client: TestClient):
    """Empty DB → empty list with total=0."""
    resp = client.get("/api/ta1-acks", headers={"Accept": "application/json"})
    assert resp.status_code == 200
    body = resp.json()
    assert body == {"total": 0, "items": []}


def test_list_ta1_acks_newest_first(client: TestClient):
    """Multiple uploads: list returns rows newest first (by auto-increment id desc)."""
    for i, payload in enumerate([ACCEPTED, REJECTED]):
        resp = client.post(
            "/api/parse-ta1",
            files={"file": (f"file{i}.ta1", payload, "text/plain")},
            headers={"Accept": "application/json"},
        )
        assert resp.status_code == 200, resp.text
    resp = client.get("/api/ta1-acks", headers={"Accept": "application/json"})
    items = resp.json()["items"]
    assert len(items) == 2
    # The REJECTED (uploaded second) is first.
    assert items[0]["ack_code"] == "R"
    assert items[1]["ack_code"] == "A"