"""Locale dictionaries for generic open-item reconciliation.

The reconciliation engine uses stable internal codes. Locale packs only provide
the words needed to recognize source-document vocabulary and render outputs.
They must not introduce customer-specific logic.
"""

from __future__ import annotations

import re
from typing import Any

SUPPORTED_LANGUAGES = ("it", "en", "fr", "de", "es")
DEFAULT_LANGUAGE = "it"


BASE_OUTPUT_LABELS = {
    "metadata_field": "Field",
    "metadata_value": "Value",
    "conclusion": "Conclusion",
    "summary": "Summary",
    "assumptions": "Assumptions",
    "next_steps": "Next Steps",
    "fallback_narrative": "The detailed row-level reconciliation is contained in the Excel workpaper.",
    "excel_authority": "The Excel workbook is the authoritative row-level audit workpaper.",
}


LANGUAGE_PACKS: dict[str, dict[str, Any]] = {
    "it": {
        "output_labels": {
            "metadata_field": "Campo",
            "metadata_value": "Valore",
            "conclusion": "Conclusioni",
            "summary": "Sintesi",
            "assumptions": "Assunzioni",
            "next_steps": "Prossimi passi",
            "fallback_narrative": "Il dettaglio riga per riga della riconciliazione è contenuto nel file Excel.",
            "excel_authority": "Il file Excel è il workpaper auditabile di dettaglio riga per riga.",
        },
        "role_keywords": {
            "bank_statement": ("estratto conto", "conto corrente", "banca"),
            "factoring_statement": (
                "factoring",
                "factor",
                "anticipo",
                "anticipi",
                "cessione",
                "pro soluto",
            ),
            "payment_order": ("distinta", "ordine pagamento"),
            "journal": ("giornale", "libro giornale"),
            "ledger": ("mastrino", "partitario", "interrogazione conto", "sottoconto"),
            "open_items": (
                "partite aperte",
                "scheda clienti",
                "scheda cliente",
                "scheda fornitori",
                "scheda fornitore",
            ),
        },
        "side_keywords": {
            "customer": ("cliente", "clienti"),
            "supplier": ("fornitore", "fornitori"),
        },
        "evidence_keywords": {
            "invoice": ("fattura", "fatture"),
            "closure": (
                "incassata",
                "incassato",
                "pagata",
                "pagato",
                "chiusa",
                "chiuso",
                "chius",
            ),
            "compensation": ("compens", "giroconto"),
            "netting": ("netting",),
            "factoring": (
                "factoring",
                "factor",
                "pro soluto",
                "pro-soluto",
                "cession",
                "cessione cred",
                "cess. cred",
                "anticipo",
                "anticip",
            ),
            "bank": (
                "bank",
                "banca",
                "estratto conto",
                "bonifico",
                "sepa",
                "wire",
                "payment",
                "pagamento",
                "incasso",
            ),
            "batch": ("distinta",),
        },
        "payment_order_terms": {
            "header": ("distinta",),
            "date_prefix": ("del",),
            "total": ("totale distinta",),
            "value_date": ("valuta",),
            "invoice": ("fattura",),
        },
        "next_steps": {
            "probable_payment": "Verificare le righe con pagamento probabile: esiste un movimento bancario probabile, ma va confermata l'allocazione riga/documento prima di trattarlo come chiusura definitiva.",
            "needs_evidence": "Acquisire le evidenze indicate nelle righe che richiedono documentazione aggiuntiva.",
            "unresolved": "Mappare manualmente le righe non risolte ai documenti sorgente o richiedere dettaglio contabile aggiuntivo.",
            "complete": "Conservare Excel e fonti come workpaper auditabile.",
        },
        "missing_evidence": {
            "probable_bank_payment_candidate": "Esiste un pagamento bancario probabile collegato alla riga; verificare distinta, descrizione bancaria o dettaglio allocazione prima di trattarlo come chiusura definitiva.",
            "payment_order_only": "La distinta è solo evidenza ponte; acquisire estratto conto, contabile bancaria o estratto del factor/operatore collegato al lotto.",
            "payment_order_amount_mismatch": "La distinta esiste, ma l'importo allocato non coincide con la partita aperta; acquisire dettaglio allocazione o conferma di saldo parziale.",
            "factoring_bridge_only": "La scrittura factor/operatore è solo evidenza ponte; acquisire estratto conto bancario o estratto operatore collegato a fattura o lotto.",
            "unallocated_external_bank_requires_allocation": "Esiste un movimento bancario, ma non è allocato alla specifica fattura/partita; acquisire dettaglio allocazione, avviso di pagamento o breakdown del lotto.",
            "internal_closure_without_external": "Esiste una chiusura interna; acquisire evidenza esterna bancaria, factor/operatore o compensazione documentata collegata alla riga.",
            "internal_booking_open_support": "La posizione aperta è supportata da registrazione/saldo interno; per superarla serve evidenza esterna specifica di chiusura.",
            "grouped_open_amount_internal_booking_support": "La posizione aperta è supportata da una registrazione interna aggregata che coincide con la somma delle partite aperte dello stesso documento; per superarla serve evidenza esterna specifica di chiusura.",
            "internal_accounting_only": "Esiste una scrittura interna di mastro/giornale; acquisire evidenza esterna bancaria, factor/operatore o compensazione documentata.",
            "compensation_needs_external_support": "La compensazione è indicata, ma la regola configurata richiede supporto bancario/esterno.",
            "default": "Acquisire evidenza specifica per documento: movimento bancario, estratto factor/operatore, compensazione documentata o dettaglio mastro.",
        },
    },
    "en": {
        "output_labels": BASE_OUTPUT_LABELS,
        "role_keywords": {
            "bank_statement": ("bank statement", "statement", "bank account", "bank"),
            "factoring_statement": (
                "factoring",
                "factor",
                "advance",
                "assignment of receivables",
                "without recourse",
            ),
            "payment_order": ("payment order", "payment batch", "remittance order"),
            "journal": ("journal", "general journal"),
            "ledger": ("ledger", "account ledger", "subledger", "account card"),
            "open_items": (
                "open items",
                "open-item",
                "customer statement",
                "supplier statement",
                "customer ledger",
                "supplier ledger",
            ),
        },
        "side_keywords": {
            "customer": ("customer", "client", "receivable"),
            "supplier": ("supplier", "vendor", "payable"),
        },
        "evidence_keywords": {
            "invoice": ("invoice", "invoices"),
            "closure": ("paid", "collected", "settled", "closed", "cleared"),
            "compensation": ("compensation", "set-off", "setoff"),
            "netting": ("netting",),
            "factoring": (
                "factoring",
                "factor",
                "without recourse",
                "assignment",
                "receivable assignment",
                "advance",
            ),
            "bank": (
                "bank",
                "bank statement",
                "wire",
                "sepa",
                "transfer",
                "payment",
                "receipt",
                "collection",
            ),
            "batch": ("batch", "payment batch", "remittance"),
        },
        "payment_order_terms": {
            "header": ("payment order", "payment batch", "remittance order"),
            "date_prefix": ("date", "dated"),
            "total": ("total payment order", "total batch", "batch total"),
            "value_date": ("value date",),
            "invoice": ("invoice",),
        },
        "next_steps": {
            "probable_payment": "Review rows classified as probable_payment: a likely bank movement exists, but row/document allocation must be confirmed before treating it as definitively closed.",
            "needs_evidence": "Obtain the requested supporting documents for the rows that need further evidence.",
            "unresolved": "Manually map unresolved rows to source documents or request additional ledger detail.",
            "complete": "Retain the Excel workbook and source documents as the audit workpaper.",
        },
        "missing_evidence": {
            "probable_bank_payment_candidate": "A probable bank payment is linked to this row; verify the payment batch, bank description, or allocation detail before treating it as definitively closed.",
            "payment_order_only": "Payment order is bridge evidence only; obtain bank statement, bank receipt, or factoring/operator statement tied to the batch.",
            "payment_order_amount_mismatch": "Payment order exists, but its allocated amount does not match the open-item row; obtain allocation detail or confirm partial settlement.",
            "factoring_bridge_only": "Factoring/operator entry is bridge evidence only; obtain bank statement or operator statement tied to the invoice or batch.",
            "unallocated_external_bank_requires_allocation": "A bank movement exists but is not allocated to the specific invoice/open item; obtain allocation detail, remittance advice, or batch breakdown.",
            "internal_closure_without_external": "Internal closure exists; obtain external bank, factoring/operator, or documented compensation evidence tied to the row.",
            "internal_booking_open_support": "Open-item position is supported by internal booking/open balance; to overturn it, obtain row-specific external closing evidence.",
            "grouped_open_amount_internal_booking_support": "Open-item position is supported by an aggregate internal booking that matches the sum of same-document open-item rows; to overturn it, obtain row-specific external closing evidence.",
            "internal_accounting_only": "Internal ledger/journal entry exists; obtain external bank, factoring/operator, or documented compensation evidence.",
            "compensation_needs_external_support": "Compensation/set-off is indicated but the configured rule requires bank/external support.",
            "default": "Obtain document-specific evidence: bank movement, factoring/operator statement, documented compensation, or source ledger detail.",
        },
    },
    "fr": {
        "output_labels": {
            "metadata_field": "Champ",
            "metadata_value": "Valeur",
            "conclusion": "Conclusions",
            "summary": "Synthèse",
            "assumptions": "Hypothèses",
            "next_steps": "Prochaines étapes",
            "fallback_narrative": "Le détail ligne par ligne de la réconciliation est contenu dans le fichier Excel.",
            "excel_authority": "Le classeur Excel constitue le workpaper audit fiable au niveau ligne.",
        },
        "role_keywords": {
            "bank_statement": (
                "relevé bancaire",
                "releve bancaire",
                "extrait de compte",
                "banque",
            ),
            "factoring_statement": (
                "affacturage",
                "factor",
                "avance",
                "cession de créance",
                "cession de creance",
                "sans recours",
            ),
            "payment_order": ("ordre de paiement", "lot de paiement", "remise"),
            "journal": ("journal", "livre journal"),
            "ledger": ("grand livre", "auxiliaire", "compte auxiliaire", "ledger"),
            "open_items": (
                "postes ouverts",
                "parties ouvertes",
                "compte client",
                "compte fournisseur",
            ),
        },
        "side_keywords": {
            "customer": ("client", "clients", "créance", "creance"),
            "supplier": ("fournisseur", "fournisseurs", "dette"),
        },
        "evidence_keywords": {
            "invoice": ("facture", "factures"),
            "closure": (
                "payée",
                "payee",
                "payé",
                "paye",
                "encaissée",
                "encaissee",
                "réglée",
                "reglee",
                "soldée",
                "soldee",
            ),
            "compensation": ("compensation",),
            "netting": ("netting",),
            "factoring": (
                "affacturage",
                "factor",
                "sans recours",
                "cession",
                "cession de créance",
                "cession de creance",
                "avance",
            ),
            "bank": (
                "banque",
                "relevé bancaire",
                "releve bancaire",
                "extrait de compte",
                "virement",
                "sepa",
                "paiement",
                "encaissement",
            ),
            "batch": ("lot", "remise"),
        },
        "payment_order_terms": {
            "header": ("ordre de paiement", "lot de paiement", "remise"),
            "date_prefix": ("du", "date"),
            "total": ("total ordre", "total remise", "total lot"),
            "value_date": ("date de valeur", "valeur"),
            "invoice": ("facture",),
        },
        "next_steps": {
            "needs_evidence": "Obtenir les justificatifs complémentaires demandés pour les lignes concernées.",
            "unresolved": "Rattacher les lignes non résolues aux documents sources ou demander un détail comptable complémentaire.",
            "complete": "Conserver le classeur Excel et les sources comme workpaper audit.",
        },
        "missing_evidence": {},
    },
    "de": {
        "output_labels": {
            "metadata_field": "Feld",
            "metadata_value": "Wert",
            "conclusion": "Ergebnisse",
            "summary": "Zusammenfassung",
            "assumptions": "Annahmen",
            "next_steps": "Nächste Schritte",
            "fallback_narrative": "Die zeilenweise Abstimmung ist in der Excel-Arbeitsmappe enthalten.",
            "excel_authority": "Die Excel-Arbeitsmappe ist das maßgebliche zeilenweise Audit-Workpaper.",
        },
        "role_keywords": {
            "bank_statement": ("kontoauszug", "bankauszug", "bankkonto", "bank"),
            "factoring_statement": (
                "factoring",
                "factor",
                "vorschuss",
                "abtretung",
                "forderungsabtretung",
                "ohne regress",
            ),
            "payment_order": (
                "zahlungsauftrag",
                "zahlungslauf",
                "sammelzahlung",
                "remittance",
            ),
            "journal": ("journal", "hauptjournal", "buchungsjournal"),
            "ledger": (
                "hauptbuch",
                "konto",
                "kontoblatt",
                "debitorenkonto",
                "kreditorenkonto",
                "ledger",
            ),
            "open_items": (
                "offene posten",
                "offener posten",
                "op-liste",
                "debitoren",
                "kreditoren",
            ),
        },
        "side_keywords": {
            "customer": ("kunde", "kunden", "debitor", "debitoren", "forderung"),
            "supplier": (
                "lieferant",
                "lieferanten",
                "kreditor",
                "kreditoren",
                "verbindlichkeit",
            ),
        },
        "evidence_keywords": {
            "invoice": ("rechnung", "rechnungen"),
            "closure": (
                "bezahlt",
                "gezahlt",
                "beglichen",
                "ausgeglichen",
                "geschlossen",
                "eingegangen",
                "vereinnahmt",
            ),
            "compensation": ("kompensation", "verrechnung", "aufrechnung"),
            "netting": ("netting",),
            "factoring": (
                "factoring",
                "factor",
                "ohne regress",
                "abtretung",
                "forderungsabtretung",
                "vorschuss",
            ),
            "bank": (
                "bank",
                "kontoauszug",
                "bankauszug",
                "überweisung",
                "ueberweisung",
                "sepa",
                "zahlung",
                "eingang",
                "einzug",
            ),
            "batch": ("zahlungslauf", "sammelzahlung", "batch"),
        },
        "payment_order_terms": {
            "header": ("zahlungsauftrag", "zahlungslauf", "sammelzahlung"),
            "date_prefix": ("vom", "datum"),
            "total": ("summe zahlungsauftrag", "summe zahlungslauf", "gesamtbetrag"),
            "value_date": ("valuta", "wertstellung"),
            "invoice": ("rechnung",),
        },
        "next_steps": {
            "needs_evidence": "Für die noch nicht ausreichend belegten Zeilen die angeforderten Nachweise beschaffen.",
            "unresolved": "Ungeklärte Zeilen den Quelldokumenten zuordnen oder zusätzliche Buchhaltungsdetails anfordern.",
            "complete": "Excel-Arbeitsmappe und Quelldokumente als Audit-Workpaper aufbewahren.",
        },
        "missing_evidence": {
            "payment_order_only": "Der Zahlungsauftrag ist nur ein Brückennachweis; Bankauszug, Bankbeleg oder Factor-/Operator-Auszug zum Zahlungslauf beschaffen.",
            "payment_order_amount_mismatch": "Der Zahlungsauftrag liegt vor, aber der zugeordnete Betrag stimmt nicht mit der offenen Position überein; Zuordnungsdetail oder Teilzahlung bestätigen.",
            "factoring_bridge_only": "Die Factor-/Operator-Buchung ist nur ein Brückennachweis; Bankauszug oder Operator-Auszug zur Rechnung oder zum Zahlungslauf beschaffen.",
            "unallocated_external_bank_requires_allocation": "Eine Bankbewegung liegt vor, ist aber nicht der konkreten Rechnung/offenen Position zugeordnet; Zuordnungsdetail, Zahlungsavis oder Aufschlüsselung des Zahlungslaufs beschaffen.",
            "internal_closure_without_external": "Eine interne Ausgleichsbuchung liegt vor; externen Bank-, Factor-/Operator- oder dokumentierten Verrechnungsnachweis zur Zeile beschaffen.",
            "internal_booking_open_support": "Die offene Position wird durch interne Buchung/offenen Saldo gestützt; zur Widerlegung ist zeilenspezifischer externer Ausgleichsnachweis erforderlich.",
            "grouped_open_amount_internal_booking_support": "Die offene Position wird durch eine aggregierte interne Buchung gestützt, die der Summe der offenen Positionen desselben Dokuments entspricht; zur Widerlegung ist zeilenspezifischer externer Ausgleichsnachweis erforderlich.",
            "internal_accounting_only": "Es liegt nur eine interne Hauptbuch-/Journalbuchung vor; externen Bank-, Factor-/Operator- oder dokumentierten Verrechnungsnachweis beschaffen.",
            "compensation_needs_external_support": "Eine Verrechnung ist erkennbar, aber die konfigurierte Regel verlangt Bank-/externen Nachweis.",
            "default": "Dokumentspezifischen Nachweis beschaffen: Bankbewegung, Factor-/Operator-Auszug, dokumentierte Verrechnung oder Quelldetail aus dem Hauptbuch.",
        },
    },
    "es": {
        "output_labels": {
            "metadata_field": "Campo",
            "metadata_value": "Valor",
            "conclusion": "Conclusiones",
            "summary": "Resumen",
            "assumptions": "Supuestos",
            "next_steps": "Próximos pasos",
            "fallback_narrative": "El detalle línea por línea de la conciliación se encuentra en el archivo Excel.",
            "excel_authority": "El libro Excel es el papel de trabajo de auditoría de referencia a nivel de línea.",
        },
        "role_keywords": {
            "bank_statement": (
                "extracto bancario",
                "estado de cuenta",
                "cuenta bancaria",
                "banco",
            ),
            "factoring_statement": (
                "factoring",
                "factor",
                "anticipo",
                "cesión de créditos",
                "cesion de creditos",
                "sin recurso",
            ),
            "payment_order": (
                "orden de pago",
                "lote de pagos",
                "remesa de pagos",
            ),
            "journal": ("diario", "libro diario"),
            "ledger": (
                "libro mayor",
                "mayor contable",
                "auxiliar",
                "ficha de cuenta",
            ),
            "open_items": (
                "partidas abiertas",
                "partida abierta",
                "estado de cliente",
                "estado de proveedor",
                "mayor de clientes",
                "mayor de proveedores",
            ),
        },
        "side_keywords": {
            "customer": ("cliente", "clientes", "cuenta por cobrar"),
            "supplier": ("proveedor", "proveedores", "cuenta por pagar"),
        },
        "evidence_keywords": {
            "invoice": ("factura", "facturas"),
            "closure": (
                "pagada",
                "pagado",
                "cobrada",
                "cobrado",
                "liquidada",
                "liquidado",
                "cerrada",
                "cerrado",
                "compensada",
                "compensado",
            ),
            "compensation": ("compensación", "compensacion"),
            "netting": ("netting", "compensación de saldos"),
            "factoring": (
                "factoring",
                "factor",
                "sin recurso",
                "cesión",
                "cesion",
                "cesión de créditos",
                "cesion de creditos",
                "anticipo",
            ),
            "bank": (
                "banco",
                "extracto bancario",
                "estado de cuenta",
                "transferencia",
                "sepa",
                "pago",
                "cobro",
                "ingreso",
            ),
            "batch": ("lote", "remesa", "lote de pagos"),
        },
        "payment_order_terms": {
            "header": ("orden de pago", "lote de pagos", "remesa de pagos"),
            "date_prefix": ("del", "fecha"),
            "total": ("total orden de pago", "total remesa", "total lote"),
            "value_date": ("fecha valor", "valor"),
            "invoice": ("factura",),
        },
        "next_steps": {
            "probable_payment": "Revisar las líneas clasificadas como probable_payment: existe un movimiento bancario probable, pero debe confirmarse la asignación a la línea o documento antes de tratarlo como cierre definitivo.",
            "needs_evidence": "Obtener los justificantes adicionales solicitados para las líneas correspondientes.",
            "unresolved": "Asignar las líneas no resueltas a los documentos fuente o solicitar detalle contable adicional.",
            "complete": "Conservar el libro Excel y los documentos fuente como papel de trabajo de auditoría.",
        },
        "missing_evidence": {
            "probable_bank_payment_candidate": "Hay un pago bancario probable vinculado a esta línea; verifique el lote de pagos, la descripción bancaria o el detalle de asignación antes de tratarlo como cierre definitivo.",
            "payment_order_only": "La orden de pago solo constituye evidencia puente; obtenga el extracto bancario, el justificante bancario o el extracto del factor u operador vinculado al lote.",
            "payment_order_amount_mismatch": "Existe una orden de pago, pero el importe asignado no coincide con la partida abierta; obtenga el detalle de asignación o confirme la liquidación parcial.",
            "factoring_bridge_only": "El asiento del factor u operador solo constituye evidencia puente; obtenga el extracto bancario o del operador vinculado a la factura o al lote.",
            "unallocated_external_bank_requires_allocation": "Existe un movimiento bancario, pero no está asignado a la factura o partida abierta concreta; obtenga el detalle de asignación, el aviso de pago o el desglose del lote.",
            "internal_closure_without_external": "Existe un cierre interno; obtenga evidencia externa bancaria, del factor u operador, o de una compensación documentada vinculada a la línea.",
            "internal_booking_open_support": "La posición abierta está respaldada por un asiento o saldo interno; para rebatirla se necesita evidencia externa de cierre específica de la línea.",
            "grouped_open_amount_internal_booking_support": "La posición abierta está respaldada por un asiento interno agregado que coincide con la suma de las partidas abiertas del mismo documento; para rebatirla se necesita evidencia externa de cierre específica de la línea.",
            "internal_accounting_only": "Solo existe un asiento interno de mayor o diario; obtenga evidencia externa bancaria, del factor u operador, o de una compensación documentada.",
            "compensation_needs_external_support": "Se indica una compensación, pero la regla configurada exige soporte bancario o externo.",
            "default": "Obtenga evidencia específica del documento: movimiento bancario, extracto del factor u operador, compensación documentada o detalle del libro mayor fuente.",
        },
    },
}


def normalize_language(language: object | None, default: str = DEFAULT_LANGUAGE) -> str:
    text = str(language or default).strip().lower().replace("_", "-")
    code = text.split("-", 1)[0]
    return code if code in SUPPORTED_LANGUAGES else default


def language_candidates(language: object | None = None) -> tuple[str, ...]:
    text = str(language or "").strip().lower().replace("_", "-")
    if not text or text == "auto":
        return SUPPORTED_LANGUAGES
    return (normalize_language(text),)


def configured_language(
    assumptions: dict[str, Any] | None = None, *, purpose: str = "document"
) -> str:
    active = assumptions or {}
    if purpose == "report":
        return normalize_language(
            active.get("report_language") or active.get("language") or DEFAULT_LANGUAGE
        )
    doc_language = active.get("document_language")
    if doc_language and str(doc_language).lower() != "auto":
        return normalize_language(doc_language)
    return normalize_language(
        active.get("report_language") or active.get("language") or DEFAULT_LANGUAGE
    )


def language_pack(language: object | None = None) -> dict[str, Any]:
    code = normalize_language(language)
    pack = LANGUAGE_PACKS[code]
    if code in {"fr"}:
        merged = dict(pack)
        missing = dict(LANGUAGE_PACKS["en"]["missing_evidence"])
        missing.update(pack.get("missing_evidence") or {})
        merged["missing_evidence"] = missing
        return merged
    return pack


def output_labels(language: object | None = None) -> dict[str, str]:
    labels = dict(BASE_OUTPUT_LABELS)
    labels.update(language_pack(language).get("output_labels") or {})
    return labels


def missing_evidence_messages(language: object | None = None) -> dict[str, str]:
    return dict(
        language_pack(language).get("missing_evidence")
        or LANGUAGE_PACKS["en"]["missing_evidence"]
    )


def keyword_tuple(
    language: object | None,
    section: str,
    key: str,
    extra: list[str] | tuple[str, ...] | None = None,
) -> tuple[str, ...]:
    pack = language_pack(language)
    values = tuple(
        str(value).lower() for value in (pack.get(section, {}).get(key) or ())
    )
    if extra:
        values += tuple(str(value).lower() for value in extra if str(value).strip())
    return values


def any_keyword_in(text: object, keywords: list[str] | tuple[str, ...]) -> bool:
    value = re.sub(r"[_/-]+", " ", str(text or "").lower())
    return any(
        (normalized := re.sub(r"[_/-]+", " ", str(keyword).lower()).strip())
        and normalized in value
        for keyword in keywords
    )


# Fixed presentation labels only; these do not classify or interpret evidence.
WORD_OUTPUT_LABELS: dict[str, dict[str, str]] = {
    "status": {
        "it": "Esito",
        "en": "Status",
        "fr": "Résultat",
        "de": "Ergebnis",
        "es": "Estado",
    },
    "rows": {
        "it": "Righe",
        "en": "Rows",
        "fr": "Lignes",
        "de": "Zeilen",
        "es": "Líneas",
    },
    "amount": {
        "it": "Importo",
        "en": "Amount",
        "fr": "Montant",
        "de": "Betrag",
        "es": "Importe",
    },
    "amount_total": {
        "it": "Importo totale",
        "en": "Amount total",
        "fr": "Montant total",
        "de": "Gesamtbetrag",
        "es": "Importe total",
    },
    "amount_abs_total": {
        "it": "Importo assoluto",
        "en": "Absolute amount",
        "fr": "Montant absolu",
        "de": "Absolutbetrag",
        "es": "Importe absoluto",
    },
    "closed_amount": {
        "it": "Importo chiuso",
        "en": "Closed amount",
        "fr": "Montant soldé",
        "de": "Ausgeglichener Betrag",
        "es": "Importe cerrado",
    },
    "probable_payment_amount": {
        "it": "Importo con pagamento probabile",
        "en": "Probable payment amount",
        "fr": "Paiement probable",
        "de": "Vermutlich bezahlt",
        "es": "Pago probable",
    },
    "open_supported_amount": {
        "it": "Importo aperto supportato",
        "en": "Open supported amount",
        "fr": "Montant ouvert justifié",
        "de": "Belegter offener Betrag",
        "es": "Importe abierto respaldado",
    },
    "needs_evidence_amount": {
        "it": "Importo da documentare",
        "en": "Needs evidence amount",
        "fr": "Montant à justifier",
        "de": "Zu belegender Betrag",
        "es": "Importe por documentar",
    },
    "unresolved_amount": {
        "it": "Importo non risolto",
        "en": "Unresolved amount",
        "fr": "Montant non résolu",
        "de": "Ungeklärter Betrag",
        "es": "Importe no resuelto",
    },
    "reference_date": {
        "it": "Data di riferimento",
        "en": "Reference date",
        "fr": "Date de référence",
        "de": "Stichtag",
        "es": "Fecha de referencia",
    },
    "aging_bucket": {
        "it": "Fascia aging",
        "en": "Aging band",
        "fr": "Tranche d’ancienneté",
        "de": "Altersgruppe",
        "es": "Tramo de antigüedad",
    },
    "min_age_days": {
        "it": "Giorni minimi",
        "en": "Minimum age in days",
        "fr": "Ancienneté minimale (jours)",
        "de": "Mindestalter (Tage)",
        "es": "Antigüedad mínima (días)",
    },
    "max_age_days": {
        "it": "Giorni massimi",
        "en": "Maximum age in days",
        "fr": "Ancienneté maximale (jours)",
        "de": "Höchstalter (Tage)",
        "es": "Antigüedad máxima (días)",
    },
    "external_category": {
        "it": "Categoria evidenza esterna",
        "en": "External category",
        "fr": "Catégorie de preuve externe",
        "de": "Externe Belegart",
        "es": "Categoría de evidencia externa",
    },
    "cash_flow_signed_total": {
        "it": "Flusso di cassa netto",
        "en": "Net cash flow",
        "fr": "Flux de trésorerie net",
        "de": "Nettozahlungsfluss",
        "es": "Flujo de caja neto",
    },
    "settlement_effect_signed_net_debit_minus_credit": {
        "it": "Effetto sul saldo dare meno avere",
        "en": "Settlement effect: debit less " "credit",
        "fr": "Effet sur le solde débit moins " "crédit",
        "de": "Saldoeffekt: Soll minus Haben",
        "es": "Efecto en saldo: debe menos " "haber",
    },
    "reconciliation_status": {
        "it": "Esito riconciliazione",
        "en": "Reconciliation status",
        "fr": "Résultat du rapprochement",
        "de": "Abstimmungsergebnis",
        "es": "Estado de conciliación",
    },
    "rule_applied": {
        "it": "Regola applicata",
        "en": "Rule applied",
        "fr": "Règle appliquée",
        "de": "Angewandte Regel",
        "es": "Regla aplicada",
    },
    "closed": {
        "it": "Chiusa da evidenza",
        "en": "Cleared by evidence",
        "fr": "Soldée sur justificatif",
        "de": "Durch Beleg ausgeglichen",
        "es": "Cerrada por evidencia",
    },
    "unresolved": {
        "it": "Non risolta",
        "en": "Unresolved",
        "fr": "Non résolue",
        "de": "Ungeklärt",
        "es": "No resuelta",
    },
    "partially_paid": {
        "it": "Pagata parzialmente",
        "en": "Partially paid",
        "fr": "Partiellement réglée",
        "de": "Teilweise bezahlt",
        "es": "Pagada parcialmente",
    },
    "needs_evidence": {
        "it": "Serve evidenza aggiuntiva",
        "en": "Additional evidence needed",
        "fr": "Justificatif complémentaire requis",
        "de": "Zusätzlicher Beleg erforderlich",
        "es": "Requiere evidencia adicional",
    },
    "probable_payment": {
        "it": "Pagamento probabile da verificare",
        "en": "Probable payment to verify",
        "fr": "Paiement probable à vérifier",
        "de": "Vermutete Zahlung prüfen",
        "es": "Pago probable pendiente de verificación",
    },
    "open_supported": {
        "it": "Aperta ma supportata",
        "en": "Open with supporting evidence",
        "fr": "Ouverte avec justificatif",
        "de": "Offen mit Beleg",
        "es": "Abierta pero respaldada",
    },
    "out_of_scope": {
        "it": "Fuori perimetro",
        "en": "Outside scope",
        "fr": "Hors périmètre",
        "de": "Außerhalb des Umfangs",
        "es": "Fuera de alcance",
    },
    "external_bank_match": {
        "it": "Movimento bancario esterno abbinato",
        "en": "Matched bank movement",
        "fr": "Mouvement bancaire rapproché",
        "de": "Zugeordnete Bankbewegung",
        "es": "Movimiento bancario externo conciliado",
    },
    "counterparty_bank_other": {
        "it": "Altri movimenti bancari della controparte",
        "en": "Other counterparty bank movements",
        "fr": "Autres mouvements bancaires de la contrepartie",
        "de": "Sonstige Bankbewegungen der Gegenpartei",
        "es": "Otros movimientos bancarios de la contraparte",
    },
    "TOTAL": {
        "it": "Totale",
        "en": "Total",
        "fr": "Total",
        "de": "Gesamt",
        "es": "Total",
    },
    "source_role": {
        "it": "Ruolo fonte",
        "en": "Source role",
        "fr": "Type de source",
        "de": "Quellenart",
        "es": "Tipo de fuente",
    },
    "files": {
        "it": "File",
        "en": "Files",
        "fr": "Fichiers",
        "de": "Dateien",
        "es": "Archivos",
    },
    "bank_statement": {
        "it": "Estratto conto bancario",
        "en": "Bank statement",
        "fr": "Relevé bancaire",
        "de": "Kontoauszug",
        "es": "Extracto bancario",
    },
    "open_items": {
        "it": "Partite aperte",
        "en": "Open items",
        "fr": "Postes ouverts",
        "de": "Offene Posten",
        "es": "Partidas abiertas",
    },
    "evidence_type": {
        "it": "Tipo evidenza",
        "en": "Evidence type",
        "fr": "Type de justificatif",
        "de": "Belegart",
        "es": "Tipo de evidencia",
    },
    "Bank": {"it": "Banca", "en": "Bank", "fr": "Banque", "de": "Bank", "es": "Banco"},
    "No evidence": {
        "it": "Nessuna evidenza",
        "en": "No evidence",
        "fr": "Aucun justificatif",
        "de": "Kein Beleg",
        "es": "Sin evidencia",
    },
    "share_of_abs_amount_percent": {
        "it": "Peso %",
        "en": "Share %",
        "fr": "Part %",
        "de": "Anteil %",
        "es": "Peso %",
    },
    "Priority": {
        "it": "Priorita",
        "en": "Priority",
        "fr": "Priorité",
        "de": "Priorität",
        "es": "Prioridad",
    },
    "Document": {
        "it": "Documento",
        "en": "Document",
        "fr": "Document",
        "de": "Beleg",
        "es": "Documento",
    },
    "Days": {"it": "Giorni", "en": "Days", "fr": "Jours", "de": "Tage", "es": "Días"},
    "Reason": {
        "it": "Motivo",
        "en": "Reason",
        "fr": "Motif",
        "de": "Grund",
        "es": "Motivo",
    },
    "evidence_none": {
        "it": "Nessuna evidenza collegata",
        "en": "No linked evidence",
        "fr": "Aucun justificatif lié",
        "de": "Kein zugeordneter Beleg",
        "es": "Sin evidencia vinculada",
    },
    "open_amount": {
        "it": "Importo aperto",
        "en": "Open amount",
        "fr": "Montant ouvert",
        "de": "Offener Betrag",
        "es": "Importe abierto",
    },
    "open_rows": {
        "it": "Righe aperte",
        "en": "Open rows",
        "fr": "Postes ouverts (lignes)",
        "de": "Offene Zeilen",
        "es": "Líneas abiertas",
    },
    "Ledger": {
        "it": "Mastro",
        "en": "Ledger",
        "fr": "Grand livre",
        "de": "Sachkonto",
        "es": "Mayor",
    },
    "Journal": {
        "it": "Giornale",
        "en": "Journal",
        "fr": "Journal",
        "de": "Buchungsjournal",
        "es": "Diario",
    },
    "Payment orders": {
        "it": "Distinte",
        "en": "Payment orders",
        "fr": "Ordres de paiement",
        "de": "Zahlungsaufträge",
        "es": "Órdenes de pago",
    },
    "Factor": {
        "it": "Factor",
        "en": "Factor",
        "fr": "Factor",
        "de": "Factor",
        "es": "Factor",
    },
    "Note": {"it": "Nota", "en": "Note", "fr": "Note", "de": "Hinweis", "es": "Nota"},
    "Timing": {
        "it": "Periodo",
        "en": "Timing",
        "fr": "Période",
        "de": "Zeitraum",
        "es": "Periodo",
    },
    "before_cutoff": {
        "it": "Prima del cut-off",
        "en": "Before cutoff",
        "fr": "Avant la date de référence",
        "de": "Vor dem Stichtag",
        "es": "Antes de la fecha de corte",
    },
    "after_cutoff": {
        "it": "Dopo il cut-off",
        "en": "After cutoff",
        "fr": "Après la date de référence",
        "de": "Nach dem Stichtag",
        "es": "Después de la fecha de corte",
    },
    "on_cutoff": {
        "it": "Alla data di cut-off",
        "en": "On cutoff",
        "fr": "À la date de référence",
        "de": "Am Stichtag",
        "es": "En la fecha de corte",
    },
    "Record refs": {
        "it": "Riferimenti alle righe",
        "en": "Record refs",
        "fr": "Références des lignes",
        "de": "Zeilenverweise",
        "es": "Referencias de líneas",
    },
    "check": {
        "it": "Controllo",
        "en": "Check",
        "fr": "Contrôle",
        "de": "Prüfung",
        "es": "Control",
    },
    "checks": {
        "it": "Controlli",
        "en": "Checks",
        "fr": "Contrôles",
        "de": "Prüfungen",
        "es": "Controles",
    },
    "expected": {
        "it": "Atteso",
        "en": "Expected",
        "fr": "Attendu",
        "de": "Erwartet",
        "es": "Esperado",
    },
    "actual": {
        "it": "Riscontrato",
        "en": "Actual",
        "fr": "Constaté",
        "de": "Festgestellt",
        "es": "Observado",
    },
    "notes": {
        "it": "Note",
        "en": "Notes",
        "fr": "Notes",
        "de": "Hinweise",
        "es": "Notas",
    },
    "PASS": {
        "it": "OK",
        "en": "Pass",
        "fr": "Conforme",
        "de": "Bestanden",
        "es": "Correcta",
    },
    "FAIL": {
        "it": "Errore",
        "en": "Fail",
        "fr": "Échec",
        "de": "Fehler",
        "es": "Error",
    },
    "WARN": {
        "it": "Avviso",
        "en": "Warning",
        "fr": "Avertissement",
        "de": "Zu prüfender Hinweis",
        "es": "Aviso",
    },
    "PENDING": {
        "it": "Da completare",
        "en": "Pending",
        "fr": "À compléter",
        "de": "Ausstehend",
        "es": "Pendiente",
    },
    "review_status": {
        "it": "Esito revisione",
        "en": "Review status",
        "fr": "État de la revue",
        "de": "Prüfstatus",
        "es": "Estado de revisión",
    },
    "codex_review_completed": {
        "it": "Revisione delle righe completata",
        "en": "Row review completed",
        "fr": "Revue des lignes terminée",
        "de": "Zeilenprüfung abgeschlossen",
        "es": "Revisión de líneas completada",
    },
    "scope_year": {
        "it": "Esercizio",
        "en": "Scope year",
        "fr": "Exercice",
        "de": "Geschäftsjahr",
        "es": "Ejercicio",
    },
    "cutoff_date": {
        "it": "Data di cut-off",
        "en": "Cutoff date",
        "fr": "Date de référence",
        "de": "Stichtag",
        "es": "Fecha de corte",
    },
    "report_language": {
        "it": "Lingua del rapporto",
        "en": "Report language",
        "fr": "Langue du rapport",
        "de": "Berichtssprache",
        "es": "Idioma del informe",
    },
    "document_language": {
        "it": "Lingua dei documenti",
        "en": "Document language",
        "fr": "Langue des documents",
        "de": "Dokumentsprache",
        "es": "Idioma de los documentos",
    },
    "currency": {
        "it": "Valuta",
        "en": "Currency",
        "fr": "Devise",
        "de": "Währung",
        "es": "Moneda",
    },
    "post_cutoff_events_excluded": {
        "it": "Eventi post cut-off esclusi dalla chiusura",
        "en": "Post-cutoff events excluded from clearing",
        "fr": "Événements ultérieurs exclus du solde à la date de " "référence",
        "de": "Spätere Ereignisse vom Ausgleich zum Stichtag " "ausgeschlossen",
        "es": "Eventos posteriores excluidos del cierre",
    },
    "payment_orders_are_bank_evidence": {
        "it": "Distinte trattate come evidenza bancaria",
        "en": "Payment orders treated as bank evidence",
        "fr": "Ordres de paiement traités comme justificatifs " "bancaires",
        "de": "Zahlungsaufträge als Bankbeleg behandelt",
        "es": "Órdenes de pago tratadas como evidencia " "bancaria",
    },
    "factoring_pro_soluto_closes_item": {
        "it": "Factoring pro-soluto chiude con evidenza esterna " "allocata",
        "en": "Non-recourse factoring clears with allocated " "external evidence",
        "fr": "Affacturage sans recours soldé avec justificatif " "externe affecté",
        "de": "Echtes Factoring gleicht mit zugeordnetem " "externem Beleg aus",
        "es": "Factoring sin recurso cierra con evidencia " "externa asignada",
    },
    "compensation_requires_bank": {
        "it": "Compensazione richiede evidenza bancaria",
        "en": "Set-off requires bank evidence",
        "fr": "Compensation nécessitant un justificatif bancaire",
        "de": "Aufrechnung erfordert Bankbeleg",
        "es": "La compensación requiere evidencia bancaria",
    },
    "assurance_run_date": {
        "it": "Data dei controlli",
        "en": "Check date",
        "fr": "Date des contrôles",
        "de": "Prüfdatum",
        "es": "Fecha de controles",
    },
    "counterparty_keywords": {
        "it": "Parole chiave della controparte",
        "en": "Counterparty keywords",
        "fr": "Mots-clés de la contrepartie",
        "de": "Suchbegriffe der Gegenpartei",
        "es": "Palabras clave de la contraparte",
    },
    "ocr_scanned": {
        "it": "OCR su scansioni",
        "en": "OCR for scans",
        "fr": "OCR des scans",
        "de": "OCR für Scans",
        "es": "OCR de documentos escaneados",
    },
    "input_dir": {
        "it": "Cartella delle fonti",
        "en": "Source folder",
        "fr": "Dossier des sources",
        "de": "Quellenordner",
        "es": "Carpeta de fuentes",
    },
    "client_id": {
        "it": "Riferimento cliente",
        "en": "Client reference",
        "fr": "Référence client",
        "de": "Mandantenreferenz",
        "es": "Referencia del cliente",
    },
    "engagement_id": {
        "it": "Riferimento incarico",
        "en": "Engagement reference",
        "fr": "Référence de mission",
        "de": "Auftragsreferenz",
        "es": "Referencia del encargo",
    },
    "run_id": {
        "it": "Riferimento esecuzione",
        "en": "Run reference",
        "fr": "Référence d’exécution",
        "de": "Laufreferenz",
        "es": "Referencia de ejecución",
    },
    "Run Metadata": {
        "it": "Metadati run",
        "en": "Run Metadata",
        "fr": "Références de l’exécution",
        "de": "Angaben zum Lauf",
        "es": "Metadatos de la ejecución",
    },
    "Rule Detail": {
        "it": "Dettaglio regole",
        "en": "Rule Detail",
        "fr": "Détail des règles",
        "de": "Angewandte Regeln",
        "es": "Detalle de reglas",
    },
    "Deterministic reconciliation workpaper": {
        "it": "Workpaper di riconciliazione " "deterministica",
        "en": "Deterministic reconciliation workpaper",
        "fr": "Dossier de rapprochement selon les règles " "appliquées",
        "de": "Abstimmung nach den angewandten Regeln",
        "es": "Papel de trabajo de conciliación " "determinista",
    },
    "Partial payments and outstanding balances": {
        "it": "Pagamenti parziali e residui aperti",
        "en": "Partial payments and outstanding " "balances",
        "fr": "Paiements partiels et soldes ouverts",
        "de": "Teilzahlungen und offene Restbeträge",
        "es": "Pagos parciales y saldos pendientes",
    },
    "True": {"it": "Sì", "en": "Yes", "fr": "Oui", "de": "Ja", "es": "Sí"},
    "False": {"it": "No", "en": "No", "fr": "Non", "de": "Nein", "es": "No"},
}


WORD_OUTPUT_LABELS.update(
    {
        "source_note_external": {
            "it": "Documento presente anche in evidenza esterna.",
            "en": "Document also present in external evidence.",
            "fr": "Document également présent dans les justificatifs externes.",
            "de": "Beleg auch in externen Nachweisen vorhanden.",
            "es": "Documento también presente en evidencia externa.",
        },
        "source_note_bridge": {
            "it": "Documento presente in distinta/compensazione: verificare supporto "
            "di chiusura.",
            "en": "Document present in a payment order or set-off: verify the clearing "
            "evidence.",
            "fr": "Document présent dans un ordre de paiement ou une compensation : "
            "vérifier la preuve de règlement.",
            "de": "Beleg in einem Zahlungsauftrag oder einer Aufrechnung vorhanden: "
            "Ausgleichsnachweis prüfen.",
            "es": "Documento presente en una orden de pago o compensación: verificar "
            "la evidencia de cierre.",
        },
        "source_note_internal": {
            "it": "Documento presente solo in evidenza interna.",
            "en": "Document present only in internal evidence.",
            "fr": "Document présent uniquement dans les justificatifs internes.",
            "de": "Beleg nur in internen Nachweisen vorhanden.",
            "es": "Documento presente solo en evidencia interna.",
        },
        "source_note_unlinked": {
            "it": "Documento presente nelle partite aperte senza evidenza collegata.",
            "en": "Document present in open items without linked evidence.",
            "fr": "Document présent dans les postes ouverts sans justificatif lié.",
            "de": "Beleg in den offenen Posten ohne zugeordneten Nachweis vorhanden.",
            "es": "Documento presente en partidas abiertas sin evidencia vinculada.",
        },
        "source_note_evidence_only": {
            "it": "Documento presente solo nelle evidenze, non nella "
            "popolazione aperta.",
            "en": "Document present only in evidence, outside the open-item "
            "population.",
            "fr": "Document présent uniquement dans les justificatifs, hors de "
            "la population des postes ouverts.",
            "de": "Beleg nur in den Nachweisen, außerhalb des Bestands offener "
            "Posten vorhanden.",
            "es": "Documento presente solo en la evidencia, fuera de la "
            "población de partidas abiertas.",
        },
        "external_bank": {
            "it": "Banca",
            "en": "Bank",
            "fr": "Banque",
            "de": "Bank",
            "es": "Banco",
        },
        "open_item": {
            "it": "Partita aperta",
            "en": "Open item",
            "fr": "Poste ouvert",
            "de": "Offener Posten",
            "es": "Partida abierta",
        },
    }
)


WORD_OUTPUT_LABELS.update(
    {
        "Input folder": {
            "it": "Cartella delle fonti",
            "en": "Input folder",
            "fr": "Dossier des sources",
            "de": "Quellenordner",
            "es": "Carpeta de fuentes",
        },
        "Studio client ID": {
            "it": "Riferimento cliente dello studio",
            "en": "Studio client ID",
            "fr": "Référence client du cabinet",
            "de": "Mandantenreferenz der Kanzlei",
            "es": "Referencia del cliente del despacho",
        },
        "Run timestamp": {
            "it": "Data e ora di esecuzione",
            "en": "Run timestamp",
            "fr": "Date et heure d’exécution",
            "de": "Ausführungszeitpunkt",
            "es": "Fecha y hora de ejecución",
        },
    }
)


def canonical_word_label(value: object) -> object:
    """Resolve exact renderer-owned captions; leave source text untouched."""
    if not isinstance(value, str):
        return value
    normalized = value.casefold().replace("_", " ")
    for key, labels in WORD_OUTPUT_LABELS.items():
        aliases = (key, *labels.values())
        if any(normalized == alias.casefold().replace("_", " ") for alias in aliases):
            return key
    return value


def word_output_label(value: object, language: str) -> object:
    """Render one known caption without translating arbitrary evidence text."""
    key = canonical_word_label(str(value) if isinstance(value, bool) else value)
    if isinstance(key, str) and key in WORD_OUTPUT_LABELS:
        return WORD_OUTPUT_LABELS[key].get(language, WORD_OUTPUT_LABELS[key]["en"])
    return value
