"""Calcul TRAÇABLE des commissions vendeurs (anti-invention · CLAUDE.md #6).

Même doctrine que `banclib/finance.py` : un calcul transparent, reproductible et
entièrement sourcé n'est PAS une invention — c'est de la modélisation auditable.
Chaque ligne de commission publie sa FORMULE avec sa valeur :

    montant = base × taux_pct

où `base` provient du Dossier Vente (un champ Currency réel, ex. montant_contrat)
et `taux_pct` provient du barème (fourni par la Direction AVEC sa source). Si l'un
des deux opérandes manque (`null` / placeholder), la valeur reste `None`
(placeholder, jamais 0-inventé) et la ligne est marquée `incomplete` — la formule
reste affichée. Aucun taux n'est jamais fabriqué : le barème livré porte
`taux_pct: null` tant que la Direction ne l'a pas confirmé.
"""

from __future__ import annotations

from typing import Any, Optional

from . import deps

is_filled = deps.is_filled


def _num(value: Any) -> Optional[float]:
    """Valeur numérique réelle, ou None si absente/placeholder/non numérique."""
    if not is_filled(value):
        return None
    try:
        return float(value)
    except (TypeError, ValueError):
        return None


def rate(value: Any) -> Optional[float]:
    """Taux de commission → fraction. Accepte 3.5 (nombre) ou « 3.5 % » (texte).

    Retourne None si absent/placeholder — jamais un taux par défaut fabriqué.
    """
    if not is_filled(value):
        return None
    if isinstance(value, bool):
        return None
    if isinstance(value, (int, float)):
        return float(value) / 100.0
    txt = str(value).replace("%", "").replace(",", ".").strip()
    try:
        return float(txt) / 100.0
    except ValueError:
        return None


def _rate_label(value: Any) -> str:
    """Libellé du taux tel qu'affiché dans la formule (verbatim si texte)."""
    if not is_filled(value):
        return "{taux_pct}"
    if isinstance(value, (int, float)) and not isinstance(value, bool):
        return f"{value:g} %"
    return str(value).strip()


def compute_line(dossier: dict, event: dict) -> dict:
    """Une ligne de commission traçable pour un évènement du barème.

    `dossier` : instance (partielle) d'un OTO Dossier Vente (base + devise).
    `event`   : un évènement du barème (update_value, role_id, base_field, taux).
    """
    base_field = event["base_field"]
    base = _num(dossier.get(base_field))
    taux = rate(event.get("taux_pct"))
    devise = dossier.get("devise")

    montant = base * taux if (base is not None and taux is not None) else None
    base_lbl = f"{base:g}" if base is not None else f"{{{base_field}}}"
    formule = f"{base_lbl} × {_rate_label(event.get('taux_pct'))}"

    manquants: list[str] = []
    if base is None:
        manquants.append(base_field)
    if taux is None:
        manquants.append("taux_pct")

    return {
        "update_value": event["update_value"],
        "role_id": event["role_id"],
        "base_field": base_field,
        "base": base,
        "taux_pct": taux,
        "devise": devise if is_filled(devise) else None,
        "montant": montant,
        "formule": formule,
        "incomplete": bool(manquants),
        "champs_manquants": manquants,
    }


def compute_dossier(dossier: dict, bareme: dict) -> list[dict]:
    """Toutes les lignes de commission d'un dossier (ordre = ordre du barème)."""
    return [compute_line(dossier, ev) for ev in bareme["evenements"]]
