"""Deterministic trust gate for canonical invoice result v2."""

from __future__ import annotations

from decimal import Decimal
from typing import Any


TOLERANCE = Decimal("0.01")
MIN_ACCEPTED_COVERAGE = Decimal("0.98")


def evaluate_invoice_quality(
    *, items: list[dict[str, Any]], subtotal: float | None,
    tax: float | None, total: float | None,
    supplier_name: str | None, invoice_date: str | None,
    adjustments: list[dict[str, Any]] | None = None,
    tax_inclusive: bool | None = None,
    reconciliation: dict[str, Any] | None = None,
) -> dict[str, Any]:
    validations: list[dict[str, Any]] = []
    item_values = [_decimal(item.get("line_total")) for item in items]
    known_items = [value for value in item_values if value is not None]
    items_sum = sum(known_items, Decimal("0")) if known_items else None
    subtotal_value = _decimal(subtotal)
    tax_value = _decimal(tax)
    total_value = _decimal(total)
    adjustment_values = [_decimal(value.get("amount")) for value in (adjustments or []) if value.get("participates_in_total") is True]
    adjustment_sum = sum((value for value in adjustment_values if value is not None), Decimal("0"))

    coverage = None
    if items_sum is not None and subtotal_value not in (None, Decimal("0")):
        coverage = max(Decimal("0"), items_sum / abs(subtotal_value))

    canonical = reconciliation or {}
    validations.extend([
        _canonical_validation("items_sum_matches_subtotal", canonical.get("subtotal_reconciled"), items_sum, subtotal_value),
        _canonical_validation("subtotal_plus_tax_matches_total", "not_applicable" if tax_inclusive is True else canonical.get("tax_reconciled"), tax_value, tax_value),
        _canonical_validation("subtotal_plus_adjustments_matches_total", canonical.get("adjustments_reconciled"),
                              (subtotal_value + adjustment_sum) if subtotal_value is not None else None, total_value),
    ])

    failed_lines = 0
    checked_lines = 0
    for index, item in enumerate(items):
        quantity = _decimal(item.get("quantity"))
        unit_price = _decimal(item.get("unit_price"))
        line_total = _decimal(item.get("line_total"))
        if quantity is None or unit_price is None or line_total is None:
            continue
        checked_lines += 1
        calculated = quantity * unit_price
        passed = abs(calculated - line_total) <= TOLERANCE
        failed_lines += 0 if passed else 1
        validations.append({
            "code": "quantity_times_unit_matches_line_total",
            "status": "passed" if passed else "failed",
            "item_index": index,
            "expected": _float(line_total),
            "calculated": _float(calculated),
            "difference": _float(calculated - line_total),
            "tolerance": 0.01,
        })

    issues: list[dict[str, str]] = []
    if not supplier_name:
        issues.append(_issue("supplier_name_missing", "extraction_incomplete"))
    if not invoice_date:
        issues.append(_issue("invoice_date_missing", "extraction_incomplete"))
    if not items:
        issues.append(_issue("line_items_missing", "extraction_incomplete"))
    if total_value is None:
        issues.append(_issue("total_missing", "extraction_incomplete"))
    if coverage is not None and coverage < MIN_ACCEPTED_COVERAGE:
        issues.append(_issue("line_item_coverage_low", "extraction_incomplete"))
    if failed_lines:
        issues.append(_issue("line_arithmetic_mismatch", "document_or_extraction_error"))
    if reconciliation:
        for code in reconciliation.get("issues") or []:
            issues.append(_issue(str(code), "extraction_or_reconciliation_error"))
    for validation in validations[:3]:
        if validation["status"] == "failed" and not any(
            issue["code"] == "line_item_coverage_low" for issue in issues
        ):
            issues.append(_issue(validation["code"], "document_or_extraction_error"))

    review_status = "accepted" if not issues else "needs_review"
    return {
        "review_status": review_status,
        "item_coverage": None if coverage is None else round(float(coverage), 4),
        "items_sum": None if items_sum is None else _float(items_sum),
        "checked_line_count": checked_lines,
        "passed_line_count": checked_lines - failed_lines,
        "adjustments_sum": _float(adjustment_sum),
        "arithmetic_valid": "passed" if not failed_lines and checked_lines else ("failed" if failed_lines else "not_available"),
        "source_evidence_valid": (reconciliation or {}).get("source_evidence_valid", "not_available"),
        "subtotal_reconciled": (reconciliation or {}).get("subtotal_reconciled", "not_available"),
        "tax_reconciled": (reconciliation or {}).get("tax_reconciled", "not_available"),
        "validations": validations,
        "issues": issues,
        "can_teach_supplier_profile": review_status == "accepted",
    }


def normalize_invoice_type(value: Any) -> str:
    if isinstance(value, dict):
        value = value.get("normalized") or value.get("computed") or value.get("verbatim") or value.get("value")
    normalized = str(value or "").strip().lower()
    if normalized in {"pos", "receipt", "point_of_sale", "point-of-sale"}:
        return "pos"
    if normalized in {"corporate", "invoice", "tax_invoice", "tax invoice"}:
        return "corporate"
    return "unknown"


def _comparison(*, code, calculated, expected, missing):
    if calculated is None or expected is None:
        return {"code": code, "status": missing, "expected": _float(expected),
                "calculated": _float(calculated), "difference": None, "tolerance": 0.01}
    difference = calculated - expected
    return {"code": code, "status": "passed" if abs(difference) <= TOLERANCE else "failed",
            "expected": _float(expected), "calculated": _float(calculated),
            "difference": _float(difference), "tolerance": 0.01}


def _canonical_validation(code, status, calculated, expected):
    normalized = status if status in {"passed", "failed", "not_available", "not_applicable"} else "not_available"
    difference = calculated - expected if calculated is not None and expected is not None else None
    return {"code": code, "status": normalized, "expected": _float(expected),
            "calculated": _float(calculated), "difference": _float(difference), "tolerance": 0.01}


def _issue(code: str, category: str) -> dict[str, str]:
    return {"code": code, "category": category}


def _decimal(value: Any) -> Decimal | None:
    if value is None or isinstance(value, bool):
        return None
    try:
        return Decimal(str(value))
    except Exception:
        return None


def _float(value: Decimal | None) -> float | None:
    return None if value is None else float(value.quantize(Decimal("0.01")))
