# Ingestion des factures extraites (PDF/image)
from decimal import Decimal
from django.db import transaction

from .models import NormalizedInvoice, NormalizedInvoiceLine


def _d(val):
    if val is None:
        return None
    return Decimal(str(val))


def _str_trunc(s, max_len):
    """Retourne s tronqué à max_len (évite DataError sur CharField)."""
    if s is None:
        return ""
    s = str(s).strip()
    return (s[:max_len] if len(s) > max_len else s) or ""


@transaction.atomic
def ingest_invoice_file(
    file_path: str,
    *,
    societe_id=None,
    client_type="",
    payment_mode=None,
    client_nom="",
    client_nif="",
    client_telephone="",
    client_adresse="",
    source_file_field=None,
):
    """
    Extrait les données du fichier (PDF ou image) et enregistre en base.
    Entête = société + client (saisis). Lignes et totaux = extraits du fichier.
    """
    from .extractor.normalized_invoice_extractor import extract_normalized_invoice

    data = extract_normalized_invoice(file_path)
    sha1 = data.get("source_sha1")
    if not sha1:
        raise ValueError("source_sha1 manquant après extraction.")

    qr_raw = data.get("qr_raw")
    if not isinstance(qr_raw, str):
        qr_raw = None

    invoice, created = NormalizedInvoice.objects.get_or_create(
        source_sha1=sha1,
        defaults={
            "source_filename": _str_trunc(data.get("source_filename"), 255),
            "invoice_type": _str_trunc(data.get("invoice_type"), 80),
            "invoice_number": _str_trunc(data.get("invoice_number"), 80),
            "code_def_dgi": _str_trunc(data.get("code_def_dgi"), 120),
            "def_nid": _str_trunc(data.get("def_nid"), 60),
            "def_compteurs": _str_trunc(data.get("def_compteurs"), 60),
            "def_datetime": _str_trunc(data.get("def_datetime"), 30),
            "nb_articles": data.get("nb_articles"),
            "ht_taxable_16": _d(data.get("ht_taxable_16")),
            "tva_16": _d(data.get("tva_16")),
            "total_ttc_cdf": _d(data.get("total_ttc_cdf")),
            "payment_mode": _str_trunc(payment_mode or data.get("payment_mode"), 40),
            "total_ttc_usd": _d(data.get("total_ttc_usd")),
            "exchange_rate": _d(data.get("exchange_rate")),
            "exchange_rate_date": _str_trunc(data.get("exchange_rate_date"), 10),
            "isf": _str_trunc(data.get("isf"), 80),
            "qr_raw": qr_raw,

            "societe_id": societe_id,
            "client_type": (client_type or "")[:50],
            "client_nom": (client_nom or "")[:200],
            "client_nif": (client_nif or "")[:30],
            "client_telephone": (client_telephone or "")[:30],
            "client_adresse": client_adresse or "",
            "source_file": source_file_field,
        },
    )

    if not created:
        # MAJ entête (saisi)
        invoice.societe_id = societe_id
        invoice.client_type = (client_type or "")[:50]
        invoice.client_nom = (client_nom or "")[:200]
        invoice.client_nif = (client_nif or "")[:30]
        invoice.client_telephone = (client_telephone or "")[:30]
        invoice.client_adresse = client_adresse or ""

        # MAJ fichier source si fourni
        if source_file_field is not None:
            invoice.source_file = source_file_field

        # MAJ champs extraits si on a une valeur
        invoice.source_filename = _str_trunc(data.get("source_filename"), 255) or invoice.source_filename
        invoice.invoice_type = _str_trunc(data.get("invoice_type"), 80) or invoice.invoice_type
        invoice.invoice_number = _str_trunc(data.get("invoice_number"), 80) or invoice.invoice_number
        invoice.code_def_dgi = _str_trunc(data.get("code_def_dgi"), 120) or invoice.code_def_dgi
        invoice.def_nid = _str_trunc(data.get("def_nid"), 60) or invoice.def_nid
        invoice.def_compteurs = _str_trunc(data.get("def_compteurs"), 60) or invoice.def_compteurs
        invoice.def_datetime = _str_trunc(data.get("def_datetime"), 30) or invoice.def_datetime

        if data.get("nb_articles") is not None:
            invoice.nb_articles = data.get("nb_articles")
        if data.get("ht_taxable_16") is not None:
            invoice.ht_taxable_16 = _d(data.get("ht_taxable_16"))
        if data.get("tva_16") is not None:
            invoice.tva_16 = _d(data.get("tva_16"))
        if data.get("total_ttc_cdf") is not None:
            invoice.total_ttc_cdf = _d(data.get("total_ttc_cdf"))
        invoice.payment_mode = _str_trunc(payment_mode or data.get("payment_mode"), 40) or invoice.payment_mode
        if data.get("total_ttc_usd") is not None:
            invoice.total_ttc_usd = _d(data.get("total_ttc_usd"))
        if data.get("exchange_rate") is not None:
            invoice.exchange_rate = _d(data.get("exchange_rate"))
        invoice.exchange_rate_date = _str_trunc(data.get("exchange_rate_date"), 10) or invoice.exchange_rate_date
        invoice.isf = _str_trunc(data.get("isf"), 80) or invoice.isf

        # IMPORTANT : MAJ QR si trouvé
        if qr_raw:
            invoice.qr_raw = qr_raw

        invoice.save(update_fields=None)

        # Recréer les lignes
        invoice.lines.all().delete()

    # Insérer lignes
    for line_data in (data.get("lines") or []):
        if isinstance(line_data, dict):
            designation = _str_trunc(line_data.get("designation"), 255)
            unit = line_data.get("unit_price_ttc")
            qty = line_data.get("quantity")
            amt = line_data.get("amount_ttc")
        else:
            designation = _str_trunc(getattr(line_data, "designation", ""), 255)
            unit = getattr(line_data, "unit_price_ttc", None)
            qty = getattr(line_data, "quantity", None)
            amt = getattr(line_data, "amount_ttc", None)

        if not designation:
            designation = "—"

        NormalizedInvoiceLine.objects.create(
            invoice=invoice,
            designation=designation,
            unit_price_ttc=_d(unit),
            quantity=_d(qty),
            amount_ttc=_d(amt),
        )

    return invoice
