<?php
class CodigoQR
{
    private $conn;

    public function __construct($db)
    {
        $this->conn = $db;
    }

    /**
     * Generar un lote de códigos QR
     */
    public function generarLote($cantidad = 48, $nombreLote = null, $usuarioId = null)
    {
        try {
            $this->conn->beginTransaction();

            // Crear el lote de impresión
            $nombreLote = $nombreLote ?: 'Lote ' . date('Y-m-d H:i:s');
            
            $queryLote = "INSERT INTO lotes_impresion (nombre, descripcion, cantidad_codigos, usuario_creacion)
                          VALUES (:nombre, :descripcion, :cantidad, :usuario_id)";
            
            $stmtLote = $this->conn->prepare($queryLote);
            $stmtLote->bindParam(':nombre', $nombreLote);
            $descripcion = "Lote de {$cantidad} códigos QR generados automáticamente";
            $stmtLote->bindParam(':descripcion', $descripcion);
            $stmtLote->bindParam(':cantidad', $cantidad);
            $stmtLote->bindParam(':usuario_id', $usuarioId);
            $stmtLote->execute();
            
            $loteId = $this->conn->lastInsertId();

            // Generar los códigos QR
            $codigosGenerados = [];
            
            for ($i = 1; $i <= $cantidad; $i++) {
                // Generar código único
                $codigo = $this->generarCodigoUnico();
                
                // El contenido del QR será el código
                $contenidoQR = $codigo;
                
                // Insertar código QR
                $queryQR = "INSERT INTO codigos_qr (codigo, codigo_qr, usuario_generacion)
                            VALUES (:codigo, :codigo_qr, :usuario_id)";
                
                $stmtQR = $this->conn->prepare($queryQR);
                $stmtQR->bindParam(':codigo', $codigo);
                $stmtQR->bindParam(':codigo_qr', $contenidoQR);
                $stmtQR->bindParam(':usuario_id', $usuarioId);
                $stmtQR->execute();
                
                $codigoQRId = $this->conn->lastInsertId();
                
                // Asociar código con lote
                $queryAsoc = "INSERT INTO lotes_codigos (lote_id, codigo_qr_id, posicion)
                              VALUES (:lote_id, :codigo_qr_id, :posicion)";
                
                $stmtAsoc = $this->conn->prepare($queryAsoc);
                $stmtAsoc->bindParam(':lote_id', $loteId);
                $stmtAsoc->bindParam(':codigo_qr_id', $codigoQRId);
                $stmtAsoc->bindParam(':posicion', $i);
                $stmtAsoc->execute();
                
                // Registrar en historial
                $this->registrarHistorial($codigoQRId, 'generado', 'Código QR generado en lote', null, null, $usuarioId);
                
                $codigosGenerados[] = [
                    'id' => $codigoQRId,
                    'codigo' => $codigo,
                    'contenido' => $contenidoQR,
                    'posicion' => $i
                ];
            }

            $this->conn->commit();

            return [
                'lote_id' => $loteId,
                'nombre_lote' => $nombreLote,
                'cantidad' => $cantidad,
                'codigos' => $codigosGenerados
            ];
            
        } catch (Exception $e) {
            $this->conn->rollBack();
            error_log("Error generando lote de QR: " . $e->getMessage());
            throw $e;
        }
    }

    /**
     * Obtener todos los lotes de impresión
     */
    public function obtenerLotes($filtros = [])
    {
        try {
            $where = ["1 = 1"];
            $params = [];

            if (!empty($filtros['estado'])) {
                $where[] = "l.estado = :estado";
                $params[':estado'] = $filtros['estado'];
            }

            if (!empty($filtros['fecha_desde'])) {
                $where[] = "DATE(l.fecha_creacion) >= :fecha_desde";
                $params[':fecha_desde'] = $filtros['fecha_desde'];
            }

            if (!empty($filtros['fecha_hasta'])) {
                $where[] = "DATE(l.fecha_creacion) <= :fecha_hasta";
                $params[':fecha_hasta'] = $filtros['fecha_hasta'];
            }

            $whereClause = implode(' AND ', $where);

            $query = "SELECT 
                        l.*,
                        u.nombre as usuario_creacion_nombre,
                        COUNT(lc.codigo_qr_id) as codigos_en_lote,
                        COUNT(CASE WHEN qr.estado = 'generado' THEN 1 END) as codigos_disponibles,
                        COUNT(CASE WHEN qr.estado = 'asignado' THEN 1 END) as codigos_asignados,
                        COUNT(CASE WHEN qr.estado = 'usado' THEN 1 END) as codigos_usados
                      FROM lotes_impresion l
                      LEFT JOIN usuarios u ON l.usuario_creacion = u.id
                      LEFT JOIN lotes_codigos lc ON l.id = lc.lote_id
                      LEFT JOIN codigos_qr qr ON lc.codigo_qr_id = qr.id
                      WHERE $whereClause
                      GROUP BY l.id
                      ORDER BY l.created_at DESC";

            $stmt = $this->conn->prepare($query);
            $stmt->execute($params);

            return $stmt->fetchAll(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            error_log("Error obteniendo lotes: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Obtener códigos QR de un lote específico
     */
    public function obtenerCodigosLote($loteId)
    {
        try {
            $query = "SELECT 
                        qr.*,
                        lc.posicion,
                        c.nombre_comercial as cliente_nombre
                      FROM codigos_qr qr
                      INNER JOIN lotes_codigos lc ON qr.id = lc.codigo_qr_id
                      LEFT JOIN clientes c ON qr.cliente_id = c.id
                      WHERE lc.lote_id = :lote_id
                      ORDER BY lc.posicion ASC";

            $stmt = $this->conn->prepare($query);
            $stmt->bindParam(':lote_id', $loteId);
            $stmt->execute();

            return $stmt->fetchAll(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            error_log("Error obteniendo códigos del lote: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Asignar código QR a cliente
     */
    public function asignarACliente($codigoQRId, $clienteId, $usuarioId = null)
    {
        try {
            $this->conn->beginTransaction();

            // Verificar que el código esté disponible
            $queryCheck = "SELECT * FROM codigos_qr WHERE id = :id AND estado = 'generado'";
            $stmtCheck = $this->conn->prepare($queryCheck);
            $stmtCheck->bindParam(':id', $codigoQRId);
            $stmtCheck->execute();
            
            $codigoQR = $stmtCheck->fetch(PDO::FETCH_ASSOC);
            if (!$codigoQR) {
                throw new Exception("Código QR no disponible para asignación");
            }

            // Actualizar código QR
            $queryUpdate = "UPDATE codigos_qr 
                           SET estado = 'asignado', 
                               cliente_id = :cliente_id, 
                               fecha_asignacion = CURRENT_TIMESTAMP,
                               usuario_asignacion = :usuario_id
                           WHERE id = :id";
            
            $stmtUpdate = $this->conn->prepare($queryUpdate);
            $stmtUpdate->bindParam(':cliente_id', $clienteId);
            $stmtUpdate->bindParam(':usuario_id', $usuarioId);
            $stmtUpdate->bindParam(':id', $codigoQRId);
            $stmtUpdate->execute();

            // Actualizar cliente
            $queryCliente = "UPDATE clientes SET codigo_qr_id = :codigo_qr_id WHERE id = :id";
            $stmtCliente = $this->conn->prepare($queryCliente);
            $stmtCliente->bindParam(':codigo_qr_id', $codigoQRId);
            $stmtCliente->bindParam(':id', $clienteId);
            $stmtCliente->execute();

            // Registrar en historial
            $this->registrarHistorial($codigoQRId, 'asignado', 'Código QR asignado a cliente', null, $clienteId, $usuarioId);

            $this->conn->commit();
            return true;
            
        } catch (Exception $e) {
            $this->conn->rollBack();
            error_log("Error asignando código QR: " . $e->getMessage());
            throw $e;
        }
    }

    /**
     * Obtener código QR por ID
     */
    public function obtenerPorId($codigoQRId)
    {
        try {
            $query = "SELECT 
                        qr.*,
                        c.nombre_comercial as cliente_nombre,
                        c.codigo as cliente_codigo,
                        l.nombre as lote_nombre
                      FROM codigos_qr qr
                      LEFT JOIN clientes c ON qr.cliente_id = c.id
                      LEFT JOIN lotes_codigos lc ON qr.id = lc.codigo_qr_id
                      LEFT JOIN lotes_impresion l ON lc.lote_id = l.id
                      WHERE qr.id = :id";
            
            $stmt = $this->conn->prepare($query);
            $stmt->bindParam(':id', $codigoQRId);
            $stmt->execute();

            return $stmt->fetch(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            error_log("Error obteniendo código QR por ID: " . $e->getMessage());
            return null;
        }
    }

    /**
     * Obtener códigos QR disponibles
     */
    public function obtenerDisponibles($limite = 100)
    {
        try {
            $query = "SELECT * FROM codigos_qr 
                      WHERE estado = 'generado' 
                      ORDER BY fecha_generacion ASC 
                      LIMIT :limite";
            
            $stmt = $this->conn->prepare($query);
            $stmt->bindParam(':limite', $limite, PDO::PARAM_INT);
            $stmt->execute();

            return $stmt->fetchAll(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            error_log("Error obteniendo códigos disponibles: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Obtener estadísticas de códigos QR
     */
    public function obtenerEstadisticas()
    {
        try {
            $stats = [];

            // Total por estado
            $query = "SELECT 
                        estado,
                        COUNT(*) as cantidad
                      FROM codigos_qr 
                      GROUP BY estado";
            
            $stmt = $this->conn->prepare($query);
            $stmt->execute();
            $resultados = $stmt->fetchAll(PDO::FETCH_ASSOC);
            
            foreach ($resultados as $row) {
                $stats[$row['estado']] = $row['cantidad'];
            }

            // Códigos generados por mes (últimos 6 meses)
            $query = "SELECT 
                        DATE_FORMAT(fecha_generacion, '%Y-%m') as mes,
                        COUNT(*) as cantidad
                      FROM codigos_qr 
                      WHERE fecha_generacion >= DATE_SUB(NOW(), INTERVAL 6 MONTH)
                      GROUP BY DATE_FORMAT(fecha_generacion, '%Y-%m')
                      ORDER BY mes DESC";
            
            $stmt = $this->conn->prepare($query);
            $stmt->execute();
            $stats['por_mes'] = $stmt->fetchAll(PDO::FETCH_ASSOC);

            // Lotes por estado
            $query = "SELECT 
                        estado,
                        COUNT(*) as cantidad
                      FROM lotes_impresion 
                      GROUP BY estado";
            
            $stmt = $this->conn->prepare($query);
            $stmt->execute();
            $lotes = $stmt->fetchAll(PDO::FETCH_ASSOC);
            
            $stats['lotes'] = [];
            foreach ($lotes as $row) {
                $stats['lotes'][$row['estado']] = $row['cantidad'];
            }

            return $stats;
        } catch (Exception $e) {
            error_log("Error obteniendo estadísticas: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Desasignar código QR de un cliente
     */
    public function desasignarDeCliente($clienteId, $usuarioId = null)
    {
        try {
            $this->conn->beginTransaction();

            // Primero obtener el código QR asignado al cliente
            $queryGetQR = "SELECT codigo_qr_id FROM clientes WHERE id = :cliente_id";
            $stmtGetQR = $this->conn->prepare($queryGetQR);
            $stmtGetQR->bindParam(':cliente_id', $clienteId);
            $stmtGetQR->execute();
            $clienteData = $stmtGetQR->fetch(PDO::FETCH_ASSOC);

            if (!$clienteData || !$clienteData['codigo_qr_id']) {
                $this->conn->rollBack();
                throw new Exception("El cliente no tiene un código QR asignado");
            }

            $codigoQRId = $clienteData['codigo_qr_id'];

            // Actualizar el código QR para marcarlo como usado (no reutilizable)
            $queryUpdate = "UPDATE codigos_qr 
                           SET estado = 'usado', 
                               cliente_id = NULL, 
                               fecha_uso = CURRENT_TIMESTAMP
                           WHERE id = :id";
            
            $stmtUpdate = $this->conn->prepare($queryUpdate);
            $stmtUpdate->bindParam(':id', $codigoQRId);
            $stmtUpdate->execute();

            // Actualizar cliente para eliminar la referencia del QR
            $queryCliente = "UPDATE clientes SET codigo_qr_id = NULL WHERE id = :id";
            $stmtCliente = $this->conn->prepare($queryCliente);
            $stmtCliente->bindParam(':id', $clienteId);
            $stmtCliente->execute();

            // Registrar en historial
            $this->registrarHistorial($codigoQRId, 'usado', 'Código QR desasignado y marcado como usado', $clienteId, null, $usuarioId);

            $this->conn->commit();
            return true;
            
        } catch (Exception $e) {
            $this->conn->rollBack();
            error_log("Error desasignando código QR: " . $e->getMessage());
            throw $e;
        }
    }

    /**
     * Generar código único
     */
    private function generarCodigoUnico()
    {
        $prefijo = 'QR';
        $timestamp = time();
        $random = str_pad(mt_rand(1, 9999), 4, '0', STR_PAD_LEFT);
        
        return $prefijo . $timestamp . $random;
    }

    /**
     * Registrar historial
     */
    private function registrarHistorial($codigoQRId, $accion, $descripcion, $clienteAnteriorId = null, $clienteNuevoId = null, $usuarioId = null)
    {
        try {
            $query = "INSERT INTO historial_qr (codigo_qr_id, accion, descripcion, cliente_anterior_id, cliente_nuevo_id, usuario_id)
                      VALUES (:codigo_qr_id, :accion, :descripcion, :cliente_anterior_id, :cliente_nuevo_id, :usuario_id)";

            $stmt = $this->conn->prepare($query);
            $stmt->bindParam(':codigo_qr_id', $codigoQRId);
            $stmt->bindParam(':accion', $accion);
            $stmt->bindParam(':descripcion', $descripcion);
            $stmt->bindParam(':cliente_anterior_id', $clienteAnteriorId);
            $stmt->bindParam(':cliente_nuevo_id', $clienteNuevoId);
            $stmt->bindParam(':usuario_id', $usuarioId);

            return $stmt->execute();
        } catch (Exception $e) {
            error_log("Error registrando historial QR: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Marcar todos los códigos de un lote como usados
     */
    public function marcarLoteComoUsado($loteId)
    {
        try {
            $this->conn->beginTransaction();

            // Obtener todos los códigos QR del lote que no estén ya usados
            $query = "SELECT qr.id, qr.codigo, qr.estado 
                      FROM codigos_qr qr
                      INNER JOIN lotes_codigos lc ON qr.id = lc.codigo_qr_id
                      WHERE lc.lote_id = :lote_id 
                      AND qr.estado IN ('generado', 'asignado')";
            
            $stmt = $this->conn->prepare($query);
            $stmt->bindParam(':lote_id', $loteId);
            $stmt->execute();
            $codigos = $stmt->fetchAll(PDO::FETCH_ASSOC);

            if (empty($codigos)) {
                $this->conn->rollBack();
                throw new Exception("No hay códigos en este lote disponibles para marcar como usados");
            }

            // Actualizar códigos QR a estado 'usado'
            $queryUpdate = "UPDATE codigos_qr 
                           SET estado = 'usado', 
                               fecha_uso = CURRENT_TIMESTAMP,
                               cliente_id = NULL
                           WHERE id IN (" . 
                           str_repeat('?,', count($codigos) - 1) . '?' . 
                           ")";

            $codigoIds = array_column($codigos, 'id');
            $stmtUpdate = $this->conn->prepare($queryUpdate);
            $stmtUpdate->execute($codigoIds);

            // Si hay clientes asignados, limpiar sus referencias
            $queryClientes = "UPDATE clientes 
                             SET codigo_qr_id = NULL 
                             WHERE codigo_qr_id IN (" . 
                             str_repeat('?,', count($codigos) - 1) . '?' . 
                             ")";
            
            $stmtClientes = $this->conn->prepare($queryClientes);
            $stmtClientes->execute($codigoIds);

            // Registrar en historial para cada código
            foreach ($codigos as $codigo) {
                $descripcion = "Código QR marcado como usado desde lote (ID: $loteId)";
                $this->registrarHistorial($codigo['id'], 'usado', $descripcion, null, null, null);
            }

            // Actualizar estado del lote
            $queryLote = "UPDATE lotes_impresion 
                         SET estado = 'usado',
                             fecha_impresion = COALESCE(fecha_impresion, CURRENT_TIMESTAMP)
                         WHERE id = :id";
            
            $stmtLote = $this->conn->prepare($queryLote);
            $stmtLote->bindParam(':id', $loteId);
            $stmtLote->execute();

            $this->conn->commit();
            return count($codigos); // Retornar cantidad de códigos marcados
            
        } catch (Exception $e) {
            $this->conn->rollBack();
            error_log("Error marcando lote como usado: " . $e->getMessage());
            throw $e;
        }
    }

    /**
     * Marcar un código QR individual como usado
     */
    public function marcarCodigoComoUsado($codigoId)
    {
        try {
            $this->conn->beginTransaction();

            // Verificar que el código existe y no esté ya usado
            $query = "SELECT id, codigo, estado, cliente_id FROM codigos_qr WHERE id = :id";
            $stmt = $this->conn->prepare($query);
            $stmt->bindParam(':id', $codigoId);
            $stmt->execute();
            $codigo = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$codigo) {
                $this->conn->rollBack();
                throw new Exception("Código QR no encontrado");
            }

            if ($codigo['estado'] === 'usado') {
                $this->conn->rollBack();
                throw new Exception("El código QR ya está marcado como usado");
            }

            // Si el código está asignado a un cliente, desasignarlo
            if ($codigo['cliente_id']) {
                $queryCliente = "UPDATE clientes SET codigo_qr_id = NULL WHERE codigo_qr_id = :codigo_id";
                $stmtCliente = $this->conn->prepare($queryCliente);
                $stmtCliente->bindParam(':codigo_id', $codigoId);
                $stmtCliente->execute();
            }

            // Marcar el código como usado
            $queryUpdate = "UPDATE codigos_qr 
                           SET estado = 'usado', 
                               cliente_id = NULL,
                               fecha_uso = CURRENT_TIMESTAMP
                           WHERE id = :id";
            
            $stmtUpdate = $this->conn->prepare($queryUpdate);
            $stmtUpdate->bindParam(':id', $codigoId);
            $stmtUpdate->execute();

            // Registrar en historial
            $descripcion = "Código QR marcado como usado manualmente";
            $clienteAnterior = $codigo['cliente_id'] ?? null;
            $this->registrarHistorial($codigoId, 'usado', $descripcion, $clienteAnterior, null, null);

            $this->conn->commit();
            return true;
            
        } catch (Exception $e) {
            $this->conn->rollBack();
            error_log("Error marcando código como usado: " . $e->getMessage());
            throw $e;
        }
    }

    /**
     * Marcar lote como impreso
     */
    public function marcarLoteImpreso($loteId)
    {
        try {
            $query = "UPDATE lotes_impresion
                      SET estado = 'impreso', fecha_impresion = CURRENT_TIMESTAMP
                      WHERE id = :id";

            $stmt = $this->conn->prepare($query);
            $stmt->bindParam(':id', $loteId);

            return $stmt->execute();
        } catch (Exception $e) {
            error_log("Error marcando lote como impreso: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Eliminar lote de códigos QR
     * Solo permite eliminar si no hay códigos asignados a clientes
     */
    public function eliminarLote($loteId)
    {
        try {
            $this->conn->beginTransaction();

            // Verificar si hay códigos asignados en este lote
            $queryCheck = "SELECT COUNT(*) as total_asignados
                          FROM codigos_qr cq
                          INNER JOIN lotes_codigos lc ON cq.id = lc.codigo_qr_id
                          WHERE lc.lote_id = :lote_id
                          AND cq.cliente_id IS NOT NULL";

            $stmtCheck = $this->conn->prepare($queryCheck);
            $stmtCheck->bindParam(':lote_id', $loteId);
            $stmtCheck->execute();
            $result = $stmtCheck->fetch(PDO::FETCH_ASSOC);

            if ($result['total_asignados'] > 0) {
                $this->conn->rollBack();
                throw new Exception("No se puede eliminar el lote porque tiene " . $result['total_asignados'] . " código(s) QR asignado(s) a clientes");
            }

            // Obtener IDs de códigos QR del lote
            $queryGetCodigos = "SELECT codigo_qr_id FROM lotes_codigos WHERE lote_id = :lote_id";
            $stmtGetCodigos = $this->conn->prepare($queryGetCodigos);
            $stmtGetCodigos->bindParam(':lote_id', $loteId);
            $stmtGetCodigos->execute();
            $codigosIds = $stmtGetCodigos->fetchAll(PDO::FETCH_COLUMN);

            // Eliminar relaciones en lotes_codigos
            $queryDeleteRelaciones = "DELETE FROM lotes_codigos WHERE lote_id = :lote_id";
            $stmtDeleteRelaciones = $this->conn->prepare($queryDeleteRelaciones);
            $stmtDeleteRelaciones->bindParam(':lote_id', $loteId);
            $stmtDeleteRelaciones->execute();

            // Eliminar códigos QR si hay alguno
            if (!empty($codigosIds)) {
                $placeholders = implode(',', array_fill(0, count($codigosIds), '?'));
                $queryDeleteCodigos = "DELETE FROM codigos_qr WHERE id IN ($placeholders)";
                $stmtDeleteCodigos = $this->conn->prepare($queryDeleteCodigos);
                $stmtDeleteCodigos->execute($codigosIds);
            }

            // Eliminar lote
            $queryDeleteLote = "DELETE FROM lotes_impresion WHERE id = :id";
            $stmtDeleteLote = $this->conn->prepare($queryDeleteLote);
            $stmtDeleteLote->bindParam(':id', $loteId);
            $stmtDeleteLote->execute();

            $this->conn->commit();
            return true;
        } catch (Exception $e) {
            $this->conn->rollBack();
            error_log("Error eliminando lote: " . $e->getMessage());
            throw $e;
        }
    }
}