"""THE M7 CC BYTE-DIFF GATE (09-build-plan M7). Two cross-validations
against the LEGACY code of record (/srv/apps/work/lib/cc-expenses.ts),
executed through the old app's tsx (scripts/ts_cc_parse.mjs /
ts_cc_export.mjs), on the REAL migrated data:

  1. PARSER — the 102 real ``cc_expense_lines.raw_line`` values run
     through the Python ``parse_cc_line`` produce field-identical output
     to the legacy ``parseCcLine``.
  2. EXPORT — for every real batch, the NEW service paste block
     (cc.paste_block over the migrated assigned lines) is BYTE-IDENTICAL
     to the legacy ``formatCcPasteBlock`` fed the same assigned rows.

The oracle data comes from the scratch clone's MIGRATED tables (the same
real snapshot the transforms produced), so this cross-validates the whole
chain: migration, the assignment SQL, tenant-settings literal, and byte
formatting — exactly the M6 paste-block gate discipline, for CC.
"""

import json
import subprocess
from decimal import Decimal
from pathlib import Path

import pytest
from sqlalchemy import text

from app.services.cc import service as cc_svc
from tests.cc.conftest import raw_lines

TSX = Path("/srv/apps/work/node_modules/.bin/tsx")
ROOT = Path(__file__).resolve().parents[2]
PARSE_RUNNER = ROOT / "scripts" / "ts_cc_parse.mjs"
EXPORT_RUNNER = ROOT / "scripts" / "ts_cc_export.mjs"


def _tsx(runner: Path, payload: dict, tmp_path: Path) -> dict:
    if not TSX.exists():
        pytest.fail(
            f"the CC byte-diff gate needs the legacy tsx runner at {TSX} —"
            " run make check on the host (the old app's node_modules)"
        )
    src = tmp_path / "payload.json"
    src.write_text(json.dumps(payload), encoding="utf-8")
    proc = subprocess.run(
        [str(TSX), str(runner), str(src)],
        capture_output=True,
        text=True,
        timeout=120,
    )
    assert proc.returncode == 0, f"tsx oracle failed: {proc.stderr[-2000:]}"
    return json.loads(proc.stdout)


def _money(raw) -> str | None:
    return None if raw is None else f"{Decimal(str(raw)):.2f}"


def test_gate_parser_byte_identical_on_real_lines(cc_engine, s, tmp_path):
    lines = raw_lines(s)
    assert len(lines) >= 100, f"expected the real migrated statement lines, got {len(lines)}"

    from app.services.cc.parse import parse_cc_line

    py_rows = []
    for raw in lines:
        p = parse_cc_line(raw)
        py_rows.append(
            None
            if p is None
            else {
                "raw_line": p.raw_line,
                "expense_date": p.expense_date,
                "description": p.description,
                "amount": _money(p.amount),
                "balance": _money(p.balance),
            }
        )

    oracle = _tsx(PARSE_RUNNER, {"raw_lines": lines}, tmp_path)["rows"]
    assert len(oracle) == len(py_rows)
    divergences = [
        (i, oracle[i], py_rows[i])
        for i in range(len(oracle))
        if oracle[i] != py_rows[i]
    ]
    assert not divergences, f"parser divergence on {len(divergences)} line(s): {divergences[:3]}"
    # only meaningful if it exercised real content
    assert sum(1 for r in py_rows if r and r["amount"]) > 0


def test_gate_export_byte_identical_for_all_real_batches(cc_engine, s, tmp_path):
    batches = s.execute(
        text("SELECT id, name FROM cc_expense_batches ORDER BY created_at")
    ).all()
    assert len(batches) >= 1, "expected the real migrated CC batches"

    from tests.cc.conftest import actor_named

    rian = actor_named(s, "rian")

    total_lines = 0
    for batch_id, name in batches:
        new_text = cc_svc.paste_block(s, rian, batch_id)["text"]

        # Legacy oracle rows: the batch's exportable assigned lines in the
        # SAME (line_index) order the service uses; amount as a NUMBER.
        rows = s.execute(
            text(
                "SELECT cl.name AS client, pr.name AS project,"
                " l.assignment_description, l.amount, l.expense_date, l.category"
                " FROM cc_expense_lines l"
                " JOIN projects pr ON pr.id = l.project_id"
                " JOIN parties cl ON cl.id = pr.client_party_id"
                " WHERE l.batch_id = :bid AND l.project_id IS NOT NULL"
                "   AND l.expense_date IS NOT NULL AND l.amount IS NOT NULL"
                " ORDER BY l.line_index"
            ),
            {"bid": str(batch_id)},
        ).all()
        oracle_rows = [
            {
                "client": r.client,
                "project": r.project,
                "assignment_description": r.assignment_description or "",
                "amount_usd": float(r.amount),
                "expense_date": r.expense_date.isoformat(),
                "category": r.category or "",
            }
            for r in rows
            if r.client and r.project
        ]
        oracle = _tsx(EXPORT_RUNNER, {"rows": oracle_rows}, tmp_path)
        assert new_text == oracle["text"], _diff(name, new_text, oracle["text"])
        total_lines += len(oracle_rows)

    assert total_lines > 0, "the export gate must exercise real assigned lines"


def _diff(name: str, new_text: str, old_text: str) -> str:
    for i, (a, b) in enumerate(zip(new_text.split("\n"), old_text.split("\n"), strict=False)):
        if a != b:
            return f"{name}: first divergent line {i}:\n  new: {a!r}\n  old: {b!r}"
    new_n, old_n = new_text.count(chr(10)), old_text.count(chr(10))
    return f"{name}: line counts differ (new {new_n} vs old {old_n})"
