"""Verify a synthetic order CSV without a model or API call.

Rules: CNY only; paid/refunded/unpaid are exact status names; refunded means
fully refunded. Amounts are nonnegative decimal strings with at most two
fractional digits. Optional thousands separators must use groups of three.
Identical records with the same ID count once; conflicting records with the
same ID are all quarantined. Missing or invalid data never becomes zero.
This is a teaching example, not a production accounting or payment system.
"""

import argparse
import csv
import json
import re
import sys
from collections import defaultdict
from dataclasses import dataclass
from decimal import Decimal, localcontext
from typing import TextIO


FIELDS = ("record_id", "status", "amount_cny")
STATUSES = frozenset(("paid", "refunded", "unpaid"))
RECORD_ID = re.compile(r"[A-Za-z0-9][A-Za-z0-9_-]*")
AMOUNT = re.compile(
    r"(?:0|[1-9][0-9]*|[1-9][0-9]{0,2}(?:,[0-9]{3})+)"
    r"(?:\.[0-9]{1,2})?"
)


class ValidationError(ValueError):
    """The file does not conform to the example's CSV schema."""


@dataclass(frozen=True)
class Row:
    line: int
    record_id: str
    status: str
    amount_cny: str

    @property
    def signature(self):
        return (self.record_id, self.status, self.amount_cny)


def parse_amount(raw: str, status: str) -> Decimal:
    if raw == "":
        raise ValueError("missing_amount")
    if status == "paid" and raw.startswith("-"):
        raise ValueError("negative_paid_amount")
    if AMOUNT.fullmatch(raw) is None:
        raise ValueError("invalid_amount")
    return Decimal(raw.replace(",", ""))


def validate_orders(source: TextIO) -> dict:
    reader = csv.DictReader(source, strict=True)
    rows = []
    try:
        if tuple(reader.fieldnames or ()) != FIELDS:
            raise ValidationError("Expected exactly: record_id,status,amount_cny")
        for values in reader:
            if None in values or any(values[field] is None for field in FIELDS):
                raise ValidationError(
                    "Wrong number of CSV fields at line {}".format(reader.line_num)
                )
            rows.append(Row(reader.line_num, *(values[field] for field in FIELDS)))
    except csv.Error as error:
        raise ValidationError(
            "Malformed CSV near line {}: {}".format(reader.line_num, error)
        ) from error

    groups = defaultdict(list)
    included = []
    excluded = []
    duplicates = []
    anomalies = []
    amounts = []
    unresolved_rows = 0

    for row in rows:
        if RECORD_ID.fullmatch(row.record_id) is None:
            anomalies.append({
                "record_id": row.record_id,
                "lines": [row.line],
                "reason": "invalid_record_id",
                "status": row.status,
                "raw_amount_cny": row.amount_cny,
            })
            unresolved_rows += 1
        else:
            groups[row.record_id].append(row)

    for record_id, group in groups.items():
        variants = {}
        for row in group:
            if row.signature in variants:
                duplicates.append({
                    "record_id": record_id,
                    "line": row.line,
                    "first_line": variants[row.signature][0].line,
                })
                variants[row.signature].append(row)
            else:
                variants[row.signature] = [row]

        lines = [row.line for row in group]
        if len(variants) != 1:
            anomalies.append({
                "record_id": record_id,
                "lines": lines,
                "reason": "conflicting_record_id",
                "variants": [{
                    "lines": [row.line for row in variant],
                    "status": variant[0].status,
                    "raw_amount_cny": variant[0].amount_cny,
                } for variant in variants.values()],
            })
            unresolved_rows += len(variants)
            continue

        row = group[0]
        anomaly = {
            "record_id": record_id,
            "lines": lines,
            "status": row.status,
            "raw_amount_cny": row.amount_cny,
        }
        if row.status not in STATUSES:
            anomaly["reason"] = "unknown_status"
            anomalies.append(anomaly)
            unresolved_rows += 1
            continue
        try:
            amount = parse_amount(row.amount_cny, row.status)
        except ValueError as error:
            anomaly["reason"] = str(error)
            anomalies.append(anomaly)
            unresolved_rows += 1
            continue

        record = {
            "record_id": record_id,
            "lines": lines,
            "status": row.status,
            "amount_cny": format(amount, ".2f"),
        }
        if row.status == "paid":
            amounts.append(amount)
            included.append(record)
        else:
            record["reason"] = (
                "fully_refunded" if row.status == "refunded" else "not_paid"
            )
            excluded.append(record)

    # Decimal addition also needs enough precision for large inputs.
    digits = max((len(amount.as_tuple().digits) for amount in amounts), default=1)
    with localcontext() as context:
        context.prec = max(28, digits + len(str(len(amounts))) + 2)
        total = sum(amounts, Decimal("0.00"))
        total_text = format(total, ".2f")

    return {
        "currency": "CNY",
        "input_rows": len(rows),
        "distinct_valid_record_ids": len(groups),
        "exact_duplicate_rows": len(duplicates),
        "confirmed_paid_records": len(included),
        "confirmed_excluded_records": len(excluded),
        "unresolved_records": len(anomalies),
        "unresolved_rows_after_deduplication": unresolved_rows,
        "confirmed_paid_total": total_text,
        "incomplete": bool(anomalies),
        "included": included,
        "excluded": excluded,
        "duplicates": duplicates,
        "anomalies": anomalies,
    }


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("csv_path", help="Path to the synthetic CSV")
    args = parser.parse_args()
    try:
        with open(args.csv_path, encoding="utf-8-sig", newline="") as source:
            result = validate_orders(source)
    except (OSError, UnicodeError, ValidationError) as error:
        print(json.dumps({"error": "invalid_input", "detail": str(error)}),
              file=sys.stderr)
        return 2
    print(json.dumps(result, indent=2))
    return 0


if __name__ == "__main__":
    sys.exit(main())
