# /home/Autopartes/pos/services/cfdi_facturapi_builder.py
"""Build Facturapi invoice payloads from Nexus sales data.

Facturapi expects a JSON payload instead of an unsigned XML. This module
generates those payloads for:
  - Ingreso (sale invoice)
  - Egreso (credit note)
  - Pago (payment complement)
  - Factura global mensual
"""

from datetime import datetime
from decimal import ROUND_HALF_UP, Decimal

# SAT defaults
RFC_PUBLICO_GENERAL = "XAXX010101000"
RFC_EXTRANJERO = "XEXX010101000"

# Forma de pago mapping (Nexus internal -> SAT code)
FORMA_PAGO_MAP = {
    "efectivo": "01",
    "transferencia": "03",
    "tarjeta": "04",
    "cheque": "02",
    "credito": "99",
    "mixto": "99",
    "99": "99",
}

# Metodo de pago
METODO_PAGO_MAP = {
    "PUE": "PUE",
    "PPD": "PPD",
}

TWO = Decimal("0.01")
SIX = Decimal("0.000001")


def _to_dec(val):
    if val is None:
        return Decimal("0")
    return Decimal(str(val))


def _fmt2(val):
    return float(_to_dec(val).quantize(TWO, ROUND_HALF_UP))


def _fmt6(val):
    return float(_to_dec(val).quantize(SIX, ROUND_HALF_UP))


def _resolve_forma_pago(sale):
    method = (sale.get("payment_method") or "").lower().strip()
    fp = (sale.get("forma_pago_sat") or "").strip()
    if fp:
        return fp
    return FORMA_PAGO_MAP.get(method, "99")


def _resolve_metodo_pago(sale):
    mp = (sale.get("metodo_pago_sat") or "").upper().strip()
    if mp in ("PUE", "PPD"):
        return mp
    # Default: credit sales are PPD, cash sales are PUE
    if sale.get("sale_type") == "credit" or sale.get("payment_method") == "credito":
        return "PPD"
    return "PUE"


def _build_items(sale_items):
    items = []
    for item in sale_items or []:
        qty = int(item.get("quantity", 1))
        unit_price = _to_dec(item.get("unit_price", 0))
        discount = _to_dec(item.get("discount_amount", 0))
        tax_rate = _to_dec(item.get("tax_rate", "0.16"))

        # Facturapi price is unit price before taxes and discounts
        product = {
            "description": item.get("name") or "Autoparte",
            "product_key": item.get("clave_prod_serv") or "25174800",
            "unit_key": item.get("clave_unidad") or "H87",
            "unit_name": "Pieza",
            "price": _fmt2(unit_price),
            "tax_included": False,
            "taxes": [
                {
                    "type": "IVA",
                    "rate": _fmt6(tax_rate),
                    "factor": "Tasa",
                }
            ],
        }
        if discount > 0:
            product["discount"] = _fmt2(discount / qty) if qty > 0 else _fmt2(discount)

        items.append({"quantity": qty, "product": product})
    return items


def _build_customer_payload(customer, tenant_cp):
    if not customer or not customer.get("rfc"):
        # Publico en general
        return {
            "tax_id": RFC_PUBLICO_GENERAL,
            "legal_name": "PUBLICO EN GENERAL",
            "tax_system": "616",
            "address": {"zip": tenant_cp or "00000"},
        }

    rfc = (customer.get("rfc") or "").upper().strip()
    return {
        "tax_id": rfc,
        "legal_name": customer.get("razon_social") or customer.get("name") or rfc,
        "tax_system": customer.get("regimen_fiscal") or "616",
        "email": customer.get("email"),
        "address": {"zip": customer.get("cp") or tenant_cp or "00000"},
    }


def build_ingreso_payload(sale, tenant_config, customer=None):
    """Build Facturapi payload for a sale (Comprobante tipo Ingreso)."""
    tenant_cp = tenant_config.get("cp", "00000")
    customer_payload = _build_customer_payload(customer, tenant_cp)

    payload = {
        "customer": customer_payload,
        "items": _build_items(sale.get("items", [])),
        "use": customer.get("uso_cfdi") if customer and customer.get("rfc") else "S01",
        "payment_form": _resolve_forma_pago(sale),
        "payment_method": _resolve_metodo_pago(sale),
        "currency": "MXN",
        "series": tenant_config.get("serie", "A"),
        "folio_number": sale["id"],
    }

    # Optional exchange rate for USD
    if sale.get("currency") and sale["currency"] != "MXN" and sale.get("exchange_rate"):
        payload["exchange"] = _fmt6(sale["exchange_rate"])
        payload["currency"] = sale["currency"]

    return payload


def build_egreso_payload(sale, tenant_config, customer, original_uuid):
    """Build Facturapi payload for a credit note (Comprobante tipo Egreso)."""
    payload = build_ingreso_payload(sale, tenant_config, customer)
    payload["type"] = "E"
    payload["related_documents"] = [{"relationship": "01", "documents": [original_uuid]}]
    payload["payment_method"] = "PUE"
    return payload


def build_pago_payload(payment, tenant_config, customer, original_uuid):
    """Build Facturapi payload for a payment complement (Comprobante tipo Pago)."""
    tenant_cp = tenant_config.get("cp", "00000")
    customer_payload = _build_customer_payload(customer, tenant_cp)

    amount = _to_dec(payment.get("amount", 0))
    base = (amount / Decimal("1.16")).quantize(TWO, ROUND_HALF_UP)

    payment_date = payment.get("date") or datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
    if "T" not in str(payment_date):
        payment_date = f"{payment_date}T12:00:00"

    forma_pago = FORMA_PAGO_MAP.get((payment.get("payment_method") or "").lower().strip(), "01")

    payload = {
        "type": "P",
        "customer": customer_payload,
        "complements": [
            {
                "type": "pago",
                "data": {
                    "payment_form": forma_pago,
                    "payment_date": payment_date,
                    "amount": _fmt2(amount),
                    "related_documents": [
                        {
                            "uuid": original_uuid,
                            "amount": _fmt2(amount),
                            "taxes": [
                                {
                                    "type": "IVA",
                                    "rate": 0.16,
                                    "factor": "Tasa",
                                    "base": _fmt2(base),
                                }
                            ],
                        }
                    ],
                },
            }
        ],
    }
    return payload


def build_global_invoice_payload(sales, tenant_config, year, month):
    """Build Facturapi payload for a monthly global invoice."""
    tenant_cp = tenant_config.get("cp", "00000")

    total_subtotal = Decimal("0")
    total_discount = Decimal("0")
    total_tax = Decimal("0")
    total_total = Decimal("0")
    all_items = []

    for sale in sales:
        total_subtotal += _to_dec(sale.get("subtotal", 0))
        total_discount += _to_dec(sale.get("discount_total", 0))
        total_tax += _to_dec(sale.get("tax_total", 0))
        total_total += _to_dec(sale.get("total", 0))
        all_items.extend(_build_items(sale.get("items", [])))

    payload = {
        "customer": {
            "tax_id": RFC_PUBLICO_GENERAL,
            "legal_name": "PUBLICO EN GENERAL",
            "tax_system": "616",
            "address": {"zip": tenant_cp},
        },
        "items": all_items,
        "use": "S01",
        "payment_form": "01",
        "payment_method": "PUE",
        "currency": "MXN",
        "series": tenant_config.get("serie", "FG"),
        "folio_number": int(f"{year}{month:02d}"),
        "global": {
            "periodicity": "04",  # Mensual
            "months": f"{month:02d}",
            "year": year,
        },
    }
    return payload
