<?php
// Prevenir acceso directo
if (!defined('BASEPATH')) exit('No direct script access allowed');

switch ($tipo) {
    // =====================================================
    // CATÁLOGOS SAT
    // =====================================================

    case 'getCatalogoRegimenFiscal':
        try {
            $query = "SELECT clave, descripcion, persona_fisica, persona_moral
                      FROM cat_regimen_fiscal
                      WHERE activo = 1
                      ORDER BY clave ASC";
            $result = $conn->query($query);

            if (!$result) {
                sendResponse(false, "Error al obtener catálogo: " . $conn->error);
            }

            $regimenes = [];
            while ($row = $result->fetch_assoc()) {
                $regimenes[] = [
                    'clave' => $row['clave'],
                    'descripcion' => $row['descripcion'],
                    'persona_fisica' => (bool)$row['persona_fisica'],
                    'persona_moral' => (bool)$row['persona_moral']
                ];
            }

            sendResponse(true, "Catálogo obtenido", $regimenes);
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    case 'getCatalogoUsoCFDI':
        try {
            $personaTipo = $datapost['persona_tipo'] ?? null;

            $query = "SELECT clave, descripcion, persona_fisica, persona_moral
                      FROM cat_uso_cfdi
                      WHERE activo = 1";

            if ($personaTipo === 'fisica') {
                $query .= " AND persona_fisica = 1";
            } elseif ($personaTipo === 'moral') {
                $query .= " AND persona_moral = 1";
            }

            $query .= " ORDER BY clave ASC";
            $result = $conn->query($query);

            if (!$result) {
                sendResponse(false, "Error al obtener catálogo: " . $conn->error);
            }

            $usos = [];
            while ($row = $result->fetch_assoc()) {
                $usos[] = [
                    'clave' => $row['clave'],
                    'descripcion' => $row['descripcion'],
                    'persona_fisica' => (bool)$row['persona_fisica'],
                    'persona_moral' => (bool)$row['persona_moral']
                ];
            }

            sendResponse(true, "Catálogo obtenido", $usos);
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    case 'getCatalogoFormaPago':
        try {
            $query = "SELECT clave, descripcion
                      FROM cat_forma_pago
                      WHERE activo = 1
                      ORDER BY clave ASC";
            $result = $conn->query($query);

            if (!$result) {
                sendResponse(false, "Error al obtener catálogo: " . $conn->error);
            }

            $formas = [];
            while ($row = $result->fetch_assoc()) {
                $formas[] = [
                    'clave' => $row['clave'],
                    'descripcion' => $row['descripcion']
                ];
            }

            sendResponse(true, "Catálogo obtenido", $formas);
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    case 'getCatalogoMetodoPago':
        try {
            $query = "SELECT clave, descripcion
                      FROM cat_metodo_pago
                      WHERE activo = 1
                      ORDER BY clave ASC";
            $result = $conn->query($query);

            if (!$result) {
                sendResponse(false, "Error al obtener catálogo: " . $conn->error);
            }

            $metodos = [];
            while ($row = $result->fetch_assoc()) {
                $metodos[] = [
                    'clave' => $row['clave'],
                    'descripcion' => $row['descripcion']
                ];
            }

            sendResponse(true, "Catálogo obtenido", $metodos);
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    // =====================================================
    // DATOS FISCALES DEL CLIENTE
    // =====================================================

    case 'getDatosFiscalesCliente':
        $cliente_id = intval($datapost['cliente_id'] ?? 0);

        if ($cliente_id <= 0) {
            sendResponse(false, "ID de cliente requerido");
        }

        try {
            $query = "SELECT id, nombre, rfc, razon_social, regimen_fiscal, uso_cfdi,
                             codigo_postal_fiscal, email_facturacion, requiere_factura
                      FROM clientes
                      WHERE id = ?";

            $stmt = $conn->prepare($query);
            $stmt->bind_param("i", $cliente_id);
            $stmt->execute();
            $result = $stmt->get_result();

            if ($row = $result->fetch_assoc()) {
                $datosFiscales = [
                    'cliente_id' => intval($row['id']),
                    'nombre' => $row['nombre'],
                    'rfc' => $row['rfc'] ?? '',
                    'razon_social' => $row['razon_social'] ?? '',
                    'regimen_fiscal' => $row['regimen_fiscal'] ?? '',
                    'uso_cfdi' => $row['uso_cfdi'] ?? '',
                    'codigo_postal_fiscal' => $row['codigo_postal_fiscal'] ?? '',
                    'email_facturacion' => $row['email_facturacion'] ?? '',
                    'requiere_factura' => (bool)($row['requiere_factura'] ?? 0),
                    'datos_completos' => !empty($row['rfc']) && !empty($row['razon_social']) &&
                                         !empty($row['regimen_fiscal']) && !empty($row['codigo_postal_fiscal'])
                ];

                sendResponse(true, "Datos fiscales obtenidos", $datosFiscales);
            } else {
                sendResponse(false, "Cliente no encontrado");
            }
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    case 'actualizarDatosFiscales':
        $cliente_id = intval($datapost['cliente_id'] ?? 0);
        $rfc = strtoupper(trim($datapost['rfc'] ?? ''));
        $razon_social = trim($datapost['razon_social'] ?? '');
        $regimen_fiscal = trim($datapost['regimen_fiscal'] ?? '');
        $uso_cfdi = trim($datapost['uso_cfdi'] ?? '');
        $codigo_postal_fiscal = trim($datapost['codigo_postal_fiscal'] ?? '');
        $email_facturacion = trim($datapost['email_facturacion'] ?? '');
        $requiere_factura = isset($datapost['requiere_factura']) ? intval($datapost['requiere_factura']) : 0;

        if ($cliente_id <= 0) {
            sendResponse(false, "ID de cliente requerido");
        }

        // Validar RFC
        if (!empty($rfc)) {
            $rfcLength = strlen($rfc);
            if ($rfcLength !== 12 && $rfcLength !== 13) {
                sendResponse(false, "RFC inválido. Debe tener 12 caracteres (moral) o 13 caracteres (física)");
            }
        }

        // Validar código postal
        if (!empty($codigo_postal_fiscal) && !preg_match('/^\d{5}$/', $codigo_postal_fiscal)) {
            sendResponse(false, "Código postal inválido. Debe tener 5 dígitos");
        }

        // Validar email
        if (!empty($email_facturacion) && !filter_var($email_facturacion, FILTER_VALIDATE_EMAIL)) {
            sendResponse(false, "Email de facturación inválido");
        }

        try {
            $query = "UPDATE clientes SET
                        rfc = ?,
                        razon_social = ?,
                        regimen_fiscal = ?,
                        uso_cfdi = ?,
                        codigo_postal_fiscal = ?,
                        email_facturacion = ?,
                        requiere_factura = ?
                      WHERE id = ?";

            $stmt = $conn->prepare($query);
            $stmt->bind_param("ssssssii", $rfc, $razon_social, $regimen_fiscal, $uso_cfdi,
                              $codigo_postal_fiscal, $email_facturacion, $requiere_factura, $cliente_id);

            if ($stmt->execute()) {
                sendResponse(true, "Datos fiscales actualizados");
            } else {
                sendResponse(false, "Error al actualizar: " . $stmt->error);
            }
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    // =====================================================
    // ASIGNAR CLIENTE A VENTAS
    // =====================================================

    case 'asignarClienteVentas':
        $venta_ids = $datapost['venta_ids'] ?? [];
        $cliente_id = intval($datapost['cliente_id'] ?? 0);
        $tipo_venta = $datapost['tipo_venta'] ?? 'venta';

        if (empty($venta_ids) || $cliente_id <= 0) {
            sendResponse(false, "IDs de ventas y cliente son requeridos");
        }

        // Determinar tabla según tipo de venta
        $tabla = ($tipo_venta === 'mostrador') ? 'ventas_mostrador' : 'ventas';

        try {
            $placeholders = implode(',', array_fill(0, count($venta_ids), '?'));
            $bindParams = array_merge([$cliente_id], array_map('intval', $venta_ids));
            $types = 'i' . str_repeat('i', count($venta_ids));

            $stmt = $conn->prepare("UPDATE $tabla SET cliente_id = ? WHERE id IN ($placeholders)");
            $stmt->bind_param($types, ...$bindParams);
            $stmt->execute();

            sendResponse(true, "Cliente asignado a " . $stmt->affected_rows . " venta(s)");
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    // =====================================================
    // VENTAS FACTURABLES
    // =====================================================

    case 'getVentasFacturables':
        $cliente_id = intval($datapost['cliente_id'] ?? 0);
        $fecha_inicio = $datapost['fecha_inicio'] ?? null;
        $fecha_fin = $datapost['fecha_fin'] ?? null;

        if ($cliente_id <= 0) {
            sendResponse(false, "ID de cliente requerido");
        }

        try {
            $query = "SELECT v.id, v.folio, v.subtotal, v.impuesto, v.descuento, v.total,
                             v.fecha_creacion as fecha_venta, v.metodo_pago, v.estado,
                             (SELECT COUNT(*) FROM venta_detalles WHERE venta_id = v.id) as total_productos
                      FROM ventas v
                      WHERE v.cliente_id = ?
                        AND (v.facturado = 0 OR v.facturado IS NULL)
                        AND v.id NOT IN (SELECT venta_id FROM factura_ventas)";

            $params = [$cliente_id];
            $types = "i";

            if ($fecha_inicio) {
                $query .= " AND DATE(v.fecha_creacion) >= ?";
                $params[] = $fecha_inicio;
                $types .= "s";
            }

            if ($fecha_fin) {
                $query .= " AND DATE(v.fecha_creacion) <= ?";
                $params[] = $fecha_fin;
                $types .= "s";
            }

            $query .= " ORDER BY v.fecha_creacion DESC";

            $stmt = $conn->prepare($query);
            $stmt->bind_param($types, ...$params);
            $stmt->execute();
            $result = $stmt->get_result();

            $ventas = [];
            while ($row = $result->fetch_assoc()) {
                $ventas[] = [
                    'id' => intval($row['id']),
                    'folio' => $row['folio'],
                    'subtotal' => floatval($row['subtotal']),
                    'impuesto' => floatval($row['impuesto']),
                    'descuento' => floatval($row['descuento']),
                    'total' => floatval($row['total']),
                    'fecha_venta' => $row['fecha_venta'],
                    'metodo_pago' => $row['metodo_pago'],
                    'estado' => $row['estado'],
                    'total_productos' => intval($row['total_productos'])
                ];
            }

            sendResponse(true, "Ventas facturables obtenidas", $ventas);
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    // =====================================================
    // VENTAS FACTURABLES GENERAL (sin filtro de cliente obligatorio)
    // =====================================================

    case 'getVentasFacturablesGeneral':
        $cliente_id = isset($datapost['cliente_id']) && $datapost['cliente_id'] !== '' ? intval($datapost['cliente_id']) : null;
        $fecha_inicio = $datapost['fecha_inicio'] ?? null;
        $fecha_fin = $datapost['fecha_fin'] ?? null;
        $folio = $datapost['folio'] ?? null;
        $caja_id = isset($datapost['caja_id']) && $datapost['caja_id'] !== '' ? intval($datapost['caja_id']) : null;

        try {
            // Query para ventas normales (pedidos/órdenes)
            $queryVentas = "SELECT
                    v.id,
                    v.folio,
                    v.cliente_id,
                    v.cliente_nombre,
                    v.subtotal,
                    v.impuesto,
                    v.descuento,
                    v.total,
                    v.fecha_creacion as fecha_venta,
                    v.metodo_pago,
                    v.estado,
                    (SELECT COUNT(*) FROM venta_detalles WHERE venta_id = v.id) as total_productos,
                    'venta' as tipo_venta
                FROM ventas v
                WHERE (v.facturado = 0 OR v.facturado IS NULL)
                    AND v.id NOT IN (SELECT venta_id FROM factura_ventas WHERE venta_id IS NOT NULL)";

            // Query para ventas de mostrador
            $queryMostrador = "SELECT
                    vm.id,
                    vm.folio,
                    vm.cliente_id,
                    COALESCE(c.nombre, 'Público en General') as cliente_nombre,
                    vm.subtotal,
                    vm.impuesto,
                    vm.descuento,
                    vm.total,
                    vm.fecha_creacion as fecha_venta,
                    vm.metodo_pago,
                    'completada' as estado,
                    (SELECT COUNT(*) FROM ventas_mostrador_detalles WHERE venta_mostrador_id = vm.id) as total_productos,
                    'mostrador' as tipo_venta
                FROM ventas_mostrador vm
                LEFT JOIN clientes c ON vm.cliente_id = c.id
                WHERE (vm.facturado = 0 OR vm.facturado IS NULL)
                    AND vm.id NOT IN (SELECT venta_mostrador_id FROM factura_ventas_mostrador WHERE venta_mostrador_id IS NOT NULL)";

            // Aplicar filtros a ambas queries
            $whereVentas = "";
            $whereMostrador = "";

            if ($cliente_id) {
                $whereVentas .= " AND v.cliente_id = " . intval($cliente_id);
                $whereMostrador .= " AND vm.cliente_id = " . intval($cliente_id);
            }

            if ($fecha_inicio) {
                $whereVentas .= " AND DATE(v.fecha_creacion) >= '" . $conn->real_escape_string($fecha_inicio) . "'";
                $whereMostrador .= " AND DATE(vm.fecha_creacion) >= '" . $conn->real_escape_string($fecha_inicio) . "'";
            }

            if ($fecha_fin) {
                $whereVentas .= " AND DATE(v.fecha_creacion) <= '" . $conn->real_escape_string($fecha_fin) . "'";
                $whereMostrador .= " AND DATE(vm.fecha_creacion) <= '" . $conn->real_escape_string($fecha_fin) . "'";
            }

            if ($folio) {
                $folioEscaped = $conn->real_escape_string($folio);
                $whereVentas .= " AND v.folio LIKE '%" . $folioEscaped . "%'";
                $whereMostrador .= " AND vm.folio LIKE '%" . $folioEscaped . "%'";
            }

            // Filtro de caja solo aplica a ventas de mostrador
            if ($caja_id) {
                $whereMostrador .= " AND vm.caja_id = " . intval($caja_id);
            }

            // Combinar queries con UNION (si hay filtro de caja, excluir ventas normales)
            if ($caja_id) {
                // Solo mostrar ventas de mostrador cuando se filtra por caja
                $query = "(" . $queryMostrador . $whereMostrador . ")
                          ORDER BY fecha_venta DESC LIMIT 100";
            } else {
                // Mostrar ambos tipos de venta
                $query = "(" . $queryVentas . $whereVentas . ")
                          UNION ALL
                          (" . $queryMostrador . $whereMostrador . ")
                          ORDER BY fecha_venta DESC LIMIT 100";
            }

            $result = $conn->query($query);

            if (!$result) {
                sendResponse(false, "Error en consulta: " . $conn->error);
            }

            $ventas = [];
            while ($row = $result->fetch_assoc()) {
                $ventas[] = [
                    'id' => intval($row['id']),
                    'folio' => $row['folio'],
                    'cliente_id' => $row['cliente_id'] ? intval($row['cliente_id']) : null,
                    'cliente_nombre' => $row['cliente_nombre'] ?: 'Público en General',
                    'subtotal' => floatval($row['subtotal']),
                    'impuesto' => floatval($row['impuesto']),
                    'descuento' => floatval($row['descuento']),
                    'total' => floatval($row['total']),
                    'fecha_venta' => $row['fecha_venta'],
                    'metodo_pago' => $row['metodo_pago'],
                    'estado' => $row['estado'],
                    'total_productos' => intval($row['total_productos']),
                    'tipo_venta' => $row['tipo_venta'] // 'venta' o 'mostrador'
                ];
            }

            sendResponse(true, "Ventas facturables obtenidas", $ventas);
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    // =====================================================
    // CONFIGURACIÓN DEL EMISOR
    // =====================================================

    case 'getConfiguracionEmisor':
        try {
            $query = "SELECT clave, valor FROM configuraciones WHERE categoria = 'facturacion'";
            $result = $conn->query($query);

            if (!$result) {
                sendResponse(false, "Error al obtener configuración: " . $conn->error);
            }

            $config = [];
            while ($row = $result->fetch_assoc()) {
                $config[$row['clave']] = $row['valor'];
            }

            sendResponse(true, "Configuración del emisor obtenida", $config);
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    case 'actualizarConfiguracionEmisor':
        $emisor_rfc = strtoupper(trim($datapost['emisor_rfc'] ?? ''));
        $emisor_razon_social = trim($datapost['emisor_razon_social'] ?? '');
        $emisor_regimen_fiscal = trim($datapost['emisor_regimen_fiscal'] ?? '');
        $emisor_codigo_postal = trim($datapost['emisor_codigo_postal'] ?? '');
        $emisor_lugar_expedicion = trim($datapost['emisor_lugar_expedicion'] ?? '');

        try {
            $conn->begin_transaction();

            $configs = [
                'emisor_rfc' => $emisor_rfc,
                'emisor_razon_social' => $emisor_razon_social,
                'emisor_regimen_fiscal' => $emisor_regimen_fiscal,
                'emisor_codigo_postal' => $emisor_codigo_postal,
                'emisor_lugar_expedicion' => $emisor_lugar_expedicion
            ];

            foreach ($configs as $clave => $valor) {
                $updateQuery = "UPDATE configuraciones SET valor = ? WHERE clave = ?";
                $updateStmt = $conn->prepare($updateQuery);
                $updateStmt->bind_param("ss", $valor, $clave);
                $updateStmt->execute();
            }

            $conn->commit();
            sendResponse(true, "Configuración del emisor actualizada");
        } catch (Exception $e) {
            $conn->rollback();
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    // =====================================================
    // EMISORES MÚLTIPLES
    // =====================================================

    case 'getEmisores':
        try {
            // Auto-crear permiso si no existe
            $conn->query("INSERT IGNORE INTO permisos (nombre, descripcion, modulo, accion, activo)
                VALUES ('facturacion_config', 'Configurar emisores y servicio de facturación', 'facturacion', 'config', 1)");

            // Auto-crear tabla si no existe
            $conn->query("CREATE TABLE IF NOT EXISTS emisores_cfdi (
                id INT AUTO_INCREMENT PRIMARY KEY,
                llave VARCHAR(500) NOT NULL,
                rfc VARCHAR(13) NOT NULL,
                razon_social VARCHAR(300) NOT NULL,
                activo TINYINT(1) DEFAULT 1,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

            $result = $conn->query("SELECT id, llave, rfc, razon_social FROM emisores_cfdi WHERE activo = 1 ORDER BY razon_social ASC");
            if (!$result) sendResponse(false, "Error: " . $conn->error);

            $emisores = [];
            while ($row = $result->fetch_assoc()) {
                $emisores[] = $row;
            }
            sendResponse(true, "Emisores obtenidos", $emisores);
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    case 'crearEmisor':
        $llave = trim($datapost['llave'] ?? '');
        $rfc = strtoupper(trim($datapost['rfc'] ?? ''));
        $razon_social = trim($datapost['razon_social'] ?? '');

        if (empty($llave) || empty($rfc) || empty($razon_social)) {
            sendResponse(false, "Llave, RFC y Razón Social son requeridos");
        }

        try {
            // Auto-crear tabla si no existe
            $conn->query("CREATE TABLE IF NOT EXISTS emisores_cfdi (
                id INT AUTO_INCREMENT PRIMARY KEY,
                llave VARCHAR(500) NOT NULL,
                rfc VARCHAR(13) NOT NULL,
                razon_social VARCHAR(300) NOT NULL,
                activo TINYINT(1) DEFAULT 1,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

            $stmt = $conn->prepare("INSERT INTO emisores_cfdi (llave, rfc, razon_social) VALUES (?, ?, ?)");
            $stmt->bind_param("sss", $llave, $rfc, $razon_social);
            $stmt->execute();

            if ($stmt->affected_rows > 0) {
                sendResponse(true, "Emisor creado", ['id' => $conn->insert_id]);
            } else {
                sendResponse(false, "No se pudo crear el emisor");
            }
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    case 'actualizarEmisor':
        $id = intval($datapost['id'] ?? 0);
        $llave = trim($datapost['llave'] ?? '');
        $rfc = strtoupper(trim($datapost['rfc'] ?? ''));
        $razon_social = trim($datapost['razon_social'] ?? '');

        if ($id <= 0) sendResponse(false, "ID requerido");
        if (empty($llave) || empty($rfc) || empty($razon_social)) {
            sendResponse(false, "Llave, RFC y Razón Social son requeridos");
        }

        try {
            $stmt = $conn->prepare("UPDATE emisores_cfdi SET llave = ?, rfc = ?, razon_social = ? WHERE id = ?");
            $stmt->bind_param("sssi", $llave, $rfc, $razon_social, $id);
            $stmt->execute();

            if ($stmt->affected_rows >= 0) {
                sendResponse(true, "Emisor actualizado");
            } else {
                sendResponse(false, "No se encontró el emisor");
            }
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    case 'eliminarEmisor':
        $id = intval($datapost['id'] ?? 0);
        if ($id <= 0) sendResponse(false, "ID requerido");

        try {
            $stmt = $conn->prepare("UPDATE emisores_cfdi SET activo = 0 WHERE id = ?");
            $stmt->bind_param("i", $id);
            $stmt->execute();

            if ($stmt->affected_rows > 0) {
                sendResponse(true, "Emisor eliminado");
            } else {
                sendResponse(false, "No se encontró el emisor");
            }
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    // =====================================================
    // CREAR FACTURA
    // =====================================================

    case 'crearFactura':
        $cliente_id = intval($datapost['cliente_id'] ?? 0);
        $venta_ids = $datapost['venta_ids'] ?? [];
        $pago_cliente_id = intval($datapost['pago_cliente_id'] ?? 0);
        $tipo_factura = $datapost['tipo_factura'] ?? $datapost['tipo'] ?? 'venta'; // 'venta', 'mostrador' o 'pago'
        $uso_cfdi = trim($datapost['uso_cfdi'] ?? '');
        $forma_pago = trim($datapost['forma_pago'] ?? '');
        $metodo_pago = trim($datapost['metodo_pago'] ?? 'PUE');
        $usuario_id = intval($datapost['usuario_id'] ?? 0);
        $emisor_id = intval($datapost['emisor_id'] ?? 0);

        // Datos fiscales del receptor (del dato fiscal seleccionado en frontend)
        $receptor_rfc = trim($datapost['rfc'] ?? '');
        $receptor_razon_social = trim($datapost['razon_social'] ?? '');
        $receptor_regimen_fiscal = trim($datapost['regimen_fiscal'] ?? '');
        $receptor_codigo_postal = trim($datapost['codigo_postal_fiscal'] ?? '');

        // Validaciones
        if ($cliente_id <= 0) {
            sendResponse(false, "Cliente requerido");
        }

        if ($tipo_factura === 'pago') {
            if ($pago_cliente_id <= 0) {
                sendResponse(false, "Debe seleccionar un pago para facturar");
            }
        } elseif ($tipo_factura === 'mostrador' || $tipo_factura === 'venta') {
            if (empty($venta_ids) || !is_array($venta_ids)) {
                sendResponse(false, "Debe seleccionar al menos una venta");
            }
        }

        if (empty($uso_cfdi)) sendResponse(false, "Uso de CFDI requerido");
        if (empty($forma_pago)) sendResponse(false, "Forma de pago requerida");
        if (empty($receptor_rfc) || empty($receptor_razon_social) || empty($receptor_regimen_fiscal) || empty($receptor_codigo_postal)) {
            sendResponse(false, "Datos fiscales del receptor incompletos");
        }

        try {
            // ========================================
            // FASE 1: LEER TODOS LOS DATOS (antes de transacción)
            // ========================================

            // Obtener email del cliente
            $clienteResult = mysqli_query($conn, "SELECT email_facturacion FROM clientes WHERE id = " . intval($cliente_id));
            $clienteData = mysqli_fetch_assoc($clienteResult);
            $receptor_email = $clienteData['email_facturacion'] ?? '';
            mysqli_free_result($clienteResult);

            // Obtener emisor de emisores_cfdi
            $emisorData = null;
            if ($emisor_id > 0) {
                $emisorResult = mysqli_query($conn, "SELECT id, llave, rfc, razon_social FROM emisores_cfdi WHERE id = " . intval($emisor_id) . " AND activo = 1");
                $emisorData = mysqli_fetch_assoc($emisorResult);
                mysqli_free_result($emisorResult);
            }
            if (!$emisorData) {
                sendResponse(false, "Emisor no encontrado o inactivo");
            }

            // Obtener URL del servicio de facturación
            $urlResult = mysqli_query($conn, "SELECT valor FROM configuraciones WHERE clave = 'empresa_facturacion_url'");
            $urlRow = mysqli_fetch_assoc($urlResult);
            $facturacion_url = trim($urlRow['valor'] ?? '');
            mysqli_free_result($urlResult);
            if (empty($facturacion_url)) {
                sendResponse(false, "URL del servicio de facturación no configurada");
            }

            // Obtener configuración del emisor (regimen, cp)
            $emisorConfigResult = mysqli_query($conn, "SELECT clave, valor FROM configuraciones WHERE categoria = 'facturacion'");
            $emisorConfig = [];
            while ($row = mysqli_fetch_assoc($emisorConfigResult)) {
                $emisorConfig[$row['clave']] = $row['valor'];
            }
            mysqli_free_result($emisorConfigResult);
            $emisor_regimen = $emisorConfig['emisor_regimen_fiscal'] ?? '612';
            $emisor_cp = $emisorConfig['emisor_codigo_postal'] ?? '';

            // Calcular totales segun tipo de factura
            $pago_info = null;
            $ventaIdsStr = '';

            if ($tipo_factura === 'pago') {
                $pagoResult = mysqli_query($conn, "SELECT pc.*, c.nombre as cliente_nombre
                              FROM pagos_clientes pc
                              JOIN clientes c ON pc.cliente_id = c.id
                              WHERE pc.id = " . intval($pago_cliente_id) . " AND pc.cliente_id = " . intval($cliente_id));
                $pago_info = mysqli_fetch_assoc($pagoResult);
                mysqli_free_result($pagoResult);

                if (!$pago_info) { sendResponse(false, "Pago no encontrado"); }
                if ($pago_info['facturado'] == 1) { sendResponse(false, "Este pago ya fue facturado"); }

                $monto_total = floatval($pago_info['monto_aplicado']);
                $totales = [
                    'subtotal' => round($monto_total / 1.16, 2),
                    'impuesto' => round($monto_total - ($monto_total / 1.16), 2),
                    'descuento' => 0,
                    'total' => $monto_total
                ];
            } elseif ($tipo_factura === 'mostrador') {
                $ventaIdsStr = implode(',', array_map('intval', $venta_ids));
                $totalesResult = mysqli_query($conn, "SELECT SUM(subtotal) as subtotal, SUM(impuesto) as impuesto,
                                        SUM(descuento) as descuento, SUM(total) as total
                                FROM ventas_mostrador WHERE id IN ($ventaIdsStr)");
                $totales = mysqli_fetch_assoc($totalesResult);
                mysqli_free_result($totalesResult);
            } else {
                $ventaIdsStr = implode(',', array_map('intval', $venta_ids));
                $totalesResult = mysqli_query($conn, "SELECT SUM(subtotal) as subtotal, SUM(impuesto) as impuesto,
                                        SUM(descuento) as descuento, SUM(total) as total
                                FROM ventas WHERE id IN ($ventaIdsStr)");
                $totales = mysqli_fetch_assoc($totalesResult);
                mysqli_free_result($totalesResult);
            }

            // Obtener detalles de productos (para conceptos CFDI)
            $detallesArr = [];
            if ($tipo_factura === 'mostrador' && !empty($ventaIdsStr)) {
                $detallesResult = mysqli_query($conn, "SELECT vmd.producto_id, vmd.producto_nombre as nombre, p.codigo_barras as codigo,
                                         p.cve_cfdi, vmd.cantidad, vmd.precio_unitario, vmd.subtotal
                                  FROM ventas_mostrador_detalles vmd
                                  LEFT JOIN productos p ON vmd.producto_id = p.id
                                  WHERE vmd.venta_mostrador_id IN ($ventaIdsStr)");
                if (!$detallesResult) { sendResponse(false, "Error query detalles mostrador: " . mysqli_error($conn)); }
                while ($row = mysqli_fetch_assoc($detallesResult)) { $detallesArr[] = $row; }
                mysqli_free_result($detallesResult);
            } elseif ($tipo_factura === 'venta' && !empty($ventaIdsStr)) {
                $detallesResult = mysqli_query($conn, "SELECT vd.producto_id, p.nombre, p.codigo_barras as codigo,
                                         p.cve_cfdi, vd.cantidad, vd.precio_unitario, vd.subtotal
                                  FROM venta_detalles vd
                                  LEFT JOIN productos p ON vd.producto_id = p.id
                                  WHERE vd.venta_id IN ($ventaIdsStr)");
                if (!$detallesResult) { sendResponse(false, "Error query detalles ventas: " . mysqli_error($conn)); }
                while ($row = mysqli_fetch_assoc($detallesResult)) { $detallesArr[] = $row; }
                mysqli_free_result($detallesResult);
            }

            // Generar folio
            $folioResult = mysqli_query($conn, "SELECT valor FROM configuraciones WHERE clave = 'facturacion_folio_actual'");
            $folioRow = mysqli_fetch_assoc($folioResult);
            $folioActual = intval($folioRow['valor'] ?? 0) + 1;
            mysqli_free_result($folioResult);

            $serieResult = mysqli_query($conn, "SELECT valor FROM configuraciones WHERE clave = 'facturacion_serie_default'");
            $serieRow = mysqli_fetch_assoc($serieResult);
            $serie = $serieRow['valor'] ?? 'A';
            mysqli_free_result($serieResult);

            $folio = str_pad($folioActual, 6, '0', STR_PAD_LEFT);

            // ========================================
            // FASE 2: ESCRITURAS (dentro de transacción)
            // ========================================
            $conn->begin_transaction();

            // Insertar factura en BD
            $insertFacturaQuery = "INSERT INTO facturas (
                folio, serie, cliente_id, usuario_id,
                receptor_rfc, receptor_razon_social, receptor_regimen_fiscal,
                receptor_uso_cfdi, receptor_codigo_postal, receptor_email,
                emisor_rfc, emisor_razon_social, emisor_regimen_fiscal, emisor_codigo_postal,
                subtotal, descuento, total_impuestos, total,
                forma_pago, metodo_pago, moneda, fecha_emision, estado
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'MXN', NOW(), 'pendiente_timbrado')";

            $insertStmt = $conn->prepare($insertFacturaQuery);
            $insertStmt->bind_param(
                "ssiissssssssssddddss",
                $folio, $serie, $cliente_id, $usuario_id,
                $receptor_rfc, $receptor_razon_social, $receptor_regimen_fiscal,
                $uso_cfdi, $receptor_codigo_postal, $receptor_email,
                $emisorData['rfc'], $emisorData['razon_social'],
                $emisor_regimen, $emisor_cp,
                $totales['subtotal'], $totales['descuento'], $totales['impuesto'], $totales['total'],
                $forma_pago, $metodo_pago
            );

            if (!$insertStmt->execute()) {
                $conn->rollback();
                sendResponse(false, "Error al crear factura: " . $insertStmt->error);
            }

            $factura_id = $conn->insert_id;
            $insertStmt->close();

            // ========================================
            // INSERTAR CONCEPTOS Y OBTENER PARA CFDI
            // ========================================
            $conceptosCFDI = [];

            if ($tipo_factura === 'pago') {
                $conceptoQuery = "INSERT INTO factura_conceptos (
                    factura_id, producto_id, no_identificacion, descripcion,
                    cantidad, unidad, valor_unitario, importe, descuento,
                    clave_prod_serv, clave_unidad, objeto_imp, base_iva, tasa_iva, importe_iva
                ) VALUES (?, 0, ?, ?, 1, 'Servicio', ?, ?, 0, '84111506', 'E48', '02', ?, 0.160000, ?)";
                $conceptoStmt = $conn->prepare($conceptoQuery);

                $descripcion = "Pago de cuentas por cobrar - Folio " . $pago_info['folio'];
                $no_identificacion = $pago_info['folio'];
                $subtotal = $totales['subtotal'];
                $impuesto = $totales['impuesto'];

                $conceptoStmt->bind_param("issdddd", $factura_id, $no_identificacion, $descripcion, $subtotal, $subtotal, $subtotal, $impuesto);
                if (!$conceptoStmt->execute()) { $conn->rollback(); sendResponse(false, "Error al insertar concepto: " . $conceptoStmt->error); }

                $conceptosCFDI[] = [
                    'ClaveProdServ' => '84111506',
                    'NoIdentificacion' => $no_identificacion,
                    'Cantidad' => '1.00',
                    'ClaveUnidad' => 'E48',
                    'Unidad' => 'Servicio',
                    'Descripcion' => $descripcion,
                    'ValorUnitario' => number_format($subtotal, 2, '.', ''),
                    'Importe' => number_format($subtotal, 2, '.', ''),
                    'DescuentoSpecified' => false,
                    'Parte' => [],
                    'ComplementoConcepto' => null,
                    'CuentaPredial' => null,
                    'InformacionAduanera' => [],
                    'Impuestos' => [
                        'Retenciones' => null,
                        'Traslados' => [[
                            'Base' => number_format($subtotal, 2, '.', ''),
                            'Impuesto' => '002',
                            'TipoFactor' => 'Tasa',
                            'TasaOCuota' => '0.160000',
                            'TasaOCuotaSpecified' => true,
                            'Importe' => number_format($impuesto, 2, '.', ''),
                            'ImporteSpecified' => true
                        ]]
                    ],
                    'ObjetoImp' => '02'
                ];

                // Marcar el pago como facturado
                mysqli_query($conn, "UPDATE pagos_clientes SET facturado = 1, factura_id = $factura_id, fecha_facturacion = NOW() WHERE id = " . intval($pago_cliente_id));

            } elseif ($tipo_factura === 'mostrador') {
                // Relacionar ventas de mostrador
                foreach ($venta_ids as $vid) {
                    $vid = intval($vid);
                    mysqli_query($conn, "INSERT INTO factura_ventas_mostrador (factura_id, venta_mostrador_id) VALUES ($factura_id, $vid)");
                }

                // Insertar conceptos (detallesArr ya fue leído en Fase 1)
                $conceptoStmt = $conn->prepare("INSERT INTO factura_conceptos (
                    factura_id, producto_id, no_identificacion, descripcion,
                    cantidad, unidad, valor_unitario, importe, descuento,
                    clave_prod_serv, clave_unidad, objeto_imp, base_iva, tasa_iva, importe_iva
                ) VALUES (?, ?, ?, ?, ?, 'Pieza', ?, ?, 0, ?, ?, '02', ?, 0.160000, ?)");

                foreach ($detallesArr as $det) {
                    $base_iva = floatval($det['subtotal']);
                    $importe_iva = round($base_iva * 0.16, 2);
                    $clave_sat = !empty($det['cve_cfdi']) ? $det['cve_cfdi'] : '01010101';
                    $unidad_sat = 'H87';
                    $codigo = $det['codigo'] ?? '';
                    $nombre = $det['nombre'] ?? '';

                    $conceptoStmt->bind_param("iissdddssdd",
                        $factura_id, $det['producto_id'], $codigo, $nombre,
                        $det['cantidad'], $det['precio_unitario'], $det['subtotal'],
                        $clave_sat, $unidad_sat, $base_iva, $importe_iva
                    );
                    if (!$conceptoStmt->execute()) { $conn->rollback(); sendResponse(false, "Error concepto mostrador: " . $conceptoStmt->error); }

                    $conceptosCFDI[] = [
                        'ClaveProdServ' => $clave_sat,
                        'NoIdentificacion' => $codigo,
                        'Cantidad' => number_format(floatval($det['cantidad']), 2, '.', ''),
                        'ClaveUnidad' => $unidad_sat,
                        'Unidad' => 'Pieza',
                        'Descripcion' => $nombre,
                        'ValorUnitario' => number_format(floatval($det['precio_unitario']), 2, '.', ''),
                        'Importe' => number_format($base_iva, 2, '.', ''),
                        'DescuentoSpecified' => false,
                        'Parte' => [],
                        'ComplementoConcepto' => null,
                        'CuentaPredial' => null,
                        'InformacionAduanera' => [],
                        'Impuestos' => [
                            'Retenciones' => null,
                            'Traslados' => [[
                                'Base' => number_format($base_iva, 2, '.', ''),
                                'Impuesto' => '002',
                                'TipoFactor' => 'Tasa',
                                'TasaOCuota' => '0.160000',
                                'TasaOCuotaSpecified' => true,
                                'Importe' => number_format($importe_iva, 2, '.', ''),
                                'ImporteSpecified' => true
                            ]]
                        ],
                        'ObjetoImp' => '02'
                    ];
                }
                $conceptoStmt->close();

                mysqli_query($conn, "UPDATE ventas_mostrador SET facturado = 1 WHERE id IN ($ventaIdsStr)");

            } else {
                // Relacionar ventas normales
                foreach ($venta_ids as $vid) {
                    $vid = intval($vid);
                    mysqli_query($conn, "INSERT INTO factura_ventas (factura_id, venta_id) VALUES ($factura_id, $vid)");
                }

                // Insertar conceptos (detallesArr ya fue leído en Fase 1)
                $conceptoStmt = $conn->prepare("INSERT INTO factura_conceptos (
                    factura_id, producto_id, no_identificacion, descripcion,
                    cantidad, unidad, valor_unitario, importe, descuento,
                    clave_prod_serv, clave_unidad, objeto_imp, base_iva, tasa_iva, importe_iva
                ) VALUES (?, ?, ?, ?, ?, 'Pieza', ?, ?, 0, ?, ?, '02', ?, 0.160000, ?)");

                foreach ($detallesArr as $det) {
                    $base_iva = floatval($det['subtotal']);
                    $importe_iva = round($base_iva * 0.16, 2);
                    $clave_sat = !empty($det['cve_cfdi']) ? $det['cve_cfdi'] : '01010101';
                    $unidad_sat = 'H87';
                    $codigo = $det['codigo'] ?? '';
                    $nombre = $det['nombre'] ?? '';

                    $conceptoStmt->bind_param("iissdddssdd",
                        $factura_id, $det['producto_id'], $codigo, $nombre,
                        $det['cantidad'], $det['precio_unitario'], $det['subtotal'],
                        $clave_sat, $unidad_sat, $base_iva, $importe_iva
                    );
                    if (!$conceptoStmt->execute()) { $conn->rollback(); sendResponse(false, "Error concepto ventas: " . $conceptoStmt->error); }

                    $conceptosCFDI[] = [
                        'ClaveProdServ' => $clave_sat,
                        'NoIdentificacion' => $codigo,
                        'Cantidad' => number_format(floatval($det['cantidad']), 2, '.', ''),
                        'ClaveUnidad' => $unidad_sat,
                        'Unidad' => 'Pieza',
                        'Descripcion' => $nombre,
                        'ValorUnitario' => number_format(floatval($det['precio_unitario']), 2, '.', ''),
                        'Importe' => number_format($base_iva, 2, '.', ''),
                        'DescuentoSpecified' => false,
                        'Parte' => [],
                        'ComplementoConcepto' => null,
                        'CuentaPredial' => null,
                        'InformacionAduanera' => [],
                        'Impuestos' => [
                            'Retenciones' => null,
                            'Traslados' => [[
                                'Base' => number_format($base_iva, 2, '.', ''),
                                'Impuesto' => '002',
                                'TipoFactor' => 'Tasa',
                                'TasaOCuota' => '0.160000',
                                'TasaOCuotaSpecified' => true,
                                'Importe' => number_format($importe_iva, 2, '.', ''),
                                'ImporteSpecified' => true
                            ]]
                        ],
                        'ObjetoImp' => '02'
                    ];
                }
                $conceptoStmt->close();

                mysqli_query($conn, "UPDATE ventas SET facturado = 1 WHERE id IN ($ventaIdsStr)");
            }

            // Actualizar folio
            mysqli_query($conn, "UPDATE configuraciones SET valor = '" . mysqli_real_escape_string($conn, strval($folioActual)) . "' WHERE clave = 'facturacion_folio_actual'");

            $conn->commit();

            // ========================================
            // TIMBRADO CFDI - LLAMAR AL SERVICIO SOAP
            // ========================================
            $totalImpuestosTrasladados = round(floatval($totales['impuesto']), 2);

            $cfdiJSON = [
                'Llave' => $emisorData['llave'],
                'Comprobante' => [
                    'Version' => '4.0',
                    'Serie' => $serie,
                    'Folio' => strval($folioActual),
                    'Fecha' => date('Y-m-d\TH:i:s'),
                    'Sello' => null,
                    'FormaPago' => $forma_pago,
                    'FormaPagoSpecified' => true,
                    'NoCertificado' => null,
                    'Certificado' => null,
                    'CondicionesDePago' => null,
                    'SubTotal' => number_format(floatval($totales['subtotal']), 2, '.', ''),
                    'DescuentoSpecified' => false,
                    'Moneda' => 'MXN',
                    'TipoCambio' => '1',
                    'TipoCambioSpecified' => true,
                    'Total' => number_format(floatval($totales['total']), 2, '.', ''),
                    'TipoDeComprobante' => 'I',
                    'MetodoPago' => $metodo_pago,
                    'MetodoPagoSpecified' => true,
                    'LugarExpedicion' => $emisor_cp,
                    'Confirmacion' => null,
                    'CfdiRelacionados' => null,
                    'Emisor' => null,
                    'Receptor' => [
                        'Rfc' => $receptor_rfc,
                        'Nombre' => $receptor_razon_social,
                        'DomicilioFiscalReceptor' => $receptor_codigo_postal,
                        'ResidenciaFiscalSpecified' => false,
                        'NumRegIdTrib' => null,
                        'RegimenFiscalReceptor' => $receptor_regimen_fiscal,
                        'UsoCFDI' => $uso_cfdi
                    ],
                    'Conceptos' => $conceptosCFDI,
                    'Impuestos' => [
                        'TotalImpuestosTrasladados' => number_format($totalImpuestosTrasladados, 2, '.', ''),
                        'TotalImpuestosTrasladadosSpecified' => true,
                        'TotalImpuestosRetenidos' => '0.00',
                        'TotalImpuestosRetenidosSpecified' => false,
                        'Retenciones' => null,
                        'Traslados' => [[
                            'Base' => number_format(floatval($totales['subtotal']), 2, '.', ''),
                            'Impuesto' => '002',
                            'TipoFactor' => 'Tasa',
                            'TasaOCuota' => '0.160000',
                            'TasaOCuotaSpecified' => true,
                            'Importe' => number_format($totalImpuestosTrasladados, 2, '.', ''),
                            'ImporteSpecified' => true
                        ]]
                    ]
                ]
            ];

            $jsonCFDIString = json_encode($cfdiJSON, JSON_UNESCAPED_UNICODE);

            // Construir envelope SOAP
            $soapEnvelope = '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:xsd="http://www.w3.org/2001/XMLSchema"
              xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <TimbrarCFDI xmlns="http://tempuri.org/">
      <solicitudTimbradoComprobanteIngreso>' . str_replace('&', '&amp;', $jsonCFDIString) . '</solicitudTimbradoComprobanteIngreso>
    </TimbrarCFDI>
  </soap:Body>
</soap:Envelope>';

            // Enviar petición SOAP al PAC
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $facturacion_url);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $soapEnvelope);
            curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: text/xml; charset=utf-8"]);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 120);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);

            $soapResponse = curl_exec($ch);
            $curlErrno = curl_errno($ch);
            $curlError = curl_error($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            $timbradoExitoso = false;
            $timbradoError = null;
            $uuid = null;
            $xmlTimbrado = null;

            if ($curlErrno) {
                $timbradoError = "Error de conexión con el PAC: " . $curlError;
            } elseif ($httpCode !== 200) {
                $timbradoError = "Error HTTP del PAC: " . $httpCode;
            } else {
                // Parsear respuesta SOAP
                $cleanResponse = str_replace(["soap:", "xsi:", "xsd:"], "", $soapResponse);
                libxml_use_internal_errors(true);
                $xml = simplexml_load_string($cleanResponse);

                if ($xml === false) {
                    $timbradoError = "Error al parsear respuesta XML del PAC";
                } else {
                    $body = $xml->Body;
                    $timbrarResponse = $body->TimbrarCFDIResponse;
                    $resultado = $timbrarResponse->TimbrarCFDIResult;

                    if (isset($resultado) && (string)$resultado !== '') {
                        $resultStr = (string)$resultado;
                        $resultJson = json_decode($resultStr, true);

                        if ($resultJson !== null) {
                            if (isset($resultJson['OperacionExitosa']) && $resultJson['OperacionExitosa'] === true) {
                                $timbradoExitoso = true;
                                $uuid = $resultJson['UUID'] ?? $resultJson['Uuid'] ?? null;
                                $xmlTimbrado = $resultJson['XMLTimbrado'] ?? $resultJson['XmlTimbrado'] ?? null;
                            } else {
                                $timbradoError = $resultJson['MensajeErrorDetallado'] ?? $resultJson['MensajeError'] ?? 'Error de timbrado desconocido';
                            }
                        } else {
                            $timbradoError = "Respuesta del PAC no es JSON válido";
                        }
                    } else {
                        // Buscar SOAP Fault
                        $fault = $body->Fault;
                        if (isset($fault)) {
                            $timbradoError = "SOAP Fault: " . (string)$fault->faultstring;
                        } else {
                            $timbradoError = "Respuesta vacía del PAC";
                        }
                    }
                }
            }

            // Actualizar factura según resultado del timbrado
            if ($timbradoExitoso && $uuid) {
                $updateStmt = $conn->prepare("UPDATE facturas SET uuid = ?, xml_timbrado = ?, estado = 'timbrada', fecha_timbrado = NOW() WHERE id = ?");
                $updateStmt->bind_param("ssi", $uuid, $xmlTimbrado, $factura_id);
                $updateStmt->execute();

                sendResponse(true, "Factura timbrada exitosamente", [
                    'factura_id' => $factura_id,
                    'folio' => $serie . $folio,
                    'total' => floatval($totales['total']),
                    'estado' => 'timbrada',
                    'uuid' => $uuid
                ]);
            } else {
                // La factura se creó en BD pero no se timbró
                sendResponse(true, "Factura creada pero error al timbrar: " . $timbradoError, [
                    'factura_id' => $factura_id,
                    'folio' => $serie . $folio,
                    'total' => floatval($totales['total']),
                    'estado' => 'pendiente_timbrado',
                    'timbrado_error' => $timbradoError
                ]);
            }

        } catch (Exception $e) {
            $conn->rollback();
            sendResponse(false, "Error al crear factura: " . $e->getMessage());
        }
        break;

    // =====================================================
    // LISTAR FACTURAS
    // =====================================================

    case 'getFacturas':
        $fecha_inicio = $datapost['fecha_inicio'] ?? null;
        $fecha_fin = $datapost['fecha_fin'] ?? null;
        $cliente_id = intval($datapost['cliente_id'] ?? 0);
        $estado = $datapost['estado'] ?? null;
        $folio = trim($datapost['folio'] ?? '');

        try {
            $query = "SELECT f.id, f.uuid, f.folio, f.serie, f.cliente_id,
                             f.receptor_rfc, f.receptor_razon_social,
                             f.subtotal, f.descuento, f.total_impuestos, f.total,
                             f.forma_pago, f.metodo_pago, f.estado,
                             f.fecha_emision, f.fecha_timbrado,
                             c.nombre as cliente_nombre
                      FROM facturas f
                      LEFT JOIN clientes c ON f.cliente_id = c.id
                      WHERE 1=1";

            $params = [];
            $types = "";

            if ($fecha_inicio) {
                $query .= " AND DATE(f.fecha_emision) >= ?";
                $params[] = $fecha_inicio;
                $types .= "s";
            }

            if ($fecha_fin) {
                $query .= " AND DATE(f.fecha_emision) <= ?";
                $params[] = $fecha_fin;
                $types .= "s";
            }

            if ($cliente_id > 0) {
                $query .= " AND f.cliente_id = ?";
                $params[] = $cliente_id;
                $types .= "i";
            }

            if ($estado) {
                $query .= " AND f.estado = ?";
                $params[] = $estado;
                $types .= "s";
            }

            if (!empty($folio)) {
                $query .= " AND (f.folio LIKE ? OR CONCAT(f.serie, f.folio) LIKE ?)";
                $folioSearch = "%$folio%";
                $params[] = $folioSearch;
                $params[] = $folioSearch;
                $types .= "ss";
            }

            $query .= " ORDER BY f.fecha_emision DESC LIMIT 500";

            if (!empty($params)) {
                $stmt = $conn->prepare($query);
                $stmt->bind_param($types, ...$params);
                $stmt->execute();
                $result = $stmt->get_result();
            } else {
                $result = $conn->query($query);
            }

            $facturas = [];
            while ($row = $result->fetch_assoc()) {
                $facturas[] = [
                    'id' => intval($row['id']),
                    'uuid' => $row['uuid'],
                    'folio' => $row['serie'] . $row['folio'],
                    'serie' => $row['serie'],
                    'folio_numero' => $row['folio'],
                    'cliente_id' => intval($row['cliente_id']),
                    'cliente_nombre' => $row['cliente_nombre'],
                    'receptor_rfc' => $row['receptor_rfc'],
                    'receptor_razon_social' => $row['receptor_razon_social'],
                    'subtotal' => floatval($row['subtotal']),
                    'descuento' => floatval($row['descuento']),
                    'total_impuestos' => floatval($row['total_impuestos']),
                    'total' => floatval($row['total']),
                    'forma_pago' => $row['forma_pago'],
                    'metodo_pago' => $row['metodo_pago'],
                    'estado' => $row['estado'],
                    'fecha_emision' => $row['fecha_emision'],
                    'fecha_timbrado' => $row['fecha_timbrado']
                ];
            }

            sendResponse(true, "Facturas obtenidas", $facturas);
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    // =====================================================
    // DETALLE DE FACTURA
    // =====================================================

    case 'getFactura':
        $factura_id = intval($datapost['id'] ?? 0);
        $uuid = trim($datapost['uuid'] ?? '');

        if ($factura_id <= 0 && empty($uuid)) {
            sendResponse(false, "ID o UUID de factura requerido");
        }

        try {
            $query = "SELECT f.*, c.nombre as cliente_nombre
                      FROM facturas f
                      LEFT JOIN clientes c ON f.cliente_id = c.id
                      WHERE " . ($factura_id > 0 ? "f.id = ?" : "f.uuid = ?");

            $stmt = $conn->prepare($query);
            if ($factura_id > 0) {
                $stmt->bind_param("i", $factura_id);
            } else {
                $stmt->bind_param("s", $uuid);
            }
            $stmt->execute();
            $result = $stmt->get_result();

            if ($row = $result->fetch_assoc()) {
                $factura_id = intval($row['id']);

                // Obtener conceptos
                $conceptosQuery = "SELECT * FROM factura_conceptos WHERE factura_id = ?";
                $conceptosStmt = $conn->prepare($conceptosQuery);
                $conceptosStmt->bind_param("i", $factura_id);
                $conceptosStmt->execute();
                $conceptosResult = $conceptosStmt->get_result();

                $conceptos = [];
                while ($concepto = $conceptosResult->fetch_assoc()) {
                    $conceptos[] = [
                        'id' => intval($concepto['id']),
                        'producto_id' => intval($concepto['producto_id']),
                        'descripcion' => $concepto['descripcion'],
                        'no_identificacion' => $concepto['no_identificacion'],
                        'cantidad' => floatval($concepto['cantidad']),
                        'unidad' => $concepto['unidad'],
                        'valor_unitario' => floatval($concepto['valor_unitario']),
                        'importe' => floatval($concepto['importe']),
                        'descuento' => floatval($concepto['descuento']),
                        'base_iva' => floatval($concepto['base_iva']),
                        'tasa_iva' => floatval($concepto['tasa_iva']),
                        'importe_iva' => floatval($concepto['importe_iva'])
                    ];
                }

                // Obtener ventas relacionadas
                $ventasQuery = "SELECT v.id, v.folio, v.total, v.fecha_creacion as fecha_venta
                               FROM factura_ventas fv
                               JOIN ventas v ON fv.venta_id = v.id
                               WHERE fv.factura_id = ?";
                $ventasStmt = $conn->prepare($ventasQuery);
                $ventasStmt->bind_param("i", $factura_id);
                $ventasStmt->execute();
                $ventasResult = $ventasStmt->get_result();

                $ventas = [];
                while ($venta = $ventasResult->fetch_assoc()) {
                    $ventas[] = [
                        'id' => intval($venta['id']),
                        'folio' => $venta['folio'],
                        'total' => floatval($venta['total']),
                        'fecha_venta' => $venta['fecha_venta']
                    ];
                }

                $factura = [
                    'id' => intval($row['id']),
                    'uuid' => $row['uuid'],
                    'folio' => $row['serie'] . $row['folio'],
                    'serie' => $row['serie'],
                    'folio_numero' => $row['folio'],
                    'cliente_id' => intval($row['cliente_id']),
                    'cliente_nombre' => $row['cliente_nombre'],
                    'receptor' => [
                        'rfc' => $row['receptor_rfc'],
                        'razon_social' => $row['receptor_razon_social'],
                        'regimen_fiscal' => $row['receptor_regimen_fiscal'],
                        'uso_cfdi' => $row['receptor_uso_cfdi'],
                        'codigo_postal' => $row['receptor_codigo_postal'],
                        'email' => $row['receptor_email']
                    ],
                    'emisor' => [
                        'rfc' => $row['emisor_rfc'],
                        'razon_social' => $row['emisor_razon_social'],
                        'regimen_fiscal' => $row['emisor_regimen_fiscal'],
                        'codigo_postal' => $row['emisor_codigo_postal']
                    ],
                    'subtotal' => floatval($row['subtotal']),
                    'descuento' => floatval($row['descuento']),
                    'total_impuestos' => floatval($row['total_impuestos']),
                    'total' => floatval($row['total']),
                    'forma_pago' => $row['forma_pago'],
                    'metodo_pago' => $row['metodo_pago'],
                    'moneda' => $row['moneda'],
                    'estado' => $row['estado'],
                    'ruta_xml' => $row['ruta_xml'],
                    'ruta_pdf' => $row['ruta_pdf'],
                    'fecha_emision' => $row['fecha_emision'],
                    'fecha_timbrado' => $row['fecha_timbrado'],
                    'fecha_cancelacion' => $row['fecha_cancelacion'],
                    'motivo_cancelacion' => $row['motivo_cancelacion'],
                    'conceptos' => $conceptos,
                    'ventas' => $ventas
                ];

                sendResponse(true, "Factura obtenida", $factura);
            } else {
                sendResponse(false, "Factura no encontrada");
            }
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    // =====================================================
    // DESCARGAR XML/PDF
    // =====================================================

    case 'descargarFacturaXML':
        $factura_id = intval($datapost['factura_id'] ?? 0);

        if ($factura_id <= 0) {
            sendResponse(false, "ID de factura requerido");
        }

        try {
            $query = "SELECT ruta_xml, folio, serie FROM facturas WHERE id = ?";
            $stmt = $conn->prepare($query);
            $stmt->bind_param("i", $factura_id);
            $stmt->execute();
            $result = $stmt->get_result();

            if ($row = $result->fetch_assoc()) {
                if (!empty($row['ruta_xml'])) {
                    sendResponse(true, "URL de XML", [
                        'url' => $row['ruta_xml'],
                        'filename' => $row['serie'] . $row['folio'] . '.xml'
                    ]);
                } else {
                    sendResponse(false, "El XML aún no está disponible (factura pendiente de timbrado)");
                }
            } else {
                sendResponse(false, "Factura no encontrada");
            }
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    case 'descargarFacturaPDF':
        $factura_id = intval($datapost['factura_id'] ?? 0);

        if ($factura_id <= 0) {
            sendResponse(false, "ID de factura requerido");
        }

        try {
            $query = "SELECT ruta_pdf, folio, serie FROM facturas WHERE id = ?";
            $stmt = $conn->prepare($query);
            $stmt->bind_param("i", $factura_id);
            $stmt->execute();
            $result = $stmt->get_result();

            if ($row = $result->fetch_assoc()) {
                if (!empty($row['ruta_pdf'])) {
                    sendResponse(true, "URL de PDF", [
                        'url' => $row['ruta_pdf'],
                        'filename' => $row['serie'] . $row['folio'] . '.pdf'
                    ]);
                } else {
                    sendResponse(false, "El PDF aún no está disponible (factura pendiente de timbrado)");
                }
            } else {
                sendResponse(false, "Factura no encontrada");
            }
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    // =====================================================
    // REENVIAR FACTURA POR EMAIL
    // =====================================================

    case 'reenviarFactura':
        $factura_id = intval($datapost['factura_id'] ?? 0);
        $email = trim($datapost['email'] ?? '');

        if ($factura_id <= 0) {
            sendResponse(false, "ID de factura requerido");
        }

        try {
            // Obtener datos de la factura
            $query = "SELECT receptor_email, ruta_xml, ruta_pdf, folio, serie, estado
                      FROM facturas WHERE id = ?";
            $stmt = $conn->prepare($query);
            $stmt->bind_param("i", $factura_id);
            $stmt->execute();
            $result = $stmt->get_result();

            if ($row = $result->fetch_assoc()) {
                if ($row['estado'] === 'pendiente_timbrado') {
                    sendResponse(false, "La factura aún no ha sido timbrada");
                }

                $emailDestino = !empty($email) ? $email : $row['receptor_email'];

                if (empty($emailDestino)) {
                    sendResponse(false, "No hay email de destino");
                }

                // TODO: Implementar envío de email real
                // Por ahora solo simular
                sendResponse(true, "Factura enviada a " . $emailDestino, [
                    'email' => $emailDestino,
                    'folio' => $row['serie'] . $row['folio']
                ]);
            } else {
                sendResponse(false, "Factura no encontrada");
            }
        } catch (Exception $e) {
            sendResponse(false, "Error: " . $e->getMessage());
        }
        break;

    default:
        sendResponse(false, "Operación no válida para facturación: " . $tipo);
        break;
}
?>
