# apps/wp_invoices/services/pipeline.py

from typing import Any, Dict, Optional
import copy
import os
import re

from apps.wp_invoices.services.extractor import (
    run_extraction, run_item_recovery_v2, run_unified_extraction_v2,
)
from apps.wp_invoices.services.canonical_result import build_canonical_result, build_canonical_result_v2
from apps.wp_invoices.services.supplier_profiles import (
    load_active_profile, prompt_hints, supplier_key,
)
from apps.wp_invoices.services.ocr_layout import associate_items, crop_exclusive_row, extract_layout, transcript


def process_invoice_bytes(
    file_bytes: bytes,
    filename: str,
    content_type: str,
    engine: Optional[str] = None,
    context: Optional[dict] = None,
) -> Dict[str, Any]:
    """
    Orquesta el flujo mínimo de wp_invoices para un archivo:

      1. Usa run_extraction(...) para obtener el JSON de la factura.
      2. (En el futuro) podría llamar analyzer/validation.
      3. Devuelve un dict con:
          - extracted: dict con la factura
          - checks: dict con validaciones (por ahora vacío)

    Parámetros:
      - engine: "mini", "thinking", etc. Si es None, usa el default
        WP_INVOICES_DEFAULT_ENGINE (por ahora 'mini').
    """

    # Si no nos pasaron engine, usamos el default del sistema
    if engine is None:
        engine = os.getenv("WP_INVOICES_DEFAULT_ENGINE", "mini")

    # 1) Ejecutar la extracción con tu extractor.py
    extracted = run_extraction(
        data=file_bytes,
        filename=filename,
        mimetype=content_type,
        engine=engine,
        context=context,
    )

    # 2) Checks mínimos (luego los llenamos con validation.py)
    checks: Dict[str, Any] = {
        "warnings": [],
        "errors": [],
        "engine": engine,
    }

    return {
        "extracted": extracted,
        "checks": checks,
    }


def extract_canonical_invoice(
    *,
    file_bytes: bytes,
    filename: str,
    content_type: str,
    invoice_id: str,
    artifact_id: str | None,
    engine: Optional[str] = None,
    context: Optional[dict] = None,
) -> Dict[str, Any]:
    """Run the reusable extractor and return only the stable public shape."""
    selected_engine = engine or os.getenv("WP_INVOICES_DEFAULT_ENGINE", "mini")
    legacy_result = process_invoice_bytes(
        file_bytes=file_bytes,
        filename=filename,
        content_type=content_type,
        engine=selected_engine,
        context=context,
    )
    return build_canonical_result(
        extracted=legacy_result.get("extracted") or {},
        invoice_id=invoice_id,
        filename=filename,
        content_type=content_type,
        size_bytes=len(file_bytes),
        artifact_id=artifact_id,
        engine=selected_engine,
    )


def extract_canonical_invoice_v2(
    *, file_bytes: bytes, filename: str, content_type: str,
    invoice_id: str, artifact_id: str | None,
    engine: Optional[str] = None, context: Optional[dict] = None,
) -> Dict[str, Any]:
    """Run one unified AI call and apply the deterministic v2 trust gate."""
    selected_engine = engine or os.getenv("WP_INVOICES_DEFAULT_ENGINE", "mini")
    extraction = run_unified_extraction_v2(
        data=file_bytes, filename=filename, mimetype=content_type,
        engine=selected_engine, context=context,
    )
    primary = build_canonical_result_v2(
        extracted=extraction["extracted"], invoice_id=invoice_id,
        filename=filename, content_type=content_type, size_bytes=len(file_bytes),
        artifact_id=artifact_id, engine=selected_engine,
    )
    primary_visual = _apply_layout_and_refresh(primary, extraction.get("ocr_layout") or {})
    ocr_succeeded = (extraction.get("ocr_layout") or {}).get("status") != "failed" and bool((extraction.get("ocr_layout") or {}).get("lines"))
    result = primary
    recovery_attempted = _needs_item_recovery(primary) and bool(_monetary_recovery_targets(primary))
    recovery_accepted = False
    recovery_failed = False
    recovery_usage: dict[str, Any] = {}
    recovery: dict[str, Any] = {}
    recovery_patch_operations: list[dict[str, Any]] = []
    candidate: dict[str, Any] | None = None
    candidate_visual: dict[str, Any] = {}
    profile_hint = _active_supplier_hint(context, result)
    if recovery_attempted:
        try:
            allowed_targets = _monetary_recovery_targets(primary)
            recovery = _run_isolated_monetary_recovery(
                data=file_bytes, filename=filename, mimetype=content_type, engine=selected_engine,
                context=context, supplier_hint=profile_hint, layout=extraction.get("ocr_layout") or {},
                allowed_targets=allowed_targets,
            )
            recovery_usage = recovery["usage"]
            candidate, recovery_patch_operations = _apply_monetary_recovery_patches(
                primary, recovery["patches"], extraction.get("ocr_layout") or {},
                allowed_targets=allowed_targets, crop_candidates=recovery.get("crop_candidates") or {},
            )
            candidate_visual = _apply_layout_and_refresh(candidate, extraction.get("ocr_layout") or {})
            accepted, acceptance_reason = _monetary_patch_acceptance(primary, candidate, recovery_patch_operations)
            if accepted:
                result = candidate
                recovery_accepted = True
        except Exception:
            recovery_failed = True
    result["processing"]["extractor_version"] = "unified-v2.3-ocr-layout"
    result["processing"]["pipeline_version"] = "unified-v2.3-ocr-layout"
    result["processing"]["canonical_revision"] = "monetary-column-v1"
    result["processing"]["execution_mode"] = "ocr_layout" if ocr_succeeded else "visual_fallback"
    result["processing"]["usage"] = _combine_usage(extraction["usage"], recovery_usage)
    result["processing"]["recovery"] = {
        "attempted": recovery_attempted,
        "accepted": recovery_accepted,
        "failed": recovery_failed,
        "primary_score": _quality_score_data(primary),
        "recovered_score": _quality_score_data(candidate) if candidate is not None else None,
        "trigger_reasons": _recovery_trigger_reasons(primary),
        "primary_item_count": len(primary.get("items") or []),
        "primary_items_sum": (primary.get("reconciliation") or {}).get("items_sum"),
        "recovery_item_count": len(candidate.get("items") or []) if candidate is not None else None,
        "recovery_items_sum": (candidate.get("reconciliation") or {}).get("items_sum") if candidate is not None else None,
        "selected_result": "recovery" if recovery_accepted else "primary",
        "selection_reason": "hard_monetary_patch_conditions_passed" if recovery_accepted else (
            "recovery_failed" if recovery_failed else "recovery_did_not_improve_score" if recovery_attempted else "recovery_not_required"
        ),
        "status": "accepted" if recovery_accepted else ("failed" if recovery_failed else "not_required" if not recovery_attempted else "rejected"),
        "patch_operations": recovery_patch_operations,
        "allowed_targets": _monetary_recovery_targets(primary),
    }
    incomplete = recovery_attempted and recovery_failed
    result["processing"]["processing_status"] = "incomplete" if incomplete else "complete"
    if incomplete:
        result["status"] = "incomplete"
    result["processing"]["supplier_profile_applied"] = bool(profile_hint)
    primary_snapshot = copy.deepcopy(primary)
    candidate_snapshot = copy.deepcopy(candidate)
    result["_pipeline_audit"] = {
        "primary": {
            "raw": extraction.get("raw_output"),
            "parsed": extraction.get("extracted"),
            "canonical": primary_snapshot,
            "reconciliation": primary_snapshot.get("reconciliation"),
            "model": extraction.get("model"),
            "provider_request_id": extraction.get("provider_request_id"),
            "provider_response_id": extraction.get("provider_response_id"),
            "usage": extraction.get("usage") or {},
            "ocr_layout": extraction.get("ocr_layout") or {},
            "visual_association": primary_visual,
        },
        "recovery": ({
            "raw": recovery.get("raw_output"),
            "parsed": recovery.get("parsed"),
            "canonical": candidate_snapshot,
            "reconciliation": candidate_snapshot.get("reconciliation") if candidate_snapshot else None,
            "model": recovery.get("model"),
            "provider_request_id": recovery.get("provider_request_id"),
            "provider_response_id": recovery.get("provider_response_id"),
            "usage": recovery.get("usage") or {},
            "accepted": recovery_accepted,
            "visual_association": candidate_visual if candidate is not None else None,
        } if recovery else None),
    }
    return result


def _active_supplier_hint(context: dict[str, Any] | None, result: dict[str, Any]) -> str:
    tenant_id = str((((context or {}).get("tenant") or {}).get("tenant_id")) or "").strip()
    supplier = result.get("supplier") or {}
    key = supplier_key(tax_id=supplier.get("tax_id"), name=supplier.get("name"))
    if not tenant_id or not key:
        return ""
    try:
        return prompt_hints(load_active_profile(tenant_id=tenant_id, key=key))
    except Exception:
        return ""


def _needs_item_recovery(result: dict[str, Any]) -> bool:
    issue_codes = {issue.get("code") for issue in result["quality"]["issues"]}
    return bool(issue_codes & {
        "line_items_missing", "line_item_coverage_low", "item_line_count_mismatch",
        "subtotal_not_reconciled", "suspicious_extraction", "source_evidence_incomplete",
        "items_sum_matches_subtotal",
    })


def _recovery_trigger_reasons(result: dict[str, Any]) -> list[str]:
    eligible = {
        "line_items_missing", "line_item_coverage_low", "item_line_count_mismatch",
        "subtotal_not_reconciled", "suspicious_extraction", "source_evidence_incomplete",
        "items_sum_matches_subtotal",
    }
    return sorted({str(issue.get("code")) for issue in result.get("quality", {}).get("issues") or [] if issue.get("code") in eligible})


def _apply_layout_and_refresh(result: dict[str, Any], layout: dict[str, Any]) -> dict[str, Any]:
    from apps.wp_invoices.services.reconciliation import reconcile_invoice
    from apps.wp_invoices.services.quality_gate import evaluate_invoice_quality

    _materialize_unrepresented_primary_rows(result, layout)
    primary_lines = layout.get("item_primary_lines") or []
    if primary_lines:
        result["observed_item_line_count"] = len(primary_lines)
        result["item_count"] = len(result.get("items") or [])
    association = associate_items(layout, result.get("items") or [])
    by_item = {line.get("consumed_by_item_id"): line for line in association["visual_item_lines"] if line.get("consumed_by_item_id")}
    for index, item in enumerate(result.get("items") or [], 1):
        line = by_item.get(f"item-{index}")
        if not line:
            continue
        item["source_line"] = line.get("source_line_id")
        item["bounding_box"] = line.get("estimated_bounding_region")
        _apply_row_completeness(item, line)
        selected_token = line.get("selected_line_total_token")
        line_evidence = ((item.get("evidence") or {}).get("line_total") or {})
        if selected_token:
            try:
                observed_total = float(str(selected_token.get("raw") or "").replace("$", "").replace(",", "."))
            except ValueError:
                observed_total = None
            if observed_total is not None:
                if item.get("line_total") != observed_total:
                    line_evidence["semantic_candidate"] = item.get("line_total")
                item["line_total"] = observed_total
                item["subtotal_contribution"] = observed_total
                line_evidence.update({"raw_text": selected_token.get("raw"), "origin": "observed",
                                      "status": "confirmed", "verification": "ocr_verified",
                                      "source_line": line.get("source_line_id"),
                                      "bounding_box": selected_token.get("bbox"),
                                      "bounding_box_origin": "tesseract_observed"})
        else:
            if item.get("recovery_crop_confirmed") and item.get("line_total") is not None:
                line_evidence.update({"status": "confirmed", "verification": "isolated_crop_ocr_verified",
                                      "origin": "observed"})
                item["subtotal_contribution"] = item["line_total"]
                item["missing_fields"] = [field for field in item.get("missing_fields") or [] if field != "line_total"]
                item["complete"] = not item["missing_fields"]
                item["ocr_row_status"] = "complete" if item["complete"] else "incomplete"
                selected_token = {"recovered_from_isolated_crop": True}
            else:
                semantic_candidate = item.get("line_total")
                line_evidence.update({"semantic_candidate": semantic_candidate, "status": "missing",
                                      "verification": "unavailable", "origin": "observed"})
                item["line_total"] = None
                item["subtotal_contribution"] = 0.0
                item["complete"] = False
                item["missing_fields"] = sorted(set((item.get("missing_fields") or []) + ["line_total"]))
                item["ocr_row_status"] = "incomplete"
        continuation = line.get("weight_continuation") or {}
        rate_token = continuation.get("selected_unit_rate_token") or {}
        if continuation and rate_token:
            try:
                item["quantity"] = float(continuation["weight_quantity"])
                item["unit_price"] = float(str(rate_token["raw"]).replace("$", "").replace(",", "."))
                item["transaction_unit"] = "kg"; item["unit_price_basis"] = "kg"
                item["weighted_arithmetic_valid"] = abs(item["quantity"] * item["unit_price"] - item["line_total"]) <= 0.01
            except (TypeError, ValueError):
                item["weighted_arithmetic_valid"] = False
        if line.get("product_code"):
            item["product_code"] = str(line["product_code"])
            item["product_code_evidence"] = {
                "raw_text": str(line["product_code"]), "source_line_id": line["source_line_id"],
                "bbox": line["estimated_bounding_region"], "confidence": line.get("confidence", 0),
                "evidence_state": "confirmed",
            }
        if line.get("tax_code"):
            semantic_tax = str(item.get("tax_code") or "")
            state = "confirmed" if not semantic_tax or semantic_tax == line["tax_code"] else "conflicting"
            item["tax_code"] = line["tax_code"] if state == "confirmed" else semantic_tax
            item["tax_code_evidence"] = {
                "raw_text": line["tax_code"], "source_line_id": line["source_line_id"],
                "bbox": line["estimated_bounding_region"], "confidence": line.get("confidence", 0),
                "evidence_state": state,
            }
        candidates = {str(value).replace("$", "").replace(",", "").strip() for value in line.get("monetary_candidates") or []}
        for field_name, field_evidence in (item.get("evidence") or {}).items():
            if field_name == "line_total":
                continue
            raw = str(field_evidence.get("raw_text") or "").replace("AUD", "").replace("$", "").replace(",", "").strip()
            verified = bool(raw and any(raw == candidate or raw.lstrip("-") == candidate.lstrip("-") for candidate in candidates))
            field_evidence.update({
                "source_line": line["source_line_id"],
                "bounding_box": line["estimated_bounding_region"],
                "bounding_box_origin": "tesseract_observed",
                "verification": "ocr_verified" if verified else "ocr_conflict",
                "status": "confirmed" if verified else "conflicting",
            })
    reconciliation = reconcile_invoice(
        items=result.get("items") or [], adjustments=result.get("adjustments") or [],
        tax_summary=result.get("tax_summary") or {}, subtotal=result.get("subtotal"), total=result.get("total"),
        observed_item_line_count=result.get("observed_item_line_count"),
    )
    result["adjustments"] = reconciliation["reconciled_adjustments"]
    result["reconciliation"] = reconciliation
    result["quality"] = evaluate_invoice_quality(
        items=result.get("items") or [], subtotal=result.get("subtotal"), tax=result.get("tax"), total=result.get("total"),
        supplier_name=(result.get("supplier") or {}).get("name"), invoice_date=result.get("invoice_date"),
        adjustments=result.get("adjustments") or [], tax_inclusive=result.get("tax_inclusive"), reconciliation=reconciliation,
    )
    result["review_status"] = result["quality"]["review_status"]
    return association


def _apply_row_completeness(item: dict[str, Any], line: dict[str, Any]) -> None:
    missing = list(line.get("missing_fields") or [])
    item["complete"] = not missing
    item["missing_fields"] = missing
    item["ocr_row_status"] = "incomplete" if missing else "complete"


def _materialize_unrepresented_primary_rows(result: dict[str, Any], layout: dict[str, Any]) -> None:
    """Keep independently observed product rows even when semantic fields are incomplete."""
    initial = associate_items(layout, result.get("items") or [])
    for line in initial.get("unconsumed_source_lines") or []:
        raw = str(line.get("raw_text") or "")
        code = line.get("product_code")
        description = re.sub(r"^\s*\d{4,8}\s+", "", raw)
        description = re.sub(r"\s+-?\d+[.,]\d{2}\s*[AB]?\s*$", "", description, flags=re.I).strip()
        money = list(line.get("monetary_candidates") or [])
        line_total = None
        if money:
            try:
                line_total = float(str(money[-1]).replace("$", "").replace(",", "."))
            except ValueError:
                pass
        missing = list(line.get("missing_fields") or [])
        result.setdefault("items", []).append({
            "description": description or raw, "quantity": None, "unit_price": None,
            "tax": None, "line_total": line_total, "confidence": float(line.get("confidence") or 0),
            "product_code": code, "product_code_evidence": {
                "raw_text": code, "source_line_id": line.get("source_line_id"),
                "bbox": line.get("estimated_bounding_region"), "confidence": line.get("confidence", 0),
                "evidence_state": "confirmed" if code else "missing",
            },
            "transaction_unit": "each", "package_size": None, "unit_price_basis": None,
            "tax_code": line.get("tax_code"), "tax_code_evidence": {
                "raw_text": line.get("tax_code"), "source_line_id": line.get("source_line_id"),
                "bbox": line.get("estimated_bounding_region"), "confidence": line.get("confidence", 0),
                "evidence_state": "confirmed" if line.get("tax_code") else "missing",
            },
            "source_text": raw, "source_line": line.get("source_line_id"),
            "bounding_box": line.get("estimated_bounding_region"),
            "complete": not missing, "missing_fields": missing, "ocr_row_status": "incomplete" if missing else "complete",
            "evidence": {
                "quantity": {"raw_text": None, "origin": "observed", "status": "missing"},
                "unit_price": {"raw_text": None, "origin": "observed", "status": "missing"},
                "tax": {"raw_text": None, "origin": "observed", "status": "missing"},
                "line_total": {"raw_text": money[-1] if money else None, "origin": "observed",
                    "status": "confirmed" if money else "missing", "source_line": line.get("source_line_id"),
                    "bounding_box": line.get("estimated_bounding_region"), "bounding_box_origin": "tesseract_observed",
                    "verification": "ocr_verified" if money else "unavailable"},
            },
        })


def _merge_recovery_patch(primary: dict[str, Any], recovered: dict[str, Any]) -> tuple[dict[str, Any], list[dict[str, Any]]]:
    """Patch incomplete existing OCR rows; recovery cannot erase observed rows."""
    if not any(str(item.get("source_line") or "").startswith("ocr-line-") for item in primary.get("items") or []):
        return recovered, [{"operation": "replace_without_ocr_layout", "item_count": len(recovered.get("items") or [])}]
    merged = copy.deepcopy(primary)
    candidates = {str(item.get("source_line")): item for item in recovered.get("items") or [] if item.get("source_line")}
    operations = []
    for index, item in enumerate(merged.get("items") or []):
        source_line = str(item.get("source_line") or "")
        candidate = candidates.get(source_line)
        if not candidate:
            continue
        changed = []
        for field in ("description", "quantity", "unit_price", "line_total", "tax_code", "transaction_unit", "unit_price_basis", "package_size"):
            if item.get(field) in (None, "", {}) and candidate.get(field) not in (None, "", {}):
                item[field] = copy.deepcopy(candidate[field]); changed.append(field)
        if changed:
            item["missing_fields"] = [name for name in item.get("missing_fields") or [] if name not in changed]
            item["complete"] = not item.get("missing_fields")
            item["ocr_row_status"] = "complete" if item["complete"] else "incomplete"
            operations.append({"operation": "update_incomplete_item", "source_line_id": source_line,
                               "item_index": index + 1, "fields": changed})
    return merged, operations


def _monetary_recovery_targets(result: dict[str, Any]) -> dict[str, str]:
    targets = {}
    for item in result.get("items") or []:
        state = str((((item.get("evidence") or {}).get("line_total") or {}).get("status")) or "missing")
        if state == "missing":
            targets[str(item.get("source_line") or "")] = "fill_missing_line_total"
        elif state in {"ambiguous", "conflicting"}:
            targets[str(item.get("source_line") or "")] = "replace_conflicting_line_total"
        elif item.get("weighted_arithmetic_valid") is False:
            targets[str(item.get("source_line") or "")] = "confirm_weighted_values"
    return {key: value for key, value in targets.items() if key.startswith("ocr-line-")}


def _run_isolated_monetary_recovery(*, data: bytes, filename: str, mimetype: str, engine: str,
                                    context: dict | None, supplier_hint: str, layout: dict[str, Any],
                                    allowed_targets: dict[str, str]) -> dict[str, Any]:
    lines = {str(line.get("source_line_id")): line for line in layout.get("item_primary_lines") or []}
    patches = []; calls = []; crop_candidates = {}; usages = []
    for source_line_id, operation in allowed_targets.items():
        line = lines.get(source_line_id)
        interval = (line or {}).get("exclusive_vertical_interval")
        if not line or not interval:
            raise ValueError("recovery_target_interval_unavailable")
        crop = crop_exclusive_row(data, mimetype, interval)
        crop_layout = extract_layout(crop, "image/png")
        candidates = sorted({round(float(str(value).replace("$", "").replace(",", ".")), 2)
                             for crop_line in crop_layout.get("lines") or []
                             for value in crop_line.get("monetary_candidates") or []})
        crop_candidates[source_line_id] = candidates
        response = run_item_recovery_v2(
            data=crop, filename=f"{source_line_id}.png", mimetype="image/png", engine=engine,
            context=context, supplier_hints=supplier_hint,
            visual_hints=(f"IMMUTABLE ALLOWED TARGET: {source_line_id}; ALLOWED OPERATION: {operation}; "
                          f"EXCLUSIVE INTERVAL: {interval}; EXPECTED LINE-TOTAL COLUMN X: {layout.get('line_total_column_x')}; "
                          f"LOCAL OCR MONETARY CANDIDATES: {candidates}; ROW: {line.get('raw_text')}"),
        )
        patches.extend(response.get("patches") or []); calls.append(response); usages.append(response.get("usage") or {})
    return {"patches": patches, "parsed": {"patches": patches}, "calls": calls,
            "crop_candidates": crop_candidates, "usage": _combine_usage(*usages),
            "raw_output": [call.get("raw_output") for call in calls],
            "model": calls[0].get("model") if calls else None,
            "provider_request_id": [call.get("provider_request_id") for call in calls],
            "provider_response_id": [call.get("provider_response_id") for call in calls]}


def _apply_monetary_recovery_patches(primary: dict[str, Any], patches: list[dict[str, Any]],
                                     layout: dict[str, Any], *, allowed_targets: dict[str, str] | None = None,
                                     crop_candidates: dict[str, list[float]] | None = None) -> tuple[dict[str, Any], list[dict[str, Any]]]:
    candidate = copy.deepcopy(primary)
    allowed_targets = allowed_targets if allowed_targets is not None else _monetary_recovery_targets(primary)
    enforce_crop_support = crop_candidates is not None
    crop_candidates = crop_candidates or {}
    invalid = [patch for patch in patches if allowed_targets.get(str(patch.get("source_line_id") or "")) != patch.get("operation")]
    duplicate_targets = len({str(patch.get("source_line_id") or "") for patch in patches}) != len(patches)
    if invalid or duplicate_targets:
        return candidate, [{"accepted": False, "reason": "entire_response_outside_immutable_target_set",
                            "allowed_targets": allowed_targets}]
    by_source = {str(item.get("source_line") or ""): item for item in candidate.get("items") or []}
    layout_lines = {str(line.get("source_line_id") or ""): line for line in layout.get("item_primary_lines") or []}
    operations = []
    for patch in patches:
        operation = str(patch.get("operation") or "")
        source = str(patch.get("source_line_id") or "")
        item = by_source.get(source); line = layout_lines.get(source) or {}
        if item is None or operation not in {"fill_missing_line_total", "replace_conflicting_line_total", "select_candidate", "confirm_weighted_values"}:
            operations.append({"source_line_id": source, "operation": operation, "accepted": False, "reason": "invalid_target_or_operation"}); continue
        evidence_state = str((((item.get("evidence") or {}).get("line_total") or {}).get("status")) or "missing")
        if evidence_state == "confirmed":
            operations.append({"source_line_id": source, "operation": operation, "accepted": False, "reason": "confirmed_ocr_value_protected"}); continue
        new_total = patch.get("selected_candidate") if operation == "select_candidate" else patch.get("line_total")
        if enforce_crop_support and new_total is not None and round(float(new_total), 2) not in set(crop_candidates.get(source) or []):
            operations.append({"source_line_id": source, "operation": operation, "accepted": False,
                               "reason": "value_not_supported_by_isolated_crop"}); continue
        if operation == "fill_missing_line_total" and evidence_state not in {"missing", "ambiguous"}:
            operations.append({"source_line_id": source, "operation": operation, "accepted": False, "reason": "target_not_missing"}); continue
        if operation == "replace_conflicting_line_total" and evidence_state != "conflicting":
            operations.append({"source_line_id": source, "operation": operation, "accepted": False, "reason": "target_not_conflicting"}); continue
        if operation == "select_candidate":
            candidates = {round(float(str(value).replace("$", "").replace(",", ".")), 2) for value in line.get("monetary_candidates") or []}
            if new_total is None or round(float(new_total), 2) not in candidates:
                operations.append({"source_line_id": source, "operation": operation, "accepted": False, "reason": "candidate_not_observed_in_row"}); continue
        if operation == "confirm_weighted_values":
            item["quantity"] = patch.get("quantity"); item["unit_price"] = patch.get("unit_price")
            item["transaction_unit"] = "kg"; item["unit_price_basis"] = "kg"
        if new_total is not None:
            item["line_total"] = round(float(new_total), 2)
            item["recovery_crop_confirmed"] = True
            ((item.get("evidence") or {}).get("line_total") or {}).update(
                {"status": "observed_unverified", "origin": "inferred", "recovery_evidence": patch.get("evidence_text")}
            )
        operations.append({"source_line_id": source, "operation": operation, "accepted": True,
                           "line_total": new_total})
    return candidate, operations


def _monetary_patch_acceptance(primary: dict[str, Any], candidate: dict[str, Any],
                               operations: list[dict[str, Any]]) -> tuple[bool, str]:
    primary_items = primary.get("items") or []; candidate_items = candidate.get("items") or []
    if len(candidate_items) != len(primary_items):
        return False, "item_count_changed"
    if [item.get("source_line") for item in candidate_items] != [item.get("source_line") for item in primary_items]:
        return False, "source_line_identity_changed"
    if not any(operation.get("accepted") for operation in operations):
        return False, "no_valid_patch"
    def delta(result):
        value = (result.get("reconciliation") or {}).get("subtotal_delta")
        if value is None:
            subtotal = result.get("subtotal") or 0
            value = sum(float(item.get("line_total") or 0) for item in result.get("items") or []) - float(subtotal)
        return abs(float(value))
    if delta(candidate) >= delta(primary):
        return False, "subtotal_delta_not_reduced"
    for old, new in zip(primary_items, candidate_items):
        status = ((((old.get("evidence") or {}).get("line_total") or {}).get("status")))
        if status == "confirmed" and old.get("line_total") != new.get("line_total"):
            return False, "confirmed_ocr_value_replaced"
    old_ambiguity = sum(1 for item in primary_items if
                        ((item.get("evidence") or {}).get("line_total") or {}).get("status") in {"ambiguous", "conflicting"})
    new_ambiguity = sum(1 for item in candidate_items if
                        ((item.get("evidence") or {}).get("line_total") or {}).get("status") in {"ambiguous", "conflicting"})
    return (new_ambiguity <= old_ambiguity, "accepted" if new_ambiguity <= old_ambiguity else "new_ambiguity_introduced")


def _quality_score(result: dict[str, Any]) -> tuple[float, float, int, int]:
    coverage = result["quality"].get("item_coverage")
    reconciliation = result.get("reconciliation") or {}
    passed = sum(1 for key in (
        "item_count_reconciled", "subtotal_reconciled", "adjustments_reconciled",
        "tax_reconciled", "source_evidence_valid",
    ) if reconciliation.get(key) == "passed")
    return (
        float(passed),
        -1.0 if coverage is None else min(float(coverage), 1.0),
        -len(result["quality"].get("issues") or []),
        len(result.get("items") or []),
    )


def _quality_score_data(result: dict[str, Any] | None) -> dict[str, Any] | None:
    if result is None:
        return None
    score = _quality_score(result)
    return {
        "passed_reconciliation_checks": int(score[0]),
        "item_coverage": None if score[1] == -1.0 else score[1],
        "issue_count": -score[2],
        "item_count": score[3],
    }


def _combine_usage(*usages: dict[str, Any]) -> dict[str, int]:
    keys = ("input_tokens", "output_tokens", "total_tokens")
    return {key: sum(int(usage.get(key, 0) or 0) for usage in usages) for key in keys}
