"""SAP S/4HANA — the field table, plus the bridge to the SAP door that already exists.

Two ways in, and both are read-only on the SAP side. NEOedge reads what SAP posted; it never
posts to SAP. That boundary is not a limitation to be lifted later — a module that could
write into a customer's books would be a system of record, and the whole proposition is that
we are not one.

  1. **Documents**, as SAP hands them over (OData, CDS view, IDoc, extractor — the shape is
     a flat dict of SAP field names, whichever transport delivered it).
  2. **OTLP attributes**, the `erp.*` form the ApeiroRA connector already speaks. That door
     is built and fail-closed; `from_otlp_attributes` converts its vocabulary into a draft
     so an installation that already emits telemetry needs no second integration.

SAP names one fact under several fields depending on the document type — a purchase order
is `EBELN`, a billing document is `VBELN`, an FI document is `BELNR` — so every entry is a
priority list rather than a single name.
"""

from __future__ import annotations

from .base import AdapterError, ErpAdapter, FieldMap, REFUSED_BAD_FIELD

SAP_S4HANA = FieldMap(
    system_id="SAP-S4HANA",
    company_code=("BUKRS", "CompanyCode"),
    document_no=("EBELN", "VBELN", "BELNR", "MBLNR", "PurchaseOrder", "BillingDocument", "AccountingDocument"),
    fiscal_year=("GJAHR", "FiscalYear"),
    doc_date=("BLDAT", "DocumentDate", "BUDAT", "PostingDate"),
    amount=("WRBTR", "NETWR", "RMWWR", "AmountInTransactionCurrency"),
    currency=("WAERS", "WAERK", "TransactionCurrency"),
    doc_type=("BSART", "BLART", "FKART", "NEOEDGE_DOCTYPE"),
    lines=("items", "to_Item", "LineItems"),
    line_sku=("MATNR", "Material", "sku"),
    line_quantity=("MENGE", "Quantity", "quantity"),
    line_unit=("MEINS", "BaseUnit", "unit"),
    line_unit_price=("NETPR", "NetPriceAmount", "unit_price"),
    doc_kinds={
        # purchasing
        "NB": "PURCHASE_ORDER", "EC": "PURCHASE_ORDER", "PO": "PURCHASE_ORDER",
        "AB": "ORDER_CONFIRMATION", "OC": "ORDER_CONFIRMATION",
        # logistics
        "LF": "DELIVERY_NOTE", "DN": "DELIVERY_NOTE",
        "WE": "GOODS_RECEIPT", "GR": "GOODS_RECEIPT",
        # financials
        "RE": "INVOICE", "F2": "INVOICE", "IN": "INVOICE",
        "KZ": "PAYMENT", "ZP": "PAYMENT", "PY": "PAYMENT",
        "G2": "CREDIT_MEMO", "CM": "CREDIT_MEMO",
        # the framework agreement the whole relation hangs from
        "MK": "CONTRACT", "SLA": "CONTRACT", "CT": "CONTRACT",
    },
)

# The `erp.*` attribute vocabulary already carried over OTLP by the ApeiroRA connector.
_OTLP_TO_SAP = {
    "erp.doc.company_code": "BUKRS",
    "erp.doc.number": "BELNR",
    "erp.doc.fiscal_year": "GJAHR",
    "erp.doc.type": "NEOEDGE_DOCTYPE",
    "erp.amount": "WRBTR",
    "erp.currency": "WAERS",
    "erp.posting_date": "BLDAT",
}

# `erp.doc.type` already carries canonical-ish names; map them onto our kinds explicitly
# rather than assuming the two vocabularies stay in step.
_OTLP_DOC_TYPES = {
    "PO": "NB",
    "GOODS_RECEIPT": "WE",
    "INVOICE": "RE",
    "PAYMENT": "KZ",
    "CREDIT_MEMO": "G2",
    "ORDER_CONFIRMATION": "AB",
    "DELIVERY_NOTE": "LF",
    "CONTRACT": "MK",
}


def from_otlp_attributes(attributes: dict) -> dict:
    """Convert `erp.*` OTLP attributes into a native SAP-shaped dict this adapter can read.

    Fail-closed and loud, the same contract the connector's mapper holds: an incomplete set
    produces no draft at all rather than a partial one. A partial record looks reconcilable
    and is not, which is worse than nothing.
    """
    required = ("erp.doc.company_code", "erp.doc.number", "erp.doc.type", "erp.posting_date")
    missing = [a for a in required if attributes.get(a) in (None, "")]
    native: dict = {}
    for attribute, sap_field in _OTLP_TO_SAP.items():
        if attributes.get(attribute) not in (None, ""):
            native[sap_field] = attributes[attribute]
    if missing:
        raise AdapterError(
            REFUSED_BAD_FIELD,
            f"incomplete erp.* attribute set, missing {missing} — no draft is produced "
            "(fail-closed: a partial record looks reconcilable and is not)",
        )
    doc_type = str(native.get("NEOEDGE_DOCTYPE", ""))
    native["NEOEDGE_DOCTYPE"] = _OTLP_DOC_TYPES.get(doc_type, doc_type)
    return native


def adapter() -> ErpAdapter:
    return ErpAdapter(SAP_S4HANA)
