"""
Vistas — App Finance
=======================
Dashboard financiero · Detalle factura · Registrar pago · Anular factura · Exportar Excel.
"""

import json
import logging
from datetime import timedelta
from decimal import Decimal

from django.contrib import messages
from django.db.models import Count, Sum
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.views.decorators.http import require_POST

from apps.users.decorators import doctora_required, moderador_required

from .forms import DateRangeForm, PaymentForm
from .models import (
    AuditoriaDia,
    CierreDia,
    Clinica,
    DetalleCierreDia,
    DetalleFacturaDia,
    DiaClinica,
    DiaClinicaDetalle,
    Examen,
    FacturaDia,
    Imagen,
    IngresoExtra,
    Invoice,
    Laboratorio,
    Payment,
    Proceder,
    ServicioMedico,
    TaxBracket,
    TipoCliente,
)

logger = logging.getLogger("apps.finance")


def _parse_date_range(request):
    """Extrae rango de fechas del GET, defaultea al mes en curso."""
    form = DateRangeForm(request.GET)
    hoy = timezone.now().date()
    if form.is_valid() and form.cleaned_data["fecha_inicio"]:
        fecha_inicio = form.cleaned_data["fecha_inicio"]
        fecha_fin = form.cleaned_data["fecha_fin"] or hoy
    else:
        fecha_inicio = hoy.replace(day=1)
        fecha_fin = hoy
    return form, fecha_inicio, fecha_fin


# ============================================================
# DASHBOARD FINANCIERO
# ============================================================
@moderador_required
def dashboard_financiero(request):
    """Panel financiero con resumen de ingresos, IRT y desglose."""
    form, fecha_inicio, fecha_fin = _parse_date_range(request)
    hoy = timezone.now().date()
    ayer = hoy - timedelta(days=1)
    ano_actual = hoy.year
    primer_dia_ano = hoy.replace(month=1, day=1)
    primer_dia_mes = hoy.replace(day=1)

    # ---- Facturas del año (excluye anuladas) ----
    facturas_ano = FacturaDia.objects.filter(
        fecha__gte=primer_dia_ano,
        fecha__lte=hoy,
        is_active=True,
    ).exclude(estado=FacturaDia.Estado.ANULADA)

    # IRT Anual = sum(irt_monto) de facturas del año
    irt_anual = Decimal("0.00")
    ingreso_anual = Decimal("0.00")
    total_facturas_anual = 0
    for fd in facturas_ano:
        irt_anual += fd.irt_monto
        ingreso_anual += fd.ingreso_neto
        total_facturas_anual += 1

    # Subtotal Mes = ingreso_neto + irt_monto de facturas del mes (excluye anuladas)
    facturas_mes = FacturaDia.objects.filter(
        fecha__gte=primer_dia_mes,
        fecha__lte=hoy,
        is_active=True,
    ).exclude(estado=FacturaDia.Estado.ANULADA)
    subtotal_mes_ingreso = Decimal("0.00")
    subtotal_mes_irt = Decimal("0.00")
    for fd in facturas_mes:
        subtotal_mes_ingreso += fd.ingreso_neto
        subtotal_mes_irt += fd.irt_monto

    # Ingresos Hoy
    ingresos_hoy_facturas = FacturaDia.objects.filter(
        fecha=hoy, is_active=True,
    ).exclude(estado=FacturaDia.Estado.ANULADA)
    ingresos_hoy = Decimal("0.00")
    for fd in ingresos_hoy_facturas:
        ingresos_hoy += fd.ingreso_neto
    ingresos_hoy = ingresos_hoy.quantize(Decimal("0.01"))

    # Ingresos de ayer (comparativa)
    ingresos_ayer_facturas = FacturaDia.objects.filter(
        fecha=ayer, is_active=True,
    ).exclude(estado=FacturaDia.Estado.ANULADA)
    ingresos_ayer = Decimal("0.00")
    for fd in ingresos_ayer_facturas:
        ingresos_ayer += fd.ingreso_neto
    ingresos_ayer = ingresos_ayer.quantize(Decimal("0.01"))

    # Variación porcentual
    if ingresos_ayer > 0:
        variacion = ((ingresos_hoy - ingresos_ayer) / ingresos_ayer * 100).quantize(Decimal("0.1"))
    else:
        variacion = Decimal("100.0") if ingresos_hoy > 0 else Decimal("0.0")

    # ---- Desglose por Tipo de Servicio (DIARIO — acumula todas las clínicas de hoy) ----
    detalles_hoy_base = DetalleFacturaDia.objects.filter(
        factura__is_active=True,
        factura__fecha=hoy,
    ).exclude(factura__estado=FacturaDia.Estado.ANULADA)

    desglose_servicios = (
        detalles_hoy_base
        .values("tipo", "factura__clinica__nombre")
        .annotate(
            cantidad=Count("id"),
            total_ingreso_neto=Sum("subtotal_medico") - Sum("irt_monto"),
        )
        .order_by("tipo", "factura__clinica__nombre")
    )
    tipo_display = dict(DetalleFacturaDia.TipoItem.choices)
    desglose_servicios_list = []
    for item in desglose_servicios:
        desglose_servicios_list.append({
            "tipo": tipo_display.get(item["tipo"], item["tipo"]),
            "cantidad": item["cantidad"],
            "clinica": item["factura__clinica__nombre"] or "—",
            "total_ingreso_neto": item["total_ingreso_neto"] or Decimal("0"),
        })

    # ---- Desglose por Especialidad (DIARIO — servicio_medico por nombre_item + clínica) ----
    desglose_especialidades = (
        detalles_hoy_base
        .filter(tipo=DetalleFacturaDia.TipoItem.SERVICIO_MEDICO)
        .values("nombre_item", "factura__clinica__nombre")
        .annotate(
            cantidad=Count("id"),
            total_ingreso_neto=Sum("subtotal_medico") - Sum("irt_monto"),
        )
        .order_by("nombre_item", "factura__clinica__nombre")
    )
    desglose_especialidades_list = []
    for item in desglose_especialidades:
        desglose_especialidades_list.append({
            "nombre": item["nombre_item"],
            "cantidad": item["cantidad"],
            "clinica": item["factura__clinica__nombre"] or "—",
            "total_ingreso_neto": item["total_ingreso_neto"] or Decimal("0"),
        })

    # Citas agendadas para hoy (solo confirmadas)
    from apps.appointments.models import Cita

    citas_hoy = (
        Cita.objects.filter(
            fecha_hora__date=hoy,
            estado=Cita.Estado.CONFIRMADA,
            is_active=True,
        )
        .select_related("paciente", "servicio")
        .order_by("fecha_hora")
    )

    # Tipos de cliente
    tipos_cliente = TipoCliente.objects.filter(is_active=True).order_by("nombre")

    # Catálogos para selects
    laboratorios = Laboratorio.objects.filter(is_active=True).order_by("nombre")
    imagenes = Imagen.objects.filter(is_active=True).order_by("nombre")
    procederes = Proceder.objects.filter(is_active=True).order_by("nombre")
    examenes = Examen.objects.filter(is_active=True).order_by("nombre")
    servicios_medicos = ServicioMedico.objects.filter(is_active=True).order_by("nombre")
    clinicas = Clinica.objects.filter(is_active=True).order_by("nombre")

    # Día clínica abierto hoy (si existe)
    dia_abierto = DiaClinica.objects.filter(
        fecha=hoy, estado=DiaClinica.Estado.ABIERTO, is_active=True,
    ).select_related("clinica").first()

    # Facturas del día clínica (FacturaDia)
    facturas_dia = []
    irt_actual = TaxBracket.objects.filter(is_active=True).first()
    irt_porciento = irt_actual.porciento_irt if irt_actual else Decimal("0")
    if dia_abierto:
        facturas_dia = list(
            FacturaDia.objects.filter(
                dia_clinica=dia_abierto, is_active=True,
            ).exclude(estado=FacturaDia.Estado.ANULADA)
            .select_related("tipo_cliente")
            .prefetch_related("detalles")
            .order_by("created_at")
        )
        # Agregar datos de detalles por categoría para renderizar la tabla facturación
        for fd in facturas_dia:
            cats = {"servicio_medico": {"precio": Decimal("0"), "medico": Decimal("0")},
                    "laboratorio": {"precio": Decimal("0"), "medico": Decimal("0")},
                    "imagen": {"precio": Decimal("0"), "medico": Decimal("0")},
                    "proceder": {"precio": Decimal("0"), "medico": Decimal("0")}}
            for det in fd.detalles.all():
                if det.tipo in cats:
                    cats[det.tipo]["precio"] += det.precio_venta
                    cats[det.tipo]["medico"] += det.subtotal_medico
            fd.serv_precio = cats["servicio_medico"]["precio"]
            fd.serv_medico = cats["servicio_medico"]["medico"]
            fd.lab_precio = cats["laboratorio"]["precio"]
            fd.lab_medico = cats["laboratorio"]["medico"]
            fd.img_precio = cats["imagen"]["precio"]
            fd.img_medico = cats["imagen"]["medico"]
            fd.proc_precio = cats["proceder"]["precio"]
            fd.proc_medico = cats["proceder"]["medico"]

    # Ingresos extra del día abierto
    ingresos_extra = []
    if dia_abierto:
        ingresos_extra = list(
            IngresoExtra.objects.filter(
                dia_clinica=dia_abierto, is_active=True,
            ).select_related("registrado_por").order_by("created_at")
        )

    context = {
        "form": form,
        "fecha_inicio": fecha_inicio,
        "fecha_fin": fecha_fin,
        "ingresos_hoy": ingresos_hoy,
        "ingresos_ayer": ingresos_ayer,
        "variacion": variacion,
        "ano_actual": ano_actual,
        "ingreso_anual": ingreso_anual.quantize(Decimal("0.01")),
        "total_facturas_anual": total_facturas_anual,
        "irt_anual": irt_anual.quantize(Decimal("0.01")),
        "subtotal_mes_ingreso": subtotal_mes_ingreso.quantize(Decimal("0.01")),
        "subtotal_mes_irt": subtotal_mes_irt.quantize(Decimal("0.01")),
        "mes_actual": hoy.strftime("%B"),
        "desglose_servicios": desglose_servicios_list,
        "desglose_especialidades": desglose_especialidades_list,
        "citas_hoy": citas_hoy,
        "tipos_cliente": tipos_cliente,
        "laboratorios": laboratorios,
        "imagenes": imagenes,
        "procederes": procederes,
        "examenes": examenes,
        "servicios_medicos": servicios_medicos,
        "clinicas": clinicas,
        "dia_abierto": dia_abierto,
        "facturas_dia": facturas_dia,
        "ingresos_extra": ingresos_extra,
        "irt_porciento": irt_porciento,
    }
    return render(request, "finance/dashboard.html", context)


# ============================================================
# LISTADO DE FACTURAS
# ============================================================
@moderador_required
def lista_facturas(request):
    """Listado completo de facturas del día clínica con filtros."""
    form, fecha_inicio, fecha_fin = _parse_date_range(request)
    estado_filter = request.GET.get("estado", "")
    clinica_filter = request.GET.get("clinica", "")

    facturas = FacturaDia.objects.filter(
        fecha__gte=fecha_inicio,
        fecha__lte=fecha_fin,
        is_active=True,
    ).select_related("clinica", "tipo_cliente", "dia_clinica").prefetch_related("detalles")

    if estado_filter:
        facturas = facturas.filter(estado=estado_filter)

    if clinica_filter:
        facturas = facturas.filter(clinica_id=clinica_filter)

    facturas = facturas.order_by("-fecha", "-created_at")

    clinicas = Clinica.objects.filter(is_active=True).order_by("nombre")

    context = {
        "facturas": facturas,
        "form": form,
        "fecha_inicio": fecha_inicio,
        "fecha_fin": fecha_fin,
        "estado_filter": estado_filter,
        "clinica_filter": clinica_filter,
        "estados": FacturaDia.Estado.choices,
        "clinicas": clinicas,
    }
    return render(request, "finance/lista_facturas.html", context)


# ============================================================
# DETALLE DE FACTURA
# ============================================================
@moderador_required
def detalle_factura(request, factura_id):
    """Detalle de una factura con historial de pagos."""
    factura = get_object_or_404(
        Invoice.objects.select_related("paciente", "servicio", "cita", "clinica"),
        id=factura_id,
        is_active=True,
    )
    pagos = factura.pagos.filter(is_active=True).select_related("registrado_por").order_by("-fecha")
    payment_form = PaymentForm(invoice=factura) if factura.estado != Invoice.Estado.ANULADO else None

    context = {
        "factura": factura,
        "pagos": pagos,
        "payment_form": payment_form,
    }
    return render(request, "finance/detalle_factura.html", context)


# ============================================================
# REGISTRAR PAGO
# ============================================================
@moderador_required
def registrar_pago(request, factura_id):
    """Registra un pago contra una factura."""
    factura = get_object_or_404(Invoice, id=factura_id, is_active=True)

    if factura.estado == Invoice.Estado.ANULADO:
        messages.error(request, "Não é possível registar pagamentos em faturas anuladas.")
        return redirect("finance:detalle_factura", factura_id=factura.id)

    if factura.estado == Invoice.Estado.PAGADO:
        messages.info(request, "Esta fatura já está totalmente paga.")
        return redirect("finance:detalle_factura", factura_id=factura.id)

    if request.method == "POST":
        form = PaymentForm(request.POST, invoice=factura)
        if form.is_valid():
            pago = form.save(commit=False)
            pago.factura = factura
            pago.registrado_por = request.user
            pago.save()
            messages.success(
                request,
                f"Pagamento de {pago.monto:,.2f} Kz registado com sucesso.",
            )
            logger.info(
                "Pago registrado: %s — %s Kz por %s",
                factura.numero,
                pago.monto,
                request.user.email,
            )
            return redirect("finance:detalle_factura", factura_id=factura.id)
    else:
        form = PaymentForm(invoice=factura)

    return render(request, "finance/detalle_factura.html", {
        "factura": factura,
        "pagos": factura.pagos.filter(is_active=True).select_related("registrado_por"),
        "payment_form": form,
    })


# ============================================================
# ANULAR FACTURA
# ============================================================
@moderador_required
def anular_factura(request, factura_id):
    """Anula una factura (solo si está pendiente o parcial)."""
    factura = get_object_or_404(Invoice, id=factura_id, is_active=True)

    if factura.estado == Invoice.Estado.PAGADO:
        messages.error(request, "Não é possível anular uma fatura que já foi totalmente paga.")
        return redirect("finance:detalle_factura", factura_id=factura.id)

    if factura.estado == Invoice.Estado.ANULADO:
        messages.info(request, "Esta fatura já está anulada.")
        return redirect("finance:detalle_factura", factura_id=factura.id)

    if request.method == "POST":
        factura.estado = Invoice.Estado.ANULADO
        factura.save(update_fields=["estado", "updated_at"])
        messages.success(request, f"Fatura {factura.numero} anulada com sucesso.")
        logger.info("Factura anulada: %s por %s", factura.numero, request.user.email)
        return redirect("finance:lista_facturas")

    return redirect("finance:detalle_factura", factura_id=factura.id)


# ============================================================
# EXPORTAR EXCEL
# ============================================================
@moderador_required
def exportar_excel(request):
    """Exporta facturas a Excel (.xlsx) agrupadas por clínica para un rango de fechas."""
    from datetime import date as date_type

    from openpyxl import Workbook
    from openpyxl.styles import Alignment, Border, Font, Side
    from openpyxl.worksheet.page import PrintPageSetup

    hoy = timezone.now().date()

    # Parsear rango de fechas desde GET
    fecha_inicio_str = request.GET.get("fecha_inicio")
    fecha_fin_str = request.GET.get("fecha_fin")
    try:
        fecha_inicio = date_type.fromisoformat(fecha_inicio_str) if fecha_inicio_str else hoy
    except (ValueError, TypeError):
        fecha_inicio = hoy
    try:
        fecha_fin = date_type.fromisoformat(fecha_fin_str) if fecha_fin_str else hoy
    except (ValueError, TypeError):
        fecha_fin = hoy
    if fecha_inicio > fecha_fin:
        fecha_inicio, fecha_fin = fecha_fin, fecha_inicio

    facturas = (
        FacturaDia.objects.filter(fecha__gte=fecha_inicio, fecha__lte=fecha_fin, is_active=True)
        .exclude(estado=FacturaDia.Estado.ANULADA)
        .select_related("clinica", "tipo_cliente")
        .prefetch_related("detalles")
        .order_by("clinica__nombre", "fecha", "created_at")
    )

    clinicas_dict = {}
    for f in facturas:
        cname = f.clinica.nombre if f.clinica else "Sem Clínica"
        if cname not in clinicas_dict:
            clinicas_dict[cname] = []
        clinicas_dict[cname].append(f)

    ingresos_extra_por_clinica = {}
    dias_rango = DiaClinica.objects.filter(fecha__gte=fecha_inicio, fecha__lte=fecha_fin, is_active=True).select_related("clinica")
    for dia in dias_rango:
        cname = dia.clinica.nombre
        if cname in clinicas_dict:
            extras = IngresoExtra.objects.filter(dia_clinica=dia, is_active=True).order_by("created_at")
            if extras.exists():
                ingresos_extra_por_clinica[cname] = list(extras)

    wb = Workbook()
    ws = wb.active
    ws.title = "Día Clínica"

    bold = Font(name="Calibri", bold=True, size=10)
    bold_big = Font(name="Calibri", bold=True, size=11)
    normal = Font(name="Calibri", size=10)
    thin_border = Border(
        left=Side(style="thin"), right=Side(style="thin"),
        top=Side(style="thin"), bottom=Side(style="thin"),
    )
    money_fmt = '#,##0.00" Kz"'
    pct_fmt = '0.00"%"'

    col_widths = {"A": 16, "B": 24, "C": 14, "D": 28, "E": 14, "F": 10, "G": 14, "H": 14, "I": 26}
    for col_letter, w in col_widths.items():
        ws.column_dimensions[col_letter].width = w

    # Configuración de impresión horizontal
    ws.page_setup.orientation = "landscape"
    ws.page_setup.paperSize = ws.PAPERSIZE_A4
    ws.page_setup.fitToWidth = 1
    ws.page_setup.fitToHeight = 0
    ws.sheet_properties.pageSetUpPr.fitToPage = True
    ws.print_area = None  # se calcula al final
    ws.page_margins.left = 0.4
    ws.page_margins.right = 0.4
    ws.page_margins.top = 0.5
    ws.page_margins.bottom = 0.5

    row = 1

    # Encabezado
    ws.merge_cells(f"A{row}:I{row}")
    c = ws.cell(row=row, column=1, value="Relatório de Faturas — Dra. Mariela Moreno Autie")
    c.font = Font(name="Calibri", bold=True, size=14)
    c.alignment = Alignment(horizontal="center", vertical="center")
    ws.row_dimensions[row].height = 30
    row += 1

    ws.merge_cells(f"A{row}:I{row}")
    if fecha_inicio == fecha_fin:
        fecha_label = fecha_inicio.strftime("%d/%m/%Y")
    else:
        fecha_label = f"{fecha_inicio.strftime('%d/%m/%Y')} — {fecha_fin.strftime('%d/%m/%Y')}"
    c = ws.cell(row=row, column=1, value=fecha_label)
    c.font = Font(name="Calibri", bold=True, size=12)
    c.alignment = Alignment(horizontal="center", vertical="center")
    row += 1

    irt_obj = TaxBracket.objects.filter(is_active=True).first()
    irt_porciento = irt_obj.porciento_irt if irt_obj else Decimal("0")

    ws.merge_cells(f"A{row}:I{row}")
    c = ws.cell(row=row, column=1, value=f"IRT: {irt_porciento}%")
    c.font = normal
    c.alignment = Alignment(horizontal="center")
    row += 2

    if not clinicas_dict:
        ws.merge_cells(f"A{row}:I{row}")
        ws.cell(row=row, column=1, value="Não há faturas registadas para o período selecionado.").font = Font(
            name="Calibri", italic=True, size=11
        )
    else:
        grand_pventa = Decimal("0")
        grand_medico = Decimal("0")
        grand_irt = Decimal("0")
        grand_neto = Decimal("0")
        grand_extra = Decimal("0")

        for clinica_nombre, factura_list in clinicas_dict.items():
            # Barra de clínica
            ws.merge_cells(f"A{row}:I{row}")
            c = ws.cell(row=row, column=1, value=clinica_nombre)
            c.font = bold_big
            c.alignment = Alignment(vertical="center")
            ws.row_dimensions[row].height = 24
            row += 1

            # Encabezados de columnas
            headers = ["Fatura", "Paciente", "Tipo Cliente", "Ítem (Detalhe)", "P. Venda", "% Méd.", "Sub. Médico", "IRT", "Observações"]
            for col_i, h in enumerate(headers, 1):
                c = ws.cell(row=row, column=col_i, value=h)
                c.font = bold
                c.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
                c.border = thin_border
            row += 1

            clinica_pventa = Decimal("0")
            clinica_medico = Decimal("0")
            clinica_irt = Decimal("0")
            clinica_neto = Decimal("0")

            for factura in factura_list:
                detalles = list(factura.detalles.filter(is_active=True))
                num_lines = max(len(detalles), 1)
                first_row = row

                for i in range(num_lines):
                    det = detalles[i] if i < len(detalles) else None

                    if i == 0:
                        ws.cell(row=row, column=1, value=factura.numero_factura or "S/N").border = thin_border
                        ws.cell(row=row, column=2, value=factura.paciente_nombre).border = thin_border
                        tipo_cl = factura.tipo_cliente.nombre if factura.tipo_cliente else "—"
                        ws.cell(row=row, column=3, value=tipo_cl).border = thin_border
                        obs = factura.observaciones or ""
                        c9 = ws.cell(row=row, column=9, value=obs)
                        c9.border = thin_border
                        c9.alignment = Alignment(vertical="center", wrap_text=True)
                    else:
                        for c_i in (1, 2, 3, 9):
                            ws.cell(row=row, column=c_i).border = thin_border

                    if det:
                        ws.cell(row=row, column=4, value=det.nombre_item).border = thin_border
                        c5 = ws.cell(row=row, column=5, value=float(det.precio_venta))
                        c5.number_format = money_fmt
                        c5.border = thin_border
                        c6 = ws.cell(row=row, column=6, value=float(det.porciento_medico))
                        c6.number_format = pct_fmt
                        c6.border = thin_border
                        c7 = ws.cell(row=row, column=7, value=float(det.subtotal_medico))
                        c7.number_format = money_fmt
                        c7.border = thin_border
                        c8 = ws.cell(row=row, column=8, value=float(det.irt_monto))
                        c8.number_format = money_fmt
                        c8.border = thin_border
                    else:
                        for c_i in range(4, 9):
                            ws.cell(row=row, column=c_i).border = thin_border

                    row += 1

                if num_lines > 1:
                    for c_i in (1, 2, 3, 9):
                        ws.merge_cells(start_row=first_row, start_column=c_i, end_row=first_row + num_lines - 1, end_column=c_i)
                        ws.cell(row=first_row, column=c_i).alignment = Alignment(vertical="center", wrap_text=True)

                clinica_pventa += factura.precio_venta_total
                clinica_medico += factura.porciento_medico_total
                clinica_irt += factura.irt_monto
                clinica_neto += factura.ingreso_neto

            # Subtotal de clínica
            ws.merge_cells(f"A{row}:D{row}")
            c = ws.cell(row=row, column=1, value=f"Subtotal {clinica_nombre}")
            c.font = bold
            c.alignment = Alignment(horizontal="right", vertical="center")
            for c_i in range(1, 10):
                ws.cell(row=row, column=c_i).border = thin_border
                ws.cell(row=row, column=c_i).font = bold

            c5 = ws.cell(row=row, column=5, value=float(clinica_pventa))
            c5.number_format = money_fmt
            c5.font = bold
            c7 = ws.cell(row=row, column=7, value=float(clinica_medico))
            c7.number_format = money_fmt
            c7.font = bold
            c8 = ws.cell(row=row, column=8, value=float(clinica_irt))
            c8.number_format = money_fmt
            c8.font = bold
            row += 1

            # Ingreso Neto de clínica
            ws.merge_cells(f"A{row}:F{row}")
            c = ws.cell(row=row, column=1, value=f"Receita Líquida {clinica_nombre}")
            c.font = bold
            c.alignment = Alignment(horizontal="right", vertical="center")
            ws.merge_cells(f"G{row}:I{row}")
            cn = ws.cell(row=row, column=7, value=float(clinica_neto))
            cn.number_format = money_fmt
            cn.font = bold
            for c_i in range(1, 10):
                ws.cell(row=row, column=c_i).border = thin_border
            row += 1

            # Ingresos extra
            if clinica_nombre in ingresos_extra_por_clinica:
                extras = ingresos_extra_por_clinica[clinica_nombre]
                total_extra_clinica = Decimal("0")
                ws.merge_cells(f"A{row}:I{row}")
                ws.cell(row=row, column=1, value="Receitas Extra").font = bold
                row += 1
                for ie in extras:
                    ws.merge_cells(f"A{row}:D{row}")
                    ws.cell(row=row, column=1, value=f"  {ie.motivo}").border = thin_border
                    ws.merge_cells(f"E{row}:F{row}")
                    ce = ws.cell(row=row, column=5, value=float(ie.monto))
                    ce.number_format = money_fmt
                    ce.border = thin_border
                    for c_i in range(1, 10):
                        ws.cell(row=row, column=c_i).border = thin_border
                    total_extra_clinica += ie.monto
                    row += 1
                ws.merge_cells(f"A{row}:D{row}")
                c = ws.cell(row=row, column=1, value="Total Receita Extra")
                c.font = bold
                c.alignment = Alignment(horizontal="right")
                ws.merge_cells(f"E{row}:F{row}")
                ce = ws.cell(row=row, column=5, value=float(total_extra_clinica))
                ce.number_format = money_fmt
                ce.font = bold
                for c_i in range(1, 10):
                    ws.cell(row=row, column=c_i).border = thin_border
                grand_extra += total_extra_clinica
                row += 1

            grand_pventa += clinica_pventa
            grand_medico += clinica_medico
            grand_irt += clinica_irt
            grand_neto += clinica_neto
            row += 1

        # TOTALES GENERALES
        ws.merge_cells(f"A{row}:D{row}")
        c = ws.cell(row=row, column=1, value="TOTAIS GERAIS")
        c.font = bold_big
        c.alignment = Alignment(horizontal="right", vertical="center")
        for c_i in range(1, 10):
            ws.cell(row=row, column=c_i).border = thin_border
            ws.cell(row=row, column=c_i).font = bold_big

        c5 = ws.cell(row=row, column=5, value=float(grand_pventa))
        c5.number_format = money_fmt
        c5.font = bold_big
        c7 = ws.cell(row=row, column=7, value=float(grand_medico))
        c7.number_format = money_fmt
        c7.font = bold_big
        c8 = ws.cell(row=row, column=8, value=float(grand_irt))
        c8.number_format = money_fmt
        c8.font = bold_big
        row += 1

        # Ingreso Neto General
        ws.merge_cells(f"A{row}:F{row}")
        c = ws.cell(row=row, column=1, value="RECEITA LÍQUIDA TOTAL")
        c.font = bold_big
        c.alignment = Alignment(horizontal="right", vertical="center")
        ws.merge_cells(f"G{row}:I{row}")
        cn = ws.cell(row=row, column=7, value=float(grand_neto))
        cn.number_format = money_fmt
        cn.font = bold_big
        for c_i in range(1, 10):
            ws.cell(row=row, column=c_i).border = thin_border
        row += 1

        if grand_extra > 0:
            ws.merge_cells(f"A{row}:F{row}")
            c = ws.cell(row=row, column=1, value="RECEITA EXTRA TOTAL")
            c.font = bold_big
            c.alignment = Alignment(horizontal="right", vertical="center")
            ws.merge_cells(f"G{row}:I{row}")
            ce = ws.cell(row=row, column=7, value=float(grand_extra))
            ce.number_format = money_fmt
            ce.font = bold_big
            for c_i in range(1, 10):
                ws.cell(row=row, column=c_i).border = thin_border

    # Área de impresión dinámica
    ws.print_area = f"A1:I{max(row, 1)}"

    filename = f"reporte_{fecha_inicio.strftime('%Y%m%d')}_{fecha_fin.strftime('%Y%m%d')}.xlsx"
    response = HttpResponse(
        content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    )
    response["Content-Disposition"] = f'attachment; filename="{filename}"'
    wb.save(response)
    return response


# ============================================================
# API — DÍA CLÍNICA (Abrir / Cerrar / Agregar línea)
# ============================================================
@moderador_required
@require_POST
def abrir_dia_clinica(request):
    """Abre un día de clínica para la fecha actual. Permite reabrir un día cerrado."""
    data = json.loads(request.body)
    clinica_id = data.get("clinica_id")
    if not clinica_id:
        return JsonResponse({"success": False, "error": "Selecione uma clínica."}, status=400)

    clinica = get_object_or_404(Clinica, id=clinica_id, is_active=True)
    hoy = timezone.now().date()

    # Verificar si ya hay un día abierto en OTRA clínica hoy
    dia_abierto_otro = DiaClinica.objects.filter(
        fecha=hoy, estado=DiaClinica.Estado.ABIERTO, is_active=True,
    ).exclude(clinica=clinica).first()
    if dia_abierto_otro:
        return JsonResponse({
            "success": False,
            "error": f"Já tem um dia aberto em {dia_abierto_otro.clinica.nombre}. Feche-o primeiro.",
        }, status=400)

    # Verificar si ya existe un día para esta clínica hoy
    dia_existente = DiaClinica.objects.filter(
        clinica=clinica, fecha=hoy, is_active=True,
    ).first()

    if dia_existente:
        if dia_existente.estado == DiaClinica.Estado.ABIERTO:
            return JsonResponse({
                "success": True,
                "dia_id": str(dia_existente.id),
                "clinica_nombre": clinica.nombre,
                "fecha": hoy.strftime("%d/%m/%Y"),
                "reabierto": False,
                "message": "O dia já estava aberto.",
            })
        # Día CERRADO → Reabrir con auditoría
        dia_existente.estado = DiaClinica.Estado.ABIERTO
        dia_existente.save(update_fields=["estado", "updated_at"])

        AuditoriaDia.objects.create(
            dia_clinica=dia_existente,
            accion=AuditoriaDia.Accion.REABRIR_DIA,
            usuario=request.user,
            detalle=f"Día reabierto en {clinica.nombre} después de haber sido cerrado.",
        )

        # Cargar facturas existentes para devolver al frontend
        facturas = FacturaDia.objects.filter(
            dia_clinica=dia_existente, is_active=True,
        ).exclude(estado=FacturaDia.Estado.ANULADA).select_related("tipo_cliente").prefetch_related("detalles").order_by("created_at")

        facturas_list = []
        for fd in facturas:
            cats = {"servicio_medico": {"precio": Decimal("0"), "medico": Decimal("0")},
                    "laboratorio": {"precio": Decimal("0"), "medico": Decimal("0")},
                    "imagen": {"precio": Decimal("0"), "medico": Decimal("0")},
                    "proceder": {"precio": Decimal("0"), "medico": Decimal("0")}}
            for det in fd.detalles.all():
                if det.tipo in cats:
                    cats[det.tipo]["precio"] += det.precio_venta
                    cats[det.tipo]["medico"] += det.subtotal_medico
            facturas_list.append({
                "id": str(fd.id),
                "numero_factura": fd.numero_factura,
                "paciente_nombre": fd.paciente_nombre,
                "tipo_cliente": fd.tipo_cliente.nombre if fd.tipo_cliente else "",
                "precio_venta_total": str(fd.precio_venta_total),
                "porciento_medico_total": str(fd.porciento_medico_total),
                "irt_porciento": str(fd.irt_porciento),
                "irt_monto": str(fd.irt_monto),
                "ingreso_neto": str(fd.ingreso_neto),
                "estado": fd.get_estado_display(),
                "observaciones": fd.observaciones,
                "serv_precio": str(cats["servicio_medico"]["precio"]),
                "serv_medico": str(cats["servicio_medico"]["medico"]),
                "lab_precio": str(cats["laboratorio"]["precio"]),
                "lab_medico": str(cats["laboratorio"]["medico"]),
                "img_precio": str(cats["imagen"]["precio"]),
                "img_medico": str(cats["imagen"]["medico"]),
                "proc_precio": str(cats["proceder"]["precio"]),
                "proc_medico": str(cats["proceder"]["medico"]),
            })

        # Cargar ingresos extra
        ingresos_extra = IngresoExtra.objects.filter(
            dia_clinica=dia_existente, is_active=True,
        ).order_by("created_at")
        ingresos_extra_list = [
            {"id": str(ie.id), "monto": str(ie.monto), "motivo": ie.motivo}
            for ie in ingresos_extra
        ]

        logger.info("Día reabierto: %s — %s por %s", clinica.nombre, hoy, request.user.email)

        return JsonResponse({
            "success": True,
            "dia_id": str(dia_existente.id),
            "clinica_nombre": clinica.nombre,
            "fecha": hoy.strftime("%d/%m/%Y"),
            "reabierto": True,
            "facturas": facturas_list,
            "ingresos_extra": ingresos_extra_list,
            "message": "Dia reaberto com sucesso. Os dados anteriores foram carregados.",
        })

    # No existe → crear nuevo día
    dia = DiaClinica.objects.create(
        clinica=clinica,
        fecha=hoy,
        estado=DiaClinica.Estado.ABIERTO,
        abierto_por=request.user,
    )

    AuditoriaDia.objects.create(
        dia_clinica=dia,
        accion=AuditoriaDia.Accion.ABRIR_DIA,
        usuario=request.user,
        detalle=f"Día abierto en {clinica.nombre}.",
    )

    return JsonResponse({
        "success": True,
        "dia_id": str(dia.id),
        "clinica_nombre": clinica.nombre,
        "fecha": hoy.strftime("%d/%m/%Y"),
        "reabierto": False,
        "message": "Dia aberto com sucesso.",
    })


@moderador_required
@require_POST
def cerrar_dia_clinica(request):
    """Cierra el día de clínica: crea CierreDia + DetalleCierreDia desde FacturaDia."""
    data = json.loads(request.body)
    dia_id = data.get("dia_id")

    dia = get_object_or_404(DiaClinica, id=dia_id, estado=DiaClinica.Estado.ABIERTO, is_active=True)

    # Obtener todas las facturas del día (no anuladas)
    facturas = FacturaDia.objects.filter(
        dia_clinica=dia, is_active=True,
    ).exclude(estado=FacturaDia.Estado.ANULADA).select_related("tipo_cliente")

    total_precio_venta = Decimal("0")
    total_porciento_medico = Decimal("0")
    total_irt_monto = Decimal("0")
    total_ingreso_neto = Decimal("0")
    irt_porciento_global = Decimal("0")

    # Si ya existe un cierre previo (reapertura), eliminarlo para crear uno nuevo
    CierreDia.objects.filter(dia_clinica=dia).delete()

    # Crear CierreDia
    cierre = CierreDia.objects.create(
        dia_clinica=dia,
        clinica=dia.clinica,
        fecha=dia.fecha,
        cerrado_por=request.user,
        total_facturas=facturas.count(),
    )

    # Crear detalles del cierre (snapshot de cada factura)
    for fd in facturas:
        irt_monto = fd.irt_monto
        ingreso_neto = fd.ingreso_neto

        DetalleCierreDia.objects.create(
            cierre=cierre,
            factura_dia=fd,
            numero_factura=fd.numero_factura,
            paciente_nombre=fd.paciente_nombre,
            tipo_cliente_nombre=fd.tipo_cliente.nombre if fd.tipo_cliente else "",
            precio_venta_total=fd.precio_venta_total,
            porciento_medico_total=fd.porciento_medico_total,
            irt_porciento=fd.irt_porciento,
            irt_monto=irt_monto,
            ingreso_neto=ingreso_neto,
            observaciones=fd.observaciones,
        )

        total_precio_venta += fd.precio_venta_total
        total_porciento_medico += fd.porciento_medico_total
        total_irt_monto += irt_monto
        total_ingreso_neto += ingreso_neto
        irt_porciento_global = fd.irt_porciento  # mismo % para todas

        # Confirmar la factura
        if fd.estado == FacturaDia.Estado.SIN_CONFIRMAR:
            fd.estado = FacturaDia.Estado.CONFIRMADA
            fd.save(update_fields=["estado", "updated_at"])

    # Sumar ingresos extra al ingreso neto total
    total_ingreso_extra = IngresoExtra.objects.filter(
        dia_clinica=dia, is_active=True,
    ).aggregate(total=Sum("monto"))["total"] or Decimal("0")
    total_ingreso_neto += total_ingreso_extra

    # Actualizar totales del cierre
    cierre.total_precio_venta = total_precio_venta
    cierre.total_porciento_medico = total_porciento_medico
    cierre.total_irt_porciento = irt_porciento_global
    cierre.total_irt_monto = total_irt_monto
    cierre.total_ingreso_neto = total_ingreso_neto
    cierre.save(update_fields=[
        "total_precio_venta", "total_porciento_medico",
        "total_irt_porciento", "total_irt_monto", "total_ingreso_neto", "updated_at",
    ])

    # Cerrar el día
    dia.estado = DiaClinica.Estado.CERRADO
    dia.cerrado_por = request.user
    dia.hora_cierre = timezone.now()
    dia.total_precio_venta = total_precio_venta
    dia.total_porciento_medico = total_porciento_medico
    dia.save(update_fields=[
        "estado", "cerrado_por", "hora_cierre",
        "total_precio_venta", "total_porciento_medico", "updated_at",
    ])

    # Auditoría
    AuditoriaDia.objects.create(
        dia_clinica=dia,
        accion=AuditoriaDia.Accion.CERRAR_DIA,
        usuario=request.user,
        detalle=f"Día cerrado en {dia.clinica.nombre}. Facturas: {cierre.total_facturas}, Ingreso Neto: {total_ingreso_neto} Kz (incluye {total_ingreso_extra} Kz ingreso extra).",
    )

    return JsonResponse({
        "success": True,
        "message": "Dia fechado com sucesso.",
        "total_precio_venta": str(total_precio_venta),
        "total_porciento_medico": str(total_porciento_medico),
        "total_irt_monto": str(total_irt_monto),
        "total_ingreso_neto": str(total_ingreso_neto),
        "total_ingreso_extra": str(total_ingreso_extra),
        "total_facturas": cierre.total_facturas,
    })


@moderador_required
def catalogo_precios(request):
    """Devuelve precios y porcientos de catálogos para JS."""
    irt_obj = TaxBracket.objects.filter(is_active=True).first()
    data = {
        "laboratorios": {
            str(l.id): {"precio": str(l.precio_venta), "porciento": str(l.porciento_medico)}
            for l in Laboratorio.objects.filter(is_active=True)
        },
        "imagenes": {
            str(i.id): {"precio": str(i.precio_venta), "porciento": str(i.porciento_medico)}
            for i in Imagen.objects.filter(is_active=True)
        },
        "procederes": {
            str(p.id): {"precio": str(p.precio_venta), "porciento": str(p.porciento_medico)}
            for p in Proceder.objects.filter(is_active=True)
        },
        "examenes": {
            str(e.id): {"precio": str(e.precio_venta)}
            for e in Examen.objects.filter(is_active=True)
        },
        "servicios_medicos": {
            str(s.id): {"precio": str(s.precio_venta), "porciento": str(s.porciento_medico)}
            for s in ServicioMedico.objects.filter(is_active=True)
        },
        "irt_porciento": str(irt_obj.porciento_irt) if irt_obj else "0",
    }
    return JsonResponse(data)


@moderador_required
@require_POST
def crear_catalogo_item(request):
    """Crea un nuevo ítem de catálogo (laboratorio/imagen/proceder) vía AJAX."""
    data = json.loads(request.body)
    tipo = data.get("tipo", "").strip()
    nombre = data.get("nombre", "").strip()
    precio_venta = data.get("precio_venta", "0")
    porciento_medico = data.get("porciento_medico", "0")

    if not nombre:
        return JsonResponse({"success": False, "error": "O nome é obrigatório."}, status=400)

    model_map = {
        "laboratorio": Laboratorio,
        "imagen": Imagen,
        "proceder": Proceder,
    }
    Model = model_map.get(tipo)
    if not Model:
        return JsonResponse({"success": False, "error": "Tipo não válido."}, status=400)

    try:
        item = Model.objects.create(
            nombre=nombre,
            precio_venta=Decimal(str(precio_venta)),
            porciento_medico=Decimal(str(porciento_medico)),
        )
    except Exception as e:
        logger.error("Error al crear catálogo %s: %s", tipo, e)
        return JsonResponse({"success": False, "error": "Erro ao guardar."}, status=500)

    # Actualizar cache de catálogos
    return JsonResponse({
        "success": True,
        "id": str(item.id),
        "nombre": item.nombre,
        "precio": str(item.precio_venta),
        "porciento": str(item.porciento_medico),
    })


# ============================================================
# API — CREAR FACTURA DÍA CLÍNICA
# ============================================================
@moderador_required
@require_POST
def crear_factura_dia(request):
    """Crea una FacturaDia + DetalleFacturaDia al presionar 'Agregar a Tabla'."""
    data = json.loads(request.body)

    dia_id = data.get("dia_id")
    if not dia_id:
        return JsonResponse({"success": False, "error": "Não há dia aberto."}, status=400)

    dia = get_object_or_404(DiaClinica, id=dia_id, estado=DiaClinica.Estado.ABIERTO, is_active=True)

    # Si se seleccionó una cita agendada, marcarla como completada
    cita_id = data.get("cita_id")
    cita_completada = None
    cita_obj = None
    if cita_id:
        from apps.appointments.models import Cita
        try:
            cita_obj = Cita.objects.get(id=cita_id, estado=Cita.Estado.CONFIRMADA, is_active=True)
            cita_obj.estado = Cita.Estado.COMPLETADA
            cita_obj.save(update_fields=["estado", "updated_at"])
            cita_completada = str(cita_obj.id)
        except Cita.DoesNotExist:
            pass  # Cita ya no existe o ya fue completada, continuar sin error

    paciente_nombre = (data.get("paciente_nombre") or "").strip()
    if not paciente_nombre:
        return JsonResponse({"success": False, "error": "O nome do paciente é obrigatório."}, status=400)

    numero_factura = (data.get("numero_factura") or "").strip()
    tipo_cliente_id = data.get("tipo_cliente_id") or None
    observaciones = (data.get("observaciones") or "").strip()
    detalles = data.get("detalles", [])

    # Obtener IRT activo
    irt_obj = TaxBracket.objects.filter(is_active=True).first()
    irt_porciento = irt_obj.porciento_irt if irt_obj else Decimal("0")

    # Calcular totales desde los detalles
    total_precio_venta = Decimal("0")
    total_porciento_medico = Decimal("0")

    for det in detalles:
        precio = Decimal(str(det.get("precio_venta", "0") or "0"))
        porc = Decimal(str(det.get("porciento_medico", "0") or "0"))
        sub_medico = (precio * porc / Decimal("100")).quantize(Decimal("0.01"))
        total_precio_venta += precio
        total_porciento_medico += sub_medico

    # Crear factura
    factura = FacturaDia.objects.create(
        numero_factura=numero_factura,
        paciente_nombre=paciente_nombre,
        tipo_cliente_id=tipo_cliente_id,
        estado=FacturaDia.Estado.SIN_CONFIRMAR,
        precio_venta_total=total_precio_venta,
        porciento_medico_total=total_porciento_medico,
        irt_porciento=irt_porciento,
        clinica=dia.clinica,
        dia_clinica=dia,
        observaciones=observaciones,
        cita=cita_obj,
    )

    # Crear detalles
    for det in detalles:
        precio = Decimal(str(det.get("precio_venta", "0") or "0"))
        porc = Decimal(str(det.get("porciento_medico", "0") or "0"))
        sub_medico = (precio * porc / Decimal("100")).quantize(Decimal("0.01"))
        det_irt_monto = (sub_medico * irt_porciento / Decimal("100")).quantize(Decimal("0.01"))

        DetalleFacturaDia.objects.create(
            factura=factura,
            tipo=det.get("tipo", ""),
            nombre_item=det.get("nombre_item", ""),
            precio_venta=precio,
            porciento_medico=porc,
            subtotal_medico=sub_medico,
            irt_porciento=irt_porciento,
            irt_monto=det_irt_monto,
        )

    logger.info(
        "FacturaDia creada: %s — %s — %s Kz por %s",
        factura.numero_factura,
        factura.paciente_nombre,
        factura.precio_venta_total,
        request.user.email,
    )

    # Auditoría
    AuditoriaDia.objects.create(
        dia_clinica=dia,
        accion=AuditoriaDia.Accion.CREAR_FACTURA,
        usuario=request.user,
        detalle=f"Factura creada: {factura.numero_factura or 'S/N'} — {factura.paciente_nombre} — {factura.precio_venta_total} Kz",
    )

    # Devolver datos para la fila de la tabla
    tipo_cliente_nombre = ""
    if tipo_cliente_id:
        try:
            tipo_cliente_nombre = TipoCliente.objects.get(id=tipo_cliente_id).nombre
        except TipoCliente.DoesNotExist:
            pass

    # Serializar detalles para actualizar desgloses en el frontend
    detalles_list = []
    for det in factura.detalles.all():
        detalles_list.append({
            "tipo": det.tipo,
            "tipo_display": det.get_tipo_display(),
            "nombre_item": det.nombre_item,
            "subtotal_medico": str(det.subtotal_medico),
            "irt_monto": str(det.irt_monto),
            "ingreso_neto": str(det.subtotal_medico - det.irt_monto),
        })

    return JsonResponse({
        "success": True,
        "cita_completada": cita_completada,
        "factura": {
            "id": str(factura.id),
            "numero_factura": factura.numero_factura,
            "paciente_nombre": factura.paciente_nombre,
            "tipo_cliente": tipo_cliente_nombre,
            "precio_venta_total": str(factura.precio_venta_total),
            "porciento_medico_total": str(factura.porciento_medico_total),
            "irt_porciento": str(factura.irt_porciento),
            "irt_monto": str(factura.irt_monto),
            "ingreso_neto": str(factura.ingreso_neto),
            "estado": factura.get_estado_display(),
            "clinica": dia.clinica.nombre,
            "fecha": factura.fecha.strftime("%d/%m/%Y"),
            "observaciones": factura.observaciones,
            "detalles": detalles_list,
        },
    })


# ============================================================
# API — ELIMINAR FACTURA DÍA CLÍNICA (solo sin_confirmar)
# ============================================================
@moderador_required
@require_POST
def eliminar_factura_dia(request):
    """Elimina una FacturaDia y sus detalles si está sin confirmar."""
    data = json.loads(request.body)
    factura_id = data.get("factura_id")
    if not factura_id:
        return JsonResponse({"success": False, "error": "ID de fatura obrigatório."}, status=400)

    factura = get_object_or_404(FacturaDia, id=factura_id, is_active=True)

    if factura.estado != FacturaDia.Estado.SIN_CONFIRMAR:
        return JsonResponse({
            "success": False,
            "error": "Só é possível eliminar faturas não confirmadas.",
        }, status=400)

    # Guardar datos para respuesta antes de eliminar
    ingreso_neto_eliminado = str(factura.ingreso_neto)
    irt_monto_eliminado = str(factura.irt_monto)
    clinica_nombre = factura.clinica.nombre if factura.clinica else ""
    detalles_eliminados = []
    for det in factura.detalles.all():
        detalles_eliminados.append({
            "tipo": det.tipo,
            "tipo_display": det.get_tipo_display(),
            "nombre_item": det.nombre_item,
            "ingreso_neto": str(det.subtotal_medico - det.irt_monto),
        })

    # Si la factura tenía una cita asociada, revertirla a CONFIRMADA
    cita_revertida = None
    if factura.cita and factura.cita.estado == "completada":
        from apps.appointments.models import Cita
        factura.cita.estado = Cita.Estado.CONFIRMADA
        factura.cita.save(update_fields=["estado", "updated_at"])
        cita_revertida = {
            "id": str(factura.cita.id),
            "paciente": factura.cita.paciente.get_full_name(),
            "hora": factura.cita.fecha_hora.strftime("%H:%M"),
            "servicio": factura.cita.servicio.nombre if factura.cita.servicio else "",
        }

    logger.info(
        "FacturaDia eliminada: %s — %s por %s",
        factura.numero_factura,
        factura.paciente_nombre,
        request.user.email,
    )

    # Auditoría
    AuditoriaDia.objects.create(
        dia_clinica=factura.dia_clinica,
        accion=AuditoriaDia.Accion.ELIMINAR_FACTURA,
        usuario=request.user,
        detalle=f"Factura eliminada: {factura.numero_factura or 'S/N'} — {factura.paciente_nombre} — {factura.precio_venta_total} Kz",
    )

    # Eliminar detalles y factura (CASCADE elimina detalles)
    factura.detalles.all().delete()
    factura.delete()

    return JsonResponse({
        "success": True,
        "ingreso_neto": ingreso_neto_eliminado,
        "irt_monto": irt_monto_eliminado,
        "clinica": clinica_nombre,
        "detalles": detalles_eliminados,
        "cita_revertida": cita_revertida,
    })


# ============================================================
# API — INGRESO EXTRA
# ============================================================
@moderador_required
@require_POST
def crear_ingreso_extra(request):
    """Crea un IngresoExtra para el día clínica abierto."""
    data = json.loads(request.body)
    dia_id = data.get("dia_id")
    monto = data.get("monto")
    motivo = (data.get("motivo") or "").strip()

    if not dia_id:
        return JsonResponse({"success": False, "error": "Não há dia aberto."}, status=400)
    if not monto or float(monto) <= 0:
        return JsonResponse({"success": False, "error": "O valor deve ser superior a 0."}, status=400)
    if not motivo:
        return JsonResponse({"success": False, "error": "O motivo é obrigatório."}, status=400)

    dia = get_object_or_404(DiaClinica, id=dia_id, estado=DiaClinica.Estado.ABIERTO, is_active=True)

    ingreso = IngresoExtra.objects.create(
        dia_clinica=dia,
        monto=Decimal(str(monto)),
        motivo=motivo,
        registrado_por=request.user,
    )

    # Auditoría
    AuditoriaDia.objects.create(
        dia_clinica=dia,
        accion=AuditoriaDia.Accion.CREAR_INGRESO_EXTRA,
        usuario=request.user,
        detalle=f"Ingreso Extra: {ingreso.monto} Kz — {motivo}",
    )

    logger.info("IngresoExtra creado: %s Kz — %s por %s", ingreso.monto, motivo, request.user.email)

    return JsonResponse({
        "success": True,
        "ingreso": {
            "id": str(ingreso.id),
            "monto": str(ingreso.monto),
            "motivo": ingreso.motivo,
        },
    })


@moderador_required
@require_POST
def eliminar_ingreso_extra(request):
    """Elimina un IngresoExtra."""
    data = json.loads(request.body)
    ingreso_id = data.get("ingreso_id")
    if not ingreso_id:
        return JsonResponse({"success": False, "error": "ID de receita obrigatório."}, status=400)

    ingreso = get_object_or_404(IngresoExtra, id=ingreso_id, is_active=True)

    # Verificar que el día esté abierto
    if ingreso.dia_clinica.estado != DiaClinica.Estado.ABIERTO:
        return JsonResponse({"success": False, "error": "O dia está fechado."}, status=400)

    monto_eliminado = str(ingreso.monto)
    motivo_eliminado = ingreso.motivo

    # Auditoría
    AuditoriaDia.objects.create(
        dia_clinica=ingreso.dia_clinica,
        accion=AuditoriaDia.Accion.ELIMINAR_INGRESO_EXTRA,
        usuario=request.user,
        detalle=f"Ingreso Extra eliminado: {ingreso.monto} Kz — {motivo_eliminado}",
    )

    logger.info("IngresoExtra eliminado: %s Kz — %s por %s", monto_eliminado, motivo_eliminado, request.user.email)

    ingreso.delete()

    return JsonResponse({
        "success": True,
        "monto": monto_eliminado,
    })


# ============================================================
# MOVIMENTOS DO DIA (AJAX)
# ============================================================
@moderador_required
def movimentos_dia(request):
    """Retorna todos os movimentos do dia agrupados por clínica (JSON)."""
    hoy = timezone.now().date()

    # Todas as clínicas que tiveram dia aberto hoje
    dias = (
        DiaClinica.objects.filter(fecha=hoy, is_active=True)
        .select_related("clinica", "abierto_por", "cerrado_por")
        .order_by("clinica__nombre")
    )

    resultado = []

    for dia in dias:
        # Facturas do dia
        facturas = (
            FacturaDia.objects.filter(dia_clinica=dia, is_active=True)
            .exclude(estado=FacturaDia.Estado.ANULADA)
            .select_related("tipo_cliente")
            .prefetch_related("detalles")
            .order_by("created_at")
        )

        facturas_list = []
        total_pventa = Decimal("0")
        total_medico = Decimal("0")
        total_irt = Decimal("0")
        total_neto = Decimal("0")

        for fd in facturas:
            irt_m = fd.irt_monto
            neto = fd.ingreso_neto
            total_pventa += fd.precio_venta_total
            total_medico += fd.porciento_medico_total
            total_irt += irt_m
            total_neto += neto

            # Detalles por ítem
            detalles = []
            for d in fd.detalles.all():
                detalles.append({
                    "tipo": d.get_tipo_display(),
                    "nome": d.nombre_item,
                    "preco": str(d.precio_venta),
                    "porc_med": str(d.porciento_medico),
                    "sub_med": str(d.subtotal_medico),
                    "irt": str(d.irt_monto),
                })

            facturas_list.append({
                "numero": fd.numero_factura or "S/N",
                "paciente": fd.paciente_nombre,
                "tipo_cliente": fd.tipo_cliente.nombre if fd.tipo_cliente else "—",
                "pventa": str(fd.precio_venta_total),
                "medico": str(fd.porciento_medico_total),
                "irt": str(irt_m),
                "neto": str(neto),
                "estado": fd.get_estado_display(),
                "obs": fd.observaciones or "",
                "detalles": detalles,
            })

        # Receitas extra
        extras = IngresoExtra.objects.filter(
            dia_clinica=dia, is_active=True
        ).select_related("registrado_por").order_by("created_at")

        extras_list = []
        total_extras = Decimal("0")
        for ex in extras:
            total_extras += ex.monto
            extras_list.append({
                "motivo": ex.motivo,
                "monto": str(ex.monto),
                "registado_por": ex.registrado_por.get_full_name(),
            })

        resultado.append({
            "clinica": dia.clinica.nombre,
            "estado": dia.get_estado_display(),
            "hora_abertura": dia.hora_apertura.strftime("%H:%M") if dia.hora_apertura else "",
            "hora_fecho": dia.hora_cierre.strftime("%H:%M") if dia.hora_cierre else "",
            "facturas": facturas_list,
            "extras": extras_list,
            "totais": {
                "pventa": str(total_pventa),
                "medico": str(total_medico),
                "irt": str(total_irt),
                "neto": str(total_neto),
                "extras": str(total_extras),
                "geral": str(total_neto + total_extras),
            },
            "num_facturas": len(facturas_list),
            "num_extras": len(extras_list),
        })

    return JsonResponse({"success": True, "data": resultado, "fecha": hoy.strftime("%d/%m/%Y")})
