"""Composition TRACABLE du numero e-NCF (anti-invention · CLAUDE.md #6).

Meme doctrine que `commlib/finance.py` / `banclib/finance.py` : une composition
transparente, reproductible et entierement sourcee n'est PAS une invention —
c'est de la modelisation auditable. Le e-NCF (Numero de Comprobante Fiscal
electronique) suit le format DGII :

    e-NCF = "E" + tipoeCF (2 chiffres) + secuencia (10 chiffres)   → 13 caracteres

La `secuencia` provient d'un rango autorise par la DGII (jamais fabriquee ici).
`compose_encf` reste donc `None` tant qu'un operande manque (placeholder, jamais
0-invente), et publie toujours sa FORMULE. Aucun tipo ni sequence n'est jamais
fabrique par defaut : le caller fournit des operandes reels (cote VPS/Compupar).
"""

from __future__ import annotations

import re
from typing import Any, Optional

from . import deps

is_filled = deps.is_filled

# Format e-NCF DGII (Ley 32-23) : E + tipo(2) + secuencia(10) = 13 caracteres.
ENCF_RE = re.compile(r"^E([0-9]{2})([0-9]{10})$")
FORMULE = "E + tipoeCF(2) + secuencia(10)"


def is_valid_encf(value: Any) -> bool:
    """True si `value` respecte exactement le format e-NCF DGII (13 car.)."""
    return bool(is_filled(value) and ENCF_RE.match(str(value).strip()))


def parse_encf(value: Any) -> Optional[dict]:
    """Decompose un e-NCF valide → {prefix, tipo, secuencia}. None sinon."""
    if not is_filled(value):
        return None
    m = ENCF_RE.match(str(value).strip())
    if not m:
        return None
    return {"prefix": "E", "tipo": m.group(1), "secuencia": m.group(2)}


def _tipo(value: Any) -> Optional[str]:
    """Un code de type sur 2 chiffres, ou None si absent/mal forme (jamais fabrique)."""
    if not is_filled(value):
        return None
    txt = str(value).strip()
    return txt if re.fullmatch(r"[0-9]{2}", txt) else None


def _secuencia(value: Any) -> Optional[str]:
    """Une secuencia numerique → 10 chiffres zero-paddes, ou None si absente/invalide.

    Ne fabrique jamais de sequence : `None` en entree reste `None` en sortie.
    """
    if not is_filled(value):
        return None
    txt = str(value).strip()
    if not re.fullmatch(r"[0-9]{1,10}", txt):
        return None
    return txt.zfill(10)


def compose_encf(tipo: Any, secuencia: Any) -> dict:
    """Compose un e-NCF tracable a partir d'operandes reels.

    Retourne un dict publiant la formule, la valeur composee (None si un operande
    manque — jamais fabriquee), et les champs manquants. Aucun defaut n'est
    invente : `tipo`/`secuencia` absents ⇒ `e_ncf: None`, `incomplete: True`.
    """
    t = _tipo(tipo)
    s = _secuencia(secuencia)

    manquants: list[str] = []
    if t is None:
        manquants.append("tipo")
    if s is None:
        manquants.append("secuencia")

    e_ncf = f"E{t}{s}" if (t is not None and s is not None) else None
    return {
        "e_ncf": e_ncf,
        "tipo": t,
        "secuencia": s,
        "formule": FORMULE,
        "incomplete": bool(manquants),
        "champs_manquants": manquants,
    }
