"""Any other ERP — build the table at configuration time instead of shipping code.

"And other ERPs" is not a promise to write an adapter per customer. It is this function: the
integrator states which of their columns holds which fact, and that mapping IS the adapter.
No name guessing, no heuristics on column labels — a wrong guess about which column is the
document number would not fail loudly, it would produce records that quietly never pair with
the counterparty's.

It is also what the simulated companies in the testbed run on, which keeps the pilot honest:
the sandbox goes through the same code path a real unfamiliar ERP would.
"""

from __future__ import annotations

from .base import AdapterError, ErpAdapter, FieldMap, REFUSED_MISSING_FIELD

_REQUIRED_COLUMNS = ("company_code", "document_no", "doc_date", "doc_type")
_OPTIONAL_COLUMNS = (
    "fiscal_year", "amount", "currency", "lines",
    "line_sku", "line_quantity", "line_unit", "line_unit_price",
)


def generic_adapter(system_id: str, columns: dict[str, str | tuple[str, ...]],
                    doc_kinds: dict[str, str], *, amount_basis: str = "GROSS") -> ErpAdapter:
    """An adapter from an explicit column map.

        generic_adapter(
            "ACME-LEDGER",
            {"company_code": "org", "document_no": "doc_no", "doc_date": "issued",
             "amount": "total", "currency": "ccy", "doc_type": "kind"},
            {"order": "PURCHASE_ORDER", "bill": "INVOICE"},
        )

    `columns` values may be a single column name or a priority list. `doc_kinds` maps the
    customer's own type codes onto the canonical kinds — an unmapped code is refused, never
    passed through, because a type nobody declared cannot be matched against anything.
    """
    missing = [name for name in _REQUIRED_COLUMNS if name not in columns]
    if missing:
        raise AdapterError(
            REFUSED_MISSING_FIELD,
            f"{system_id}: the column map must name {list(_REQUIRED_COLUMNS)}; missing {missing}",
        )
    unknown = set(columns) - set(_REQUIRED_COLUMNS) - set(_OPTIONAL_COLUMNS)
    if unknown:
        raise AdapterError(
            REFUSED_MISSING_FIELD,
            f"{system_id}: unknown mapping key(s) {sorted(unknown)} — "
            f"known: {sorted(_REQUIRED_COLUMNS + _OPTIONAL_COLUMNS)}",
        )
    if not doc_kinds:
        raise AdapterError(REFUSED_MISSING_FIELD, f"{system_id}: doc_kinds must map at least one type code")

    def listed(name: str) -> tuple[str, ...]:
        value = columns.get(name, ())
        return (value,) if isinstance(value, str) else tuple(value)

    return ErpAdapter(FieldMap(
        system_id=system_id,
        company_code=listed("company_code"),
        document_no=listed("document_no"),
        fiscal_year=listed("fiscal_year"),
        doc_date=listed("doc_date"),
        amount=listed("amount"),
        currency=listed("currency"),
        doc_type=listed("doc_type"),
        lines=listed("lines"),
        line_sku=listed("line_sku"),
        line_quantity=listed("line_quantity"),
        line_unit=listed("line_unit"),
        line_unit_price=listed("line_unit_price"),
        doc_kinds=dict(doc_kinds),
        default_amount_basis=amount_basis,
    ))
