"""Assemblage du plan de commissions depuis `bareme_spec.json`.

Entrée : le contrat barème (évènements référençant des `update_value` du workflow,
des `base_field` du DocType Dossier Vente et des `role_id` RBAC) + un
`RoleResolver` (id → nom Frappe issu de rbac_50_roles.json). Sortie : un bundle
déterministe (tri stable, aucun horodatage) → reproductible bit-à-bit à contrat
constant, donc diffable et re-générable en CI.

Contrainte #6 (zéro invention) : aucun taux n'est ajouté — le barème livré porte
`taux_pct: null`. Le builder n'ajoute que la résolution des noms de rôle et un
manifeste de traçabilité (compte des taux restant à confirmer inclus).
"""

from __future__ import annotations

import os
import sys
from typing import Any

# Réutilisation (workflow #5) du module CRM voisin : le résolveur de rôles
# (rbac_50_roles.json) est importé, jamais redéfini ici.
_HERE = os.path.dirname(os.path.abspath(__file__))
_CRM = os.path.normpath(os.path.join(_HERE, "..", ".."))  # 05_deliverables_mvp/crm/
if _CRM not in sys.path:
    sys.path.insert(0, _CRM)

from workflow_vente.wflib.rbac import RoleResolver  # noqa: E402


def _event_sort_key(ev: dict) -> tuple:
    """Ordre stable : par évènement de pipeline puis par rôle."""
    return (ev["update_value"], ev["role_id"])


def build_bundle(spec: dict, resolver: RoleResolver) -> dict[str, Any]:
    """Transforme le contrat barème en plan de commissions + manifeste."""
    events_spec = sorted(spec["evenements"], key=_event_sort_key)

    lignes: list[dict] = []
    for ev in events_spec:
        lignes.append({
            "update_value": ev["update_value"],
            "role_id": ev["role_id"],
            "erpnext_role_name": resolver.erpnext_name(ev["role_id"]),
            "base_field": ev["base_field"],
            "libelle": ev["libelle"],
            # Taux jamais fabriqué (#6) : recopié verbatim (null tant qu'à confirmer).
            "taux_pct": ev.get("taux_pct"),
            "source": ev.get("source"),
            "a_confirmer": bool(ev.get("a_confirmer")),
        })

    plan = {
        "name": spec["bareme_name"],
        "devise_field": spec["devise_field"],
        "evenements": lignes,
    }

    roles_used = sorted({ev["role_id"] for ev in events_spec})
    a_confirmer = sum(1 for l in lignes if l["a_confirmer"] or l["taux_pct"] is None)
    manifest = {
        "generated_from": "bareme_spec.json",
        "rbac_source": "rbac_50_roles.json",
        "workflow_source": "workflow_vente/workflow_vente_spec.json",
        "doctype_source": "dossier_vente/doctype_spec.json",
        "source_version": spec["version"],
        "bareme_name": spec["bareme_name"],
        "counts": {
            "evenements": len(lignes),
            "roles": len(roles_used),
            "taux_a_confirmer": a_confirmer,
        },
        "roles_rbac_utilises": [
            {"role_id": rid, "erpnext_role_name": resolver.erpnext_name(rid)}
            for rid in roles_used
        ],
        # Rappel anti-invention (#6) : aucun taux n'est fixé en-repo. La Direction
        # renseigne `taux_pct` + `source` avant tout calcul de commission réel.
        "note_taux": (
            "Aucun taux de commission n'est documenté dans CLAUDE.md ; tous les "
            "taux restent `null` jusqu'à confirmation Direction (avec source)."
        ),
    }

    return {"manifest": manifest, "commission_plan": plan}
