<?php

/**
 * Rutas.php - Manejo de rutas
 */

// Incluir modelos necesarios
require_once __DIR__ . '/../../modelos/Ruta.php';
require_once __DIR__ . '/../../modelos/Usuario.php';
require_once __DIR__ . '/../../modelos/Auth.php';
require_once __DIR__ . '/../../modelos/Repartidor.php';

// Función para enviar respuesta JSON (si no está definida)
if (!function_exists('sendResponse')) {
    function sendResponse($success, $message = '', $data = null)
    {
        echo json_encode([
            "success" => $success,
            "message" => $message,
            "data" => $data
        ]);
        exit;
    }
}

// Función para validar token (si no está definida)
if (!function_exists('validateToken')) {
    function validateToken($conn, $authHeader)
    {
        error_log("Validando token en Rutas.php...");

        if (empty($authHeader)) {
            error_log("Header Authorization vacío");
            return null;
        }

        $token = str_replace('Bearer ', '', $authHeader);

        // Usar el modelo Auth para verificar el JWT
        $auth = new Auth($conn);
        $userData = $auth->obtenerUsuarioDeToken($token);

        if ($userData) {
            error_log("Usuario validado: " . $userData['username']);
            return $userData;
        }

        return null;
    }
}

// Instanciar modelos
$usuario = new Usuario($conn);
$ruta = new Ruta($conn);
$repartidorModel = new Repartidor($conn);

// Operaciones que NO requieren autenticación de usuario web (para app móvil)
$operacionesAppMovil = ['getRutasDisponiblesRepartidor', 'getRutaDelDia', 'registrarVisitaCliente'];

// Verificar autenticación para operaciones web
if (!in_array($tipo, $operacionesAppMovil)) {
    $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
    if (empty($authHeader)) {
        $authHeader = getallheaders()['Authorization'] ?? '';
    }

    // Validar token
    $user = validateToken($conn, $authHeader);
    if (!$user) {
        sendResponse(false, "No autorizado " . json_encode($authHeader) . " " . json_encode($user));
    }
}

// === OBTENER TODAS LAS RUTAS ===
if ($tipo === 'getRutas') {
    error_log("=== OBTENIENDO RUTAS ===");

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'leer')) {
        sendResponse(false, "Sin permisos para ver rutas");
    }

    try {
        $filtros = [];
        if (isset($datapost['search'])) $filtros['search'] = $datapost['search'];
        if (isset($datapost['estado'])) $filtros['estado'] = $datapost['estado'];
        if (isset($datapost['fecha_inicio'])) $filtros['fecha_inicio'] = $datapost['fecha_inicio'];
        if (isset($datapost['fecha_fin'])) $filtros['fecha_fin'] = $datapost['fecha_fin'];
        if (isset($datapost['repartidor_id'])) $filtros['repartidor_id'] = $datapost['repartidor_id'];

        $rutas = $ruta->obtenerTodos($filtros);
        sendResponse(true, "Rutas obtenidas", $rutas);
    } catch (Exception $e) {
        error_log("Error obteniendo rutas: " . $e->getMessage());
        sendResponse(false, "Error al obtener rutas");
    }
}

// === OBTENER UNA RUTA ===
if ($tipo === 'getRuta') {
    error_log("=== OBTENIENDO RUTA ===");

    $id = isset($datapost['id']) ? $datapost['id'] : null;

    if (!$id) {
        sendResponse(false, "ID de ruta requerido");
    }

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'leer')) {
        sendResponse(false, "Sin permisos para ver rutas");
    }

    try {
        $rutaData = $ruta->obtenerPorId($id);
        if ($rutaData) {
            sendResponse(true, "Ruta obtenida", $rutaData);
        } else {
            sendResponse(false, "Ruta no encontrada");
        }
    } catch (Exception $e) {
        error_log("Error obteniendo ruta: " . $e->getMessage());
        sendResponse(false, "Error al obtener ruta");
    }
}

// === CREAR RUTA ===
if ($tipo === 'createRuta') {
    error_log("=== CREANDO RUTA ===");

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'crear')) {
        sendResponse(false, "Sin permisos para crear rutas");
    }

    // Validar datos requeridos
    $requiredFields = ['nombre', 'fecha_ruta'];
    foreach ($requiredFields as $field) {
        if (empty($datapost[$field])) {
            sendResponse(false, "Campo requerido: $field");
        }
    }

    try {
        // Generar código automático si no se proporciona
        $codigo = !empty($datapost['codigo']) ? $datapost['codigo'] : $ruta->generarCodigoAutomatico();

        $datos = [
            'codigo' => $codigo,
            'nombre' => $datapost['nombre'],
            'descripcion' => $datapost['descripcion'] ?? null,
            'fecha_ruta' => $datapost['fecha_ruta'],
            'hora_inicio' => $datapost['hora_inicio'] ?? null,
            'hora_fin' => $datapost['hora_fin'] ?? null,
            'estado' => $datapost['estado'] ?? 'planificada',
            'repartidor_id' => $datapost['repartidor_id'] ?? null,
            'vehiculo_id' => $datapost['vehiculo_id'] ?? null,
            'notas' => $datapost['notas'] ?? null,
            'created_by' => $user['sub'],
            'distancia_total' => $datapost['distancia_total'] ?? null,
            'tiempo_estimado' => $datapost['tiempo_estimado'] ?? null,
            'ruta_polyline' => $datapost['ruta_polyline'] ?? null,
            'waypoints_optimizados' => $datapost['waypoints_optimizados'] ?? null,
            'bounds_ne_lat' => $datapost['bounds_ne_lat'] ?? null,
            'bounds_ne_lng' => $datapost['bounds_ne_lng'] ?? null,
            'bounds_sw_lat' => $datapost['bounds_sw_lat'] ?? null,
            'bounds_sw_lng' => $datapost['bounds_sw_lng'] ?? null,
            'instrucciones_json' => $datapost['instrucciones_json'] ?? null,
        ];

        $rutaId = $ruta->crear($datos);

        if ($rutaId) {
            // Si se proporcionan paradas, agregarlas
            if (!empty($datapost['paradas']) && is_array($datapost['paradas'])) {
                foreach ($datapost['paradas'] as $index => $parada) {
                    if (!empty($parada['cliente_id'])) {
                        $ruta->agregarParada($rutaId, $parada['cliente_id'], $index + 1, $parada);
                    }
                }
            }

            sendResponse(true, "Ruta creada exitosamente", ['id' => $rutaId, 'codigo' => $codigo]);
        } else {
            sendResponse(false, "Error al crear ruta");
        }
    } catch (Exception $e) {
        error_log("Error creando ruta: " . $e->getMessage());
        sendResponse(false, $e->getMessage());
    }
}

// === ACTUALIZAR RUTA ===
if ($tipo === 'updateRuta') {
    error_log("=== ACTUALIZANDO RUTA ===");

    $id = isset($datapost['id']) ? $datapost['id'] : null;

    if (!$id) {
        sendResponse(false, "ID de ruta requerido");
    }

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'actualizar')) {
        sendResponse(false, "Sin permisos para actualizar rutas");
    }

    try {
        $datos = [];
        $allowedFields = [
            'codigo',
            'nombre',
            'descripcion',
            'fecha_ruta',
            'hora_inicio',
            'hora_fin',
            'estado',
            'repartidor_id',
            'vehiculo_id',
            'notas',
            'distancia_total',
            'tiempo_estimado',
            'ruta_polyline',
            'waypoints_optimizados',
            'bounds_ne_lat',
            'bounds_ne_lng',
            'bounds_sw_lat',
            'bounds_sw_lng',
            'instrucciones_json'
        ];

        foreach ($allowedFields as $field) {
            if (isset($datapost[$field])) {
                $datos[$field] = $datapost[$field];
            }
        }

        if (empty($datos)) {
            sendResponse(false, "No hay datos para actualizar");
        }

        $datos['usuario_id'] = $user['sub'];

        // Actualizar ruta principal
        $rutaActualizada = $ruta->actualizar($id, $datos);

        if (!$rutaActualizada) {
            sendResponse(false, "Error al actualizar ruta");
        }

        // Manejar paradas si están presentes
        if (isset($datapost['paradas']) && is_array($datapost['paradas'])) {
            try {
                // Eliminar paradas existentes
                $stmt = $conn->prepare("DELETE FROM rutas_paradas WHERE ruta_id = :ruta_id");
                $stmt->bindParam(':ruta_id', $id);
                $stmt->execute();

                // Agregar nuevas paradas
                foreach ($datapost['paradas'] as $parada) {
                    if (isset($parada['cliente_id']) && isset($parada['orden'])) {
                        $ruta->agregarParada($id, $parada['cliente_id'], $parada['orden'], $parada);
                    }
                }
            } catch (Exception $e) {
                error_log("Error actualizando paradas: " . $e->getMessage());
                sendResponse(false, "Error actualizando paradas: " . $e->getMessage());
            }
        }

        sendResponse(true, "Ruta actualizada exitosamente");
    } catch (Exception $e) {
        error_log("Error actualizando ruta: " . $e->getMessage());
        sendResponse(false, $e->getMessage());
    }
}

// === ELIMINAR RUTA ===
if ($tipo === 'deleteRuta') {
    error_log("=== ELIMINANDO RUTA ===");

    $id = isset($datapost['id']) ? $datapost['id'] : null;

    if (!$id) {
        sendResponse(false, "ID de ruta requerido");
    }

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'eliminar')) {
        sendResponse(false, "Sin permisos para eliminar rutas");
    }

    try {
        if ($ruta->eliminar($id)) {
            sendResponse(true, "Ruta eliminada exitosamente");
        } else {
            sendResponse(false, "Error al eliminar ruta");
        }
    } catch (Exception $e) {
        error_log("Error eliminando ruta: " . $e->getMessage());
        sendResponse(false, "Error al eliminar ruta");
    }
}

// === CLONAR RUTA ===
if ($tipo === 'clonarRuta') {
    error_log("=== CLONANDO RUTA ===");

    $id = isset($datapost['id']) ? $datapost['id'] : null;
    $nuevoNombre = isset($datapost['nuevo_nombre']) ? trim($datapost['nuevo_nombre']) : null;

    if (!$id) {
        sendResponse(false, "ID de ruta requerido");
    }

    if (!$nuevoNombre) {
        sendResponse(false, "Nombre de la nueva ruta requerido");
    }

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'crear')) {
        sendResponse(false, "Sin permisos para crear rutas");
    }

    try {
        // Obtener la ruta original con sus paradas
        $rutaOriginal = $ruta->obtenerPorId($id);

        if (!$rutaOriginal) {
            sendResponse(false, "Ruta no encontrada");
        }

        // Generar nuevo código
        $nuevoCodigo = $ruta->generarCodigoAutomatico();

        // Crear la nueva ruta
        $datosNuevaRuta = [
            'codigo' => $nuevoCodigo,
            'nombre' => $nuevoNombre,
            'descripcion' => $rutaOriginal['descripcion'],
            'fecha_ruta' => date('Y-m-d'), // Fecha actual
            'hora_inicio' => $rutaOriginal['hora_inicio'],
            'hora_fin' => $rutaOriginal['hora_fin'],
            'estado' => 'planificada',
            'repartidor_id' => $rutaOriginal['repartidor_id'],
            'vehiculo_id' => $rutaOriginal['vehiculo_id'],
            'notas' => $rutaOriginal['notas'],
            'created_by' => $user['sub'],
            'distancia_total' => $rutaOriginal['distancia_total'],
            'tiempo_estimado' => $rutaOriginal['tiempo_estimado'],
            'ruta_polyline' => $rutaOriginal['ruta_polyline'],
            'waypoints_optimizados' => $rutaOriginal['waypoints_optimizados'],
            'bounds_ne_lat' => $rutaOriginal['bounds_ne_lat'],
            'bounds_ne_lng' => $rutaOriginal['bounds_ne_lng'],
            'bounds_sw_lat' => $rutaOriginal['bounds_sw_lat'],
            'bounds_sw_lng' => $rutaOriginal['bounds_sw_lng'],
            'instrucciones_json' => $rutaOriginal['instrucciones_json']
        ];

        $nuevaRutaId = $ruta->crear($datosNuevaRuta);

        if (!$nuevaRutaId) {
            sendResponse(false, "Error al crear la nueva ruta");
        }

        // Clonar las paradas
        if (!empty($rutaOriginal['paradas']) && is_array($rutaOriginal['paradas'])) {
            foreach ($rutaOriginal['paradas'] as $parada) {
                $datosParada = [
                    'direccion_entrega' => $parada['direccion_entrega'] ?? null,
                    'notas' => $parada['notas'] ?? null,
                    'frecuencia_dias' => $parada['frecuencia_dias'] ?? null,
                    'dias_semana' => $parada['dias_semana'] ?? null,
                    'hora_preferida' => $parada['hora_preferida'] ?? null
                ];
                $ruta->agregarParada($nuevaRutaId, $parada['cliente_id'], $parada['orden'], $datosParada);
            }
        }

        sendResponse(true, "Ruta clonada exitosamente", [
            'id' => $nuevaRutaId,
            'codigo' => $nuevoCodigo,
            'nombre' => $nuevoNombre
        ]);

    } catch (Exception $e) {
        error_log("Error clonando ruta: " . $e->getMessage());
        sendResponse(false, "Error al clonar ruta: " . $e->getMessage());
    }
}

// === ASIGNAR VEHÍCULO ===
if ($tipo === 'asignarVehiculo') {
    error_log("=== ASIGNANDO VEHÍCULO A RUTA ===");

    $rutaId = isset($datapost['ruta_id']) ? $datapost['ruta_id'] : null;
    $vehiculoId = isset($datapost['vehiculo_id']) ? $datapost['vehiculo_id'] : null;

    if (!$rutaId || !$vehiculoId) {
        sendResponse(false, "ID de ruta y vehículo requeridos");
    }

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'actualizar')) {
        sendResponse(false, "Sin permisos para asignar vehículos");
    }

    try {
        $datos = ['vehiculo_id' => $vehiculoId, 'usuario_id' => $user['sub']];
        if ($ruta->actualizar($rutaId, $datos)) {
            sendResponse(true, "Vehículo asignado exitosamente");
        } else {
            sendResponse(false, "Error al asignar vehículo");
        }
    } catch (Exception $e) {
        error_log("Error asignando vehículo: " . $e->getMessage());
        sendResponse(false, "Error al asignar vehículo");
    }
}

// === ASIGNAR CONDUCTOR ===
if ($tipo === 'asignarConductor') {
    error_log("=== ASIGNANDO CONDUCTOR A RUTA ===");

    $rutaId = isset($datapost['ruta_id']) ? $datapost['ruta_id'] : null;
    $repartidorId = isset($datapost['repartidor_id']) ? $datapost['repartidor_id'] : null;

    if (!$rutaId || !$repartidorId) {
        sendResponse(false, "ID de ruta y repartidor requeridos");
    }

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'actualizar')) {
        sendResponse(false, "Sin permisos para asignar conductores");
    }

    try {
        $datos = ['repartidor_id' => $repartidorId, 'usuario_id' => $user['sub']];
        if ($ruta->actualizar($rutaId, $datos)) {
            sendResponse(true, "Conductor asignado exitosamente");
        } else {
            sendResponse(false, "Error al asignar conductor");
        }
    } catch (Exception $e) {
        error_log("Error asignando conductor: " . $e->getMessage());
        sendResponse(false, "Error al asignar conductor");
    }
}

// === OBTENER ESTADÍSTICAS ===
if ($tipo === 'getEstadisticasRutas') {
    error_log("=== OBTENIENDO ESTADÍSTICAS DE RUTAS ===");

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'leer')) {
        sendResponse(false, "Sin permisos para ver estadísticas");
    }

    try {
        $estadisticas = $ruta->obtenerEstadisticas();
        sendResponse(true, "Estadísticas obtenidas", $estadisticas);
    } catch (Exception $e) {
        error_log("Error obteniendo estadísticas: " . $e->getMessage());
        sendResponse(false, "Error al obtener estadísticas");
    }
}

// === OBTENER REPARTIDORES DISPONIBLES ===
if ($tipo === 'getRepartidoresDisponibles') {
    error_log("=== OBTENIENDO REPARTIDORES DISPONIBLES ===");

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'leer')) {
        sendResponse(false, "Sin permisos para ver repartidores");
    }

    try {
        $fecha = $datapost['fecha'] ?? date('Y-m-d');
        $repartidores = $ruta->obtenerRepartidoresDisponibles($fecha);
        sendResponse(true, "Repartidores disponibles obtenidos", $repartidores);
    } catch (Exception $e) {
        error_log("Error obteniendo repartidores disponibles: " . $e->getMessage());
        sendResponse(false, "Error al obtener repartidores disponibles");
    }
}

// === OBTENER VEHÍCULOS DISPONIBLES ===
if ($tipo === 'getVehiculosDisponibles') {
    error_log("=== OBTENIENDO VEHÍCULOS DISPONIBLES ===");

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'leer')) {
        sendResponse(false, "Sin permisos para ver vehículos");
    }

    try {
        $fecha = $datapost['fecha'] ?? date('Y-m-d');
        $vehiculos = $ruta->obtenerVehiculosDisponibles($fecha);
        sendResponse(true, "Vehículos disponibles obtenidos", $vehiculos);
    } catch (Exception $e) {
        error_log("Error obteniendo vehículos disponibles: " . $e->getMessage());
        sendResponse(false, "Error al obtener vehículos disponibles");
    }
}

// === CAMBIAR ESTADO DE RUTA ===
if ($tipo === 'cambiarEstadoRuta') {
    error_log("=== CAMBIANDO ESTADO DE RUTA ===");

    $rutaId = isset($datapost['ruta_id']) ? $datapost['ruta_id'] : null;
    $nuevoEstado = isset($datapost['estado']) ? $datapost['estado'] : null;

    if (!$rutaId || !$nuevoEstado) {
        sendResponse(false, "ID de ruta y nuevo estado requeridos");
    }

    // Validar estados permitidos
    $estadosPermitidos = ['planificada', 'en_progreso', 'completada', 'cancelada'];
    if (!in_array($nuevoEstado, $estadosPermitidos)) {
        sendResponse(false, "Estado no válido");
    }

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'actualizar')) {
        sendResponse(false, "Sin permisos para cambiar estado de rutas");
    }

    try {
        if ($ruta->cambiarEstado($rutaId, $nuevoEstado, $user['sub'])) {
            sendResponse(true, "Estado de ruta cambiado exitosamente");
        } else {
            sendResponse(false, "Error al cambiar estado de ruta");
        }
    } catch (Exception $e) {
        error_log("Error cambiando estado de ruta: " . $e->getMessage());
        sendResponse(false, "Error al cambiar estado de ruta");
    }
}

// === GENERAR CÓDIGO AUTOMÁTICO ===
if ($tipo === 'generarCodigoRuta') {
    error_log("=== GENERANDO CÓDIGO AUTOMÁTICO ===");

    // Verificar permisos
    if (!$usuario->verificarPermiso($user['sub'], 'rutas', 'crear')) {
        sendResponse(false, "Sin permisos para generar código");
    }

    try {
        $codigo = $ruta->generarCodigoAutomatico();
        sendResponse(true, "Código generado", ['codigo' => $codigo]);
    } catch (Exception $e) {
        error_log("Error generando código: " . $e->getMessage());
        sendResponse(false, "Error al generar código");
    }
}

// === OBTENER RUTAS DISPONIBLES PARA REPARTIDOR (APP MÓVIL) ===
if ($tipo === 'getRutasDisponiblesRepartidor') {
    error_log("=== OBTENIENDO RUTAS DISPONIBLES PARA REPARTIDOR ===");

    // Obtener token del repartidor
    $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';

    if (empty($authHeader)) {
        $headers = getallheaders();
        $authHeader = $headers['Authorization'] ?? '';
    }

    if (empty($authHeader) && function_exists('apache_request_headers')) {
        $headers = apache_request_headers();
        $authHeader = $headers['Authorization'] ?? '';
    }

    if (empty($authHeader) && isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
        $authHeader = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
    }

    if (empty($authHeader)) {
        error_log("ERROR: No se encontró token de autenticación");
        sendResponse(false, "Token de autenticación requerido");
    }

    $token = str_replace('Bearer ', '', $authHeader);

    // Verificar token y obtener repartidor_id
    $auth = new Auth($conn);
    $payload = $auth->verificarJWT($token);

    if (!$payload) {
        error_log("ERROR: Token inválido o expirado");
        sendResponse(false, "Token inválido o expirado");
    }

    // Extraer repartidor_id del payload
    $repartidorId = $payload['repartidor_id'] ?? null;

    if (!$repartidorId) {
        error_log("ERROR: Token no contiene repartidor_id");
        sendResponse(false, "Token no válido para repartidor");
    }

    try {
        // Obtener todas las rutas activas del repartidor
        $query = "SELECT r.id,
                         r.nombre,
                         r.descripcion,
                         r.estado,
                         r.fecha_ruta,
                         r.activa,
                         (SELECT COUNT(*) FROM rutas_paradas WHERE ruta_id = r.id) as total_paradas
                  FROM rutas r
                  WHERE r.repartidor_id = :repartidor_id
                  AND r.activa = 1
                  AND r.estado IN ('planificada', 'en_progreso')
                  ORDER BY
                    CASE WHEN r.estado = 'en_progreso' THEN 0 ELSE 1 END,
                    r.created_at DESC";

        $stmt = $conn->prepare($query);
        $stmt->bindParam(':repartidor_id', $repartidorId);
        $stmt->execute();

        $rutas = $stmt->fetchAll(PDO::FETCH_ASSOC);

        error_log("Rutas disponibles encontradas: " . count($rutas));

        sendResponse(true, "Rutas disponibles obtenidas", $rutas);

    } catch (Exception $e) {
        error_log("Error obteniendo rutas disponibles: " . $e->getMessage());
        sendResponse(false, "Error al obtener rutas disponibles");
    }
}

// === OBTENER RUTA DEL DÍA PARA REPARTIDOR (APP MÓVIL) ===
if ($tipo === 'getRutaDelDia') {
    error_log("=== OBTENIENDO RUTA DEL DÍA PARA REPARTIDOR ===");

    // Obtener token del repartidor de múltiples fuentes posibles
    $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';

    if (empty($authHeader)) {
        $headers = getallheaders();
        $authHeader = $headers['Authorization'] ?? '';
    }

    if (empty($authHeader) && function_exists('apache_request_headers')) {
        $headers = apache_request_headers();
        $authHeader = $headers['Authorization'] ?? '';
    }

    if (empty($authHeader) && isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
        $authHeader = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
    }

    if (empty($authHeader)) {
        error_log("ERROR: No se encontró token de autenticación");
        sendResponse(false, "Token de autenticación requerido");
    }

    $token = str_replace('Bearer ', '', $authHeader);

    // Verificar token y obtener repartidor_id
    $auth = new Auth($conn);
    $payload = $auth->verificarJWT($token);

    if (!$payload) {
        error_log("ERROR: Token inválido o expirado");
        sendResponse(false, "Token inválido o expirado");
    }

    // Extraer repartidor_id del payload
    $repartidorId = $payload['repartidor_id'] ?? null;

    if (!$repartidorId) {
        error_log("ERROR: Token no contiene repartidor_id");
        sendResponse(false, "Token no válido para repartidor");
    }

    try {
        // Obtener fecha actual
        $fechaHoy = $datapost['fecha'] ?? date('Y-m-d');
        $diaSemanaHoy = strtoupper(date('D', strtotime($fechaHoy))); // LUN, MAR, MIE, etc.

        // Mapeo de días en inglés a español
        $diasMapeo = [
            'MON' => 'LUN',
            'TUE' => 'MAR',
            'WED' => 'MIE',
            'THU' => 'JUE',
            'FRI' => 'VIE',
            'SAT' => 'SAB',
            'SUN' => 'DOM'
        ];
        $diaSemanaHoy = $diasMapeo[$diaSemanaHoy] ?? $diaSemanaHoy;

        error_log("Fecha de consulta: $fechaHoy, Día de la semana: $diaSemanaHoy");

        // Verificar si se proporciona un ruta_id específico
        $rutaIdEspecifica = isset($datapost['ruta_id']) ? (int)$datapost['ruta_id'] : null;

        // Buscar ruta activa/recurrente asignada al repartidor
        if ($rutaIdEspecifica) {
            // Buscar ruta específica del repartidor
            error_log("Buscando ruta específica: ID=$rutaIdEspecifica");
            $query = "SELECT r.*
                      FROM rutas r
                      WHERE r.id = :ruta_id
                      AND r.repartidor_id = :repartidor_id
                      AND r.activa = 1
                      AND r.estado IN ('planificada', 'en_progreso')
                      LIMIT 1";

            $stmt = $conn->prepare($query);
            $stmt->bindParam(':ruta_id', $rutaIdEspecifica);
            $stmt->bindParam(':repartidor_id', $repartidorId);
            $stmt->execute();
        } else {
            // Buscar cualquier ruta activa (comportamiento por defecto)
            error_log("Buscando cualquier ruta activa del repartidor");
            $query = "SELECT r.*
                      FROM rutas r
                      WHERE r.repartidor_id = :repartidor_id
                      AND r.activa = 1
                      AND r.estado IN ('planificada', 'en_progreso')
                      ORDER BY
                        CASE WHEN r.estado = 'en_progreso' THEN 0 ELSE 1 END,
                        r.created_at DESC
                      LIMIT 1";

            $stmt = $conn->prepare($query);
            $stmt->bindParam(':repartidor_id', $repartidorId);
            $stmt->execute();
        }

        $rutaData = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$rutaData) {
            sendResponse(true, "No hay ruta asignada", null);
        }

        error_log("Ruta encontrada: ID=" . $rutaData['id'] . ", Nombre=" . $rutaData['nombre']);

        // Obtener TODAS las paradas de la ruta con información de clientes, frecuencias y visitas del día
        // AGRUPADAS por parada para evitar duplicados cuando hay múltiples visitas al mismo cliente
        $queryParadas = "SELECT
                            rp.*,
                            c.codigo as cliente_codigo,
                            c.nombre_comercial as cliente_nombre,
                            COALESCE(rp.direccion_entrega, c.direccion) as cliente_direccion,
                            c.telefono as cliente_telefono,
                            c.telefono_secundario,
                            c.latitud,
                            c.longitud,
                            c.nombre as persona_contacto,
                            c.apellidos,
                            qr.codigo as codigo_qr,
                            -- Tomar la última visita del día para determinar el estado
                            CASE
                                WHEN MAX(CASE WHEN vp.estado = 'en_curso' THEN 1 ELSE 0 END) = 1 THEN 'en_curso'
                                WHEN MAX(CASE WHEN vp.estado = 'completada' THEN 1 ELSE 0 END) = 1 THEN 'visitado'
                                ELSE 'pendiente'
                            END as estado,
                            -- Última visita del día
                            MAX(vp.id) as visita_id,
                            MAX(vp.fecha_hora_entrada) as visita_entrada,
                            MAX(vp.fecha_hora_salida) as visita_salida,
                            -- Total de visitas en el día (contar solo IDs distintos de NULL)
                            COUNT(DISTINCT CASE WHEN vp.id IS NOT NULL THEN vp.id ELSE NULL END) as total_visitas_hoy,
                            -- Última visita histórica (cualquier día) para calcular frecuencia
                            (SELECT MAX(DATE(vph.fecha_hora_entrada))
                             FROM visitas_paradas vph
                             WHERE vph.ruta_parada_id = rp.id
                             AND vph.estado = 'completada') as ultima_visita_historica
                         FROM rutas_paradas rp
                         INNER JOIN clientes c ON rp.cliente_id = c.id
                         LEFT JOIN codigos_qr qr ON c.codigo_qr_id = qr.id
                         LEFT JOIN visitas_paradas vp ON rp.id = vp.ruta_parada_id
                            AND DATE(vp.fecha_hora_entrada) = :fecha_hoy
                            AND vp.repartidor_id = :repartidor_id
                         WHERE rp.ruta_id = :ruta_id
                         GROUP BY rp.id, c.id, qr.id
                         ORDER BY rp.orden ASC";

        $stmtParadas = $conn->prepare($queryParadas);
        $stmtParadas->bindParam(':ruta_id', $rutaData['id']);
        $stmtParadas->bindParam(':fecha_hoy', $fechaHoy);
        $stmtParadas->bindParam(':repartidor_id', $repartidorId);
        $stmtParadas->execute();

        $todasLasParadas = $stmtParadas->fetchAll(PDO::FETCH_ASSOC);

        error_log("Total de paradas en la ruta: " . count($todasLasParadas));

        // Filtrar paradas según frecuencia para el día de hoy
        $paradasDelDia = [];

        foreach ($todasLasParadas as $parada) {
            $debeVisitarseHoy = false;
            $razon = '';

            // DEBUG: Log del estado de la parada
            error_log("Parada ID {$parada['id']} - Estado calculado: {$parada['estado']} - Visita ID: " . ($parada['visita_id'] ?? 'NULL'));

            // Si la parada ya fue visitada HOY (tiene visita_id de hoy), SIEMPRE incluirla
            if (($parada['estado'] === 'visitado' || $parada['estado'] === 'en_curso') && !empty($parada['visita_id'])) {
                $debeVisitarseHoy = true;
                $razon = "Ya visitada hoy (estado: {$parada['estado']})";
            }
            // OPCIÓN 1: Verificar días específicos de la semana
            elseif (!empty($parada['dias_semana'])) {
                $diasConfigurados = explode(',', strtoupper($parada['dias_semana']));
                $diasConfigurados = array_map('trim', $diasConfigurados);

                if (in_array($diaSemanaHoy, $diasConfigurados)) {
                    $debeVisitarseHoy = true;
                    $razon = "Día de semana configurado ($diaSemanaHoy)";
                }
            }

            // OPCIÓN 2: Verificar próxima visita programada
            elseif (!empty($parada['proxima_visita'])) {
                if ($parada['proxima_visita'] === $fechaHoy) {
                    $debeVisitarseHoy = true;
                    $razon = "Fecha programada exacta";
                }
            }

            // OPCIÓN 3: Verificar frecuencia en días desde la fecha de inicio
            // Para frecuencia de 15 días se usa 14 para el cálculo (quincenal)
            // Ej: fecha_inicio=Dec 21, frecuencia=15 -> aparece Jan 4 (día 14), Jan 18 (día 28)...
            elseif (!empty($parada['frecuencia_dias']) && !empty($parada['fecha_inicio_frecuencia'])) {
                $frecuenciaDias = (int)$parada['frecuencia_dias'];
                // Solo para quincenal (15 días) usar 14, el resto mantener igual
                $frecuenciaReal = ($frecuenciaDias == 15) ? 14 : $frecuenciaDias;
                $fechaInicio = strtotime($parada['fecha_inicio_frecuencia']);
                $fechaConsulta = strtotime($fechaHoy);
                $diasDesdeInicio = floor(($fechaConsulta - $fechaInicio) / (60 * 60 * 24));

                // Verificar si hoy es múltiplo exacto de la frecuencia desde la fecha de inicio
                // Ej: frecuencia 15 (real 14) -> días 14, 28, 42... (donde día % 14 == 0 y día > 0)
                // Ej: frecuencia 7 -> días 7, 14, 21... (donde día % 7 == 0 y día > 0)
                if ($diasDesdeInicio > 0 && ($diasDesdeInicio % $frecuenciaReal) == 0) {
                    $debeVisitarseHoy = true;
                    $razon = "Día exacto del ciclo: $diasDesdeInicio días desde inicio (cada $frecuenciaDias días)";
                }
            }
            // Si tiene frecuencia pero no hay fecha de referencia, incluir por defecto
            elseif (!empty($parada['frecuencia_dias'])) {
                $debeVisitarseHoy = true;
                $razon = "Frecuencia de {$parada['frecuencia_dias']} días (sin fecha de inicio configurada)";
            }

            // Si no tiene ninguna configuración de frecuencia, incluirla por defecto
            else {
                $debeVisitarseHoy = true;
                $razon = "Sin frecuencia configurada (visita diaria por defecto)";
            }

            if ($debeVisitarseHoy) {
                error_log("Parada ID {$parada['id']} - Cliente: {$parada['cliente_nombre']} - Incluida: $razon");
                $paradasDelDia[] = $parada;
            } else {
                error_log("Parada ID {$parada['id']} - Cliente: {$parada['cliente_nombre']} - Omitida");
            }
        }

        error_log("Paradas filtradas para hoy: " . count($paradasDelDia));

        // Obtener TODAS las visitas del día para cada parada (historial completo)
        if (!empty($paradasDelDia)) {
            $paradaIds = array_column($paradasDelDia, 'id');
            $placeholders = implode(',', array_fill(0, count($paradaIds), '?'));

            $queryHistorialVisitas = "SELECT
                                        vp.id,
                                        vp.ruta_parada_id,
                                        vp.fecha_hora_entrada,
                                        vp.fecha_hora_salida,
                                        vp.estado,
                                        vp.notas_entrada,
                                        vp.notas_salida,
                                        vp.observaciones,
                                        vp.tiene_venta,
                                        vp.monto_venta,
                                        vp.cantidad_productos
                                      FROM visitas_paradas vp
                                      WHERE vp.ruta_parada_id IN ($placeholders)
                                        AND DATE(vp.fecha_hora_entrada) = ?
                                        AND vp.repartidor_id = ?
                                      ORDER BY vp.fecha_hora_entrada DESC";

            $stmtHistorial = $conn->prepare($queryHistorialVisitas);
            // Bind parada IDs
            foreach ($paradaIds as $index => $paradaId) {
                $stmtHistorial->bindValue($index + 1, $paradaId);
            }
            // Bind fecha y repartidor
            $stmtHistorial->bindValue(count($paradaIds) + 1, $fechaHoy);
            $stmtHistorial->bindValue(count($paradaIds) + 2, $repartidorId);
            $stmtHistorial->execute();

            $todasLasVisitas = $stmtHistorial->fetchAll(PDO::FETCH_ASSOC);

            // Agrupar visitas por parada_id
            $visitasPorParada = [];
            foreach ($todasLasVisitas as $visita) {
                $paradaId = $visita['ruta_parada_id'];
                if (!isset($visitasPorParada[$paradaId])) {
                    $visitasPorParada[$paradaId] = [];
                }
                $visitasPorParada[$paradaId][] = $visita;
            }

            // Agregar historial de visitas a cada parada
            foreach ($paradasDelDia as &$parada) {
                $parada['historial_visitas'] = $visitasPorParada[$parada['id']] ?? [];
            }
            unset($parada); // Romper referencia
        }

        // Agregar paradas filtradas a la ruta
        $rutaData['paradas'] = $paradasDelDia;

        // Calcular estadísticas basadas en las paradas del día
        $paradasCompletadas = array_filter($paradasDelDia, function($p) {
            return isset($p['estado']) && ($p['estado'] === 'completada' || $p['estado'] === 'visitado' || $p['estado'] === 'en_curso');
        });

        $rutaData['total_paradas'] = count($paradasDelDia);
        $rutaData['paradas_completadas'] = count($paradasCompletadas);
        $rutaData['paradas_pendientes'] = $rutaData['total_paradas'] - $rutaData['paradas_completadas'];
        $rutaData['progreso'] = $rutaData['total_paradas'] > 0
            ? round(($rutaData['paradas_completadas'] / $rutaData['total_paradas']) * 100)
            : 0;

        // Agregar información adicional sobre el filtrado
        $rutaData['fecha_consultada'] = $fechaHoy;
        $rutaData['dia_semana'] = $diaSemanaHoy;
        $rutaData['total_paradas_ruta'] = count($todasLasParadas);

        error_log("Estadísticas - Total: {$rutaData['total_paradas']}, Completadas: {$rutaData['paradas_completadas']}, Progreso: {$rutaData['progreso']}%");

        sendResponse(true, "Ruta del día obtenida con {$rutaData['total_paradas']} paradas programadas", $rutaData);

    } catch (Exception $e) {
        error_log("Error obteniendo ruta del día: " . $e->getMessage());
        sendResponse(false, "Error al obtener ruta del día");
    }
}

// === REGISTRAR VISITA A CLIENTE (APP MÓVIL) ===
if ($tipo === 'registrarVisitaCliente') {
    error_log("=== REGISTRANDO VISITA A CLIENTE ===");

    // Obtener token del repartidor
    $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
    if (empty($authHeader)) {
        $headers = getallheaders();
        $authHeader = $headers['Authorization'] ?? '';
    }

    if (empty($authHeader)) {
        sendResponse(false, "Token de autenticación requerido");
    }

    $token = str_replace('Bearer ', '', $authHeader);

    // Verificar token y obtener repartidor_id
    $auth = new Auth($conn);
    $payload = $auth->verificarJWT($token);

    if (!$payload) {
        sendResponse(false, "Token inválido o expirado");
    }

    $repartidorId = $payload['repartidor_id'] ?? null;
    if (!$repartidorId) {
        sendResponse(false, "Token no válido para repartidor");
    }

    // Obtener parámetros
    $clienteId = isset($datapost['cliente_id']) ? (int)$datapost['cliente_id'] : 0;
    $tipoVisita = isset($datapost['tipo_visita']) ? $datapost['tipo_visita'] : 'entrada';
    $latitud = isset($datapost['latitud']) ? (float)$datapost['latitud'] : null;
    $longitud = isset($datapost['longitud']) ? (float)$datapost['longitud'] : null;
    $notas = isset($datapost['notas']) ? $datapost['notas'] : '';
    $codigoQR = isset($datapost['codigo_qr']) ? $datapost['codigo_qr'] : null;

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

    // Validar tipo de visita
    if (!in_array($tipoVisita, ['entrada', 'salida', 'completa'])) {
        sendResponse(false, "Tipo de visita no válido. Debe ser 'entrada', 'salida' o 'completa'");
    }

    try {
        error_log("Registrando visita: Cliente ID=$clienteId, Repartidor ID=$repartidorId, Tipo=$tipoVisita");

        // Verificar que el cliente existe y está activo
        $queryCliente = "SELECT id, codigo, nombre_comercial, direccion, latitud, longitud, activo
                         FROM clientes WHERE id = :cliente_id";
        $stmtCliente = $conn->prepare($queryCliente);
        $stmtCliente->bindParam(':cliente_id', $clienteId);
        $stmtCliente->execute();
        $cliente = $stmtCliente->fetch(PDO::FETCH_ASSOC);

        if (!$cliente) {
            sendResponse(false, "Cliente no encontrado");
        }

        if (!$cliente['activo']) {
            sendResponse(false, "Cliente inactivo");
        }

        // Buscar la parada en la ruta activa del repartidor
        $queryParada = "SELECT rp.id, rp.ruta_id, rp.estado, r.nombre as ruta_nombre
                        FROM rutas_paradas rp
                        INNER JOIN rutas r ON rp.ruta_id = r.id
                        WHERE rp.cliente_id = :cliente_id
                        AND r.repartidor_id = :repartidor_id
                        AND r.activa = 1
                        AND r.estado IN ('planificada', 'en_progreso')
                        LIMIT 1";

        $stmtParada = $conn->prepare($queryParada);
        $stmtParada->bindParam(':cliente_id', $clienteId);
        $stmtParada->bindParam(':repartidor_id', $repartidorId);
        $stmtParada->execute();
        $parada = $stmtParada->fetch(PDO::FETCH_ASSOC);

        $esVisitaFueraDeRuta = false;
        if (!$parada) {
            error_log("ADVERTENCIA: Cliente no está en la ruta activa del repartidor - Se registrará como visita fuera de ruta");
            $esVisitaFueraDeRuta = true;
        }

        // Calcular distancia entre GPS actual y ubicación del cliente (si hay coordenadas)
        $distanciaMetros = null;
        if ($latitud && $longitud && $cliente['latitud'] && $cliente['longitud']) {
            // Fórmula de Haversine para calcular distancia
            $radioTierra = 6371000; // metros
            $dLat = deg2rad($cliente['latitud'] - $latitud);
            $dLng = deg2rad($cliente['longitud'] - $longitud);
            $a = sin($dLat/2) * sin($dLat/2) +
                 cos(deg2rad($latitud)) * cos(deg2rad($cliente['latitud'])) *
                 sin($dLng/2) * sin($dLng/2);
            $c = 2 * atan2(sqrt($a), sqrt(1-$a));
            $distanciaMetros = $radioTierra * $c;
        }

        // Verificar si ya existe una visita en curso para esta parada o cliente
        $visitaEnCurso = null;
        if ($esVisitaFueraDeRuta) {
            // Buscar visita en curso por cliente y repartidor (sin parada específica)
            $queryVisitaActual = "SELECT id, tipo_visita, fecha_hora_entrada
                                  FROM visitas_paradas
                                  WHERE cliente_id = :cliente_id
                                  AND repartidor_id = :repartidor_id
                                  AND estado = 'en_curso'
                                  ORDER BY created_at DESC
                                  LIMIT 1";
            $stmtVisitaActual = $conn->prepare($queryVisitaActual);
            $stmtVisitaActual->bindParam(':cliente_id', $clienteId);
            $stmtVisitaActual->bindParam(':repartidor_id', $repartidorId);
            $stmtVisitaActual->execute();
            $visitaEnCurso = $stmtVisitaActual->fetch(PDO::FETCH_ASSOC);
        } else {
            $queryVisitaActual = "SELECT id, tipo_visita, fecha_hora_entrada
                                  FROM visitas_paradas
                                  WHERE ruta_parada_id = :parada_id
                                  AND estado = 'en_curso'
                                  ORDER BY created_at DESC
                                  LIMIT 1";
            $stmtVisitaActual = $conn->prepare($queryVisitaActual);
            $stmtVisitaActual->bindParam(':parada_id', $parada['id']);
            $stmtVisitaActual->execute();
            $visitaEnCurso = $stmtVisitaActual->fetch(PDO::FETCH_ASSOC);
        }

        $visitaId = null;

        // Lógica según el tipo de visita
        // Obtener IDs de parada y ruta (null si es visita fuera de ruta)
        $paradaId = $esVisitaFueraDeRuta ? null : $parada['id'];
        $rutaId = $esVisitaFueraDeRuta ? null : $parada['ruta_id'];

        if ($tipoVisita === 'entrada' || $tipoVisita === 'completa') {
            // Crear nueva visita o actualizar existente
            if (!$visitaEnCurso) {
                // Crear nueva visita
                $queryInsert = "INSERT INTO visitas_paradas (
                                    ruta_parada_id, ruta_id, cliente_id, repartidor_id,
                                    tipo_visita, fecha_hora_entrada, latitud_entrada, longitud_entrada,
                                    distancia_cliente_metros, notas_entrada, codigo_qr_usado,
                                    estado, created_at
                                ) VALUES (
                                    :parada_id, :ruta_id, :cliente_id, :repartidor_id,
                                    :tipo_visita, NOW(), :latitud, :longitud,
                                    :distancia, :notas, :codigo_qr,
                                    :estado, NOW()
                                )";

                $estadoVisita = ($tipoVisita === 'completa') ? 'completada' : 'en_curso';
                $notasConFlag = $esVisitaFueraDeRuta ? "[FUERA DE RUTA] " . $notas : $notas;

                $stmtInsert = $conn->prepare($queryInsert);
                $stmtInsert->bindParam(':parada_id', $paradaId);
                $stmtInsert->bindParam(':ruta_id', $rutaId);
                $stmtInsert->bindParam(':cliente_id', $clienteId);
                $stmtInsert->bindParam(':repartidor_id', $repartidorId);
                $stmtInsert->bindParam(':tipo_visita', $tipoVisita);
                $stmtInsert->bindParam(':latitud', $latitud);
                $stmtInsert->bindParam(':longitud', $longitud);
                $stmtInsert->bindParam(':distancia', $distanciaMetros);
                $stmtInsert->bindParam(':notas', $notasConFlag);
                $stmtInsert->bindParam(':codigo_qr', $codigoQR);
                $stmtInsert->bindParam(':estado', $estadoVisita);
                $stmtInsert->execute();

                $visitaId = $conn->lastInsertId();
                error_log("Nueva visita creada con ID: $visitaId" . ($esVisitaFueraDeRuta ? " (FUERA DE RUTA)" : ""));

                // Si es visita completa y hay parada, actualizar estado de parada
                if ($tipoVisita === 'completa' && !$esVisitaFueraDeRuta) {
                    $queryUpdateParada = "UPDATE rutas_paradas
                                          SET estado = 'visitado', fecha_visita = NOW()
                                          WHERE id = :parada_id";
                    $stmtUpdateParada = $conn->prepare($queryUpdateParada);
                    $stmtUpdateParada->bindParam(':parada_id', $paradaId);
                    $stmtUpdateParada->execute();
                }
            }
        } elseif ($tipoVisita === 'salida') {
            // Actualizar visita en curso con información de salida
            if ($visitaEnCurso) {
                $queryUpdate = "UPDATE visitas_paradas SET
                                fecha_hora_salida = NOW(),
                                latitud_salida = :latitud,
                                longitud_salida = :longitud,
                                notas_salida = :notas,
                                estado = 'completada',
                                updated_at = NOW()
                                WHERE id = :visita_id";

                $stmtUpdate = $conn->prepare($queryUpdate);
                $stmtUpdate->bindParam(':latitud', $latitud);
                $stmtUpdate->bindParam(':longitud', $longitud);
                $stmtUpdate->bindParam(':notas', $notas);
                $stmtUpdate->bindParam(':visita_id', $visitaEnCurso['id']);
                $stmtUpdate->execute();

                $visitaId = $visitaEnCurso['id'];
                error_log("Visita actualizada con salida: $visitaId" . ($esVisitaFueraDeRuta ? " (FUERA DE RUTA)" : ""));

                // Actualizar estado de parada a visitado (solo si hay parada)
                if (!$esVisitaFueraDeRuta) {
                    $queryUpdateParada = "UPDATE rutas_paradas
                                          SET estado = 'visitado', fecha_visita = NOW()
                                          WHERE id = :parada_id";
                    $stmtUpdateParada = $conn->prepare($queryUpdateParada);
                    $stmtUpdateParada->bindParam(':parada_id', $paradaId);
                    $stmtUpdateParada->execute();
                }
            } else {
                // Si es visita fuera de ruta sin entrada previa, crear visita completa
                if ($esVisitaFueraDeRuta) {
                    error_log("Visita fuera de ruta sin entrada previa, creando visita completa");
                    $queryInsert = "INSERT INTO visitas_paradas (
                                        ruta_parada_id, ruta_id, cliente_id, repartidor_id,
                                        tipo_visita, fecha_hora_entrada, fecha_hora_salida,
                                        latitud_entrada, longitud_entrada, latitud_salida, longitud_salida,
                                        distancia_cliente_metros, notas_entrada, codigo_qr_usado,
                                        estado, created_at
                                    ) VALUES (
                                        :parada_id, :ruta_id, :cliente_id, :repartidor_id,
                                        'completa', NOW(), NOW(),
                                        :latitud, :longitud, :latitud2, :longitud2,
                                        :distancia, :notas, :codigo_qr,
                                        'completada', NOW()
                                    )";

                    $notasConFlag = "[FUERA DE RUTA] " . $notas;

                    $stmtInsert = $conn->prepare($queryInsert);
                    $stmtInsert->bindParam(':parada_id', $paradaId);
                    $stmtInsert->bindParam(':ruta_id', $rutaId);
                    $stmtInsert->bindParam(':cliente_id', $clienteId);
                    $stmtInsert->bindParam(':repartidor_id', $repartidorId);
                    $stmtInsert->bindParam(':latitud', $latitud);
                    $stmtInsert->bindParam(':longitud', $longitud);
                    $stmtInsert->bindParam(':latitud2', $latitud);
                    $stmtInsert->bindParam(':longitud2', $longitud);
                    $stmtInsert->bindParam(':distancia', $distanciaMetros);
                    $stmtInsert->bindParam(':notas', $notasConFlag);
                    $stmtInsert->bindParam(':codigo_qr', $codigoQR);
                    $stmtInsert->execute();

                    $visitaId = $conn->lastInsertId();
                    error_log("Visita completa fuera de ruta creada con ID: $visitaId");
                } else {
                    sendResponse(false, "No hay visita en curso para registrar salida");
                }
            }
        }

        // Obtener datos completos de la visita registrada
        if ($visitaId) {
            $queryVisita = "SELECT * FROM visitas_paradas WHERE id = :visita_id";
            $stmtVisita = $conn->prepare($queryVisita);
            $stmtVisita->bindParam(':visita_id', $visitaId);
            $stmtVisita->execute();
            $visitaCompleta = $stmtVisita->fetch(PDO::FETCH_ASSOC);
        }

        // Preparar respuesta
        $respuesta = [
            'visita_id' => $visitaId,
            'cliente' => [
                'id' => $cliente['id'],
                'codigo' => $cliente['codigo'],
                'nombre_comercial' => $cliente['nombre_comercial'],
                'direccion' => $cliente['direccion']
            ],
            'tipo_visita' => $tipoVisita,
            'timestamp' => date('Y-m-d H:i:s'),
            'ruta' => $esVisitaFueraDeRuta ? 'FUERA DE RUTA' : ($parada['ruta_nombre'] ?? null),
            'parada_id' => $paradaId,
            'distancia_metros' => $distanciaMetros ? round($distanciaMetros, 2) : null,
            'estado' => $visitaCompleta['estado'] ?? 'en_curso',
            'fuera_de_ruta' => $esVisitaFueraDeRuta
        ];

        $mensaje = $esVisitaFueraDeRuta
            ? "Visita registrada exitosamente (cliente fuera de ruta activa)"
            : "Visita registrada exitosamente";
        sendResponse(true, $mensaje, $respuesta);

    } catch (Exception $e) {
        error_log("Error registrando visita a cliente: " . $e->getMessage());
        error_log("Stack trace: " . $e->getTraceAsString());
        sendResponse(false, "Error registrando visita: " . $e->getMessage());
    }
}

// === REPORTE DE VISITAS DE RUTAS ===
if ($tipo === 'getReporteVisitasRutas') {
    error_log("=== GENERANDO REPORTE DE VISITAS DE RUTAS ===");

    // Obtener parámetros
    $fecha = isset($datapost['fecha']) ? $datapost['fecha'] : date('Y-m-d');
    $repartidorId = isset($datapost['repartidor_id']) ? (int)$datapost['repartidor_id'] : null;
    $rutaId = isset($datapost['ruta_id']) ? (int)$datapost['ruta_id'] : null;

    try {
        // Query para obtener rutas con sus paradas y estado de visitas
        $query = "SELECT
                    r.id as ruta_id,
                    r.codigo as ruta_codigo,
                    r.nombre as ruta_nombre,
                    r.estado as ruta_estado,
                    rep.id as repartidor_id,
                    rep.codigo as repartidor_codigo,
                    CONCAT(rep.nombre, ' ', rep.apellidos) as repartidor_nombre,
                    rp.id as parada_id,
                    rp.orden as parada_orden,
                    rp.frecuencia_dias,
                    c.id as cliente_id,
                    c.codigo as cliente_codigo,
                    c.nombre_comercial as cliente_nombre,
                    c.direccion as cliente_direccion,
                    vp.id as visita_id,
                    vp.fecha_hora_entrada,
                    vp.fecha_hora_salida,
                    vp.estado as visita_estado,
                    vp.tipo_visita,
                    vp.resultado as visita_resultado,
                    CASE WHEN ven.id IS NOT NULL THEN 1 ELSE 0 END as tiene_venta,
                    COALESCE(ven.total, 0) as monto_venta,
                    ven.id as venta_id,
                    CASE
                        WHEN vp.id IS NOT NULL AND vp.estado = 'completada' THEN 'visitado'
                        WHEN vp.id IS NOT NULL AND vp.estado = 'en_curso' THEN 'en_curso'
                        ELSE 'pendiente'
                    END as estado_visita
                  FROM rutas r
                  INNER JOIN repartidores rep ON r.repartidor_id = rep.id
                  INNER JOIN rutas_paradas rp ON r.id = rp.ruta_id
                  INNER JOIN clientes c ON rp.cliente_id = c.id
                  LEFT JOIN visitas_paradas vp ON rp.id = vp.ruta_parada_id
                      AND DATE(vp.fecha_hora_entrada) = :fecha
                  LEFT JOIN ventas ven ON ven.visita_id = vp.id
                  WHERE r.activa = 1";

        if ($repartidorId) {
            $query .= " AND r.repartidor_id = :repartidor_id";
        }

        if ($rutaId) {
            $query .= " AND r.id = :ruta_id";
        }

        $query .= " ORDER BY r.nombre, rp.orden";

        $stmt = $conn->prepare($query);
        $stmt->bindParam(':fecha', $fecha);

        if ($repartidorId) {
            $stmt->bindParam(':repartidor_id', $repartidorId, PDO::PARAM_INT);
        }

        if ($rutaId) {
            $stmt->bindParam(':ruta_id', $rutaId, PDO::PARAM_INT);
        }

        $stmt->execute();
        $registros = $stmt->fetchAll(PDO::FETCH_ASSOC);

        // Agrupar por ruta
        $rutasAgrupadas = [];
        foreach ($registros as $reg) {
            $rutaKey = $reg['ruta_id'];

            if (!isset($rutasAgrupadas[$rutaKey])) {
                $rutasAgrupadas[$rutaKey] = [
                    'ruta_id' => $reg['ruta_id'],
                    'ruta_codigo' => $reg['ruta_codigo'],
                    'ruta_nombre' => $reg['ruta_nombre'],
                    'repartidor_id' => $reg['repartidor_id'],
                    'repartidor_codigo' => $reg['repartidor_codigo'],
                    'repartidor_nombre' => $reg['repartidor_nombre'],
                    'paradas' => [],
                    'total_paradas' => 0,
                    'visitados' => 0,
                    'pendientes' => 0,
                    'en_curso' => 0
                ];
            }

            // Verificar si esta parada ya existe (para evitar duplicados por múltiples ventas)
            $paradaExiste = false;
            foreach ($rutasAgrupadas[$rutaKey]['paradas'] as &$paradaExistente) {
                if ($paradaExistente['parada_id'] == $reg['parada_id']) {
                    // La parada ya existe, sumar el monto de esta venta adicional
                    if ($reg['venta_id']) {
                        $paradaExistente['monto_venta'] += (float)$reg['monto_venta'];
                        $paradaExistente['ventas_ids'][] = $reg['venta_id'];
                    }
                    $paradaExiste = true;
                    break;
                }
            }
            unset($paradaExistente);

            if (!$paradaExiste) {
                $rutasAgrupadas[$rutaKey]['paradas'][] = [
                    'parada_id' => $reg['parada_id'],
                    'orden' => $reg['parada_orden'],
                    'frecuencia_dias' => $reg['frecuencia_dias'],
                    'cliente_id' => $reg['cliente_id'],
                    'cliente_codigo' => $reg['cliente_codigo'],
                    'cliente_nombre' => $reg['cliente_nombre'],
                    'cliente_direccion' => $reg['cliente_direccion'],
                    'visita_id' => $reg['visita_id'],
                    'hora_entrada' => $reg['fecha_hora_entrada'] ? date('H:i', strtotime($reg['fecha_hora_entrada'])) : null,
                    'hora_salida' => $reg['fecha_hora_salida'] ? date('H:i', strtotime($reg['fecha_hora_salida'])) : null,
                    'estado_visita' => $reg['estado_visita'],
                    'tipo_visita' => $reg['tipo_visita'],
                    'tiene_venta' => (bool)$reg['tiene_venta'],
                    'monto_venta' => $reg['monto_venta'] ? (float)$reg['monto_venta'] : 0,
                    'visita_resultado' => $reg['visita_resultado'],
                    'ventas_ids' => $reg['venta_id'] ? [$reg['venta_id']] : [],
                    'productos' => []
                ];
            }

            // Solo contar si es una parada nueva
            if (!$paradaExiste) {
                $rutasAgrupadas[$rutaKey]['total_paradas']++;

                if ($reg['estado_visita'] === 'visitado') {
                    $rutasAgrupadas[$rutaKey]['visitados']++;
                } elseif ($reg['estado_visita'] === 'en_curso') {
                    $rutasAgrupadas[$rutaKey]['en_curso']++;
                } else {
                    $rutasAgrupadas[$rutaKey]['pendientes']++;
                }
            }
        }

        // Obtener productos vendidos para cada parada con ventas
        foreach ($rutasAgrupadas as &$ruta) {
            foreach ($ruta['paradas'] as &$parada) {
                if (!empty($parada['ventas_ids'])) {
                    // Buscar productos de todas las ventas de esta parada
                    $ventasIdsStr = implode(',', array_map('intval', $parada['ventas_ids']));
                    $queryProductos = "SELECT
                                        vd.producto_id,
                                        p.codigo as producto_codigo,
                                        p.nombre as producto_nombre,
                                        SUM(vd.cantidad) as cantidad,
                                        vd.precio_unitario,
                                        SUM(vd.subtotal) as subtotal
                                       FROM ventas_detalle vd
                                       INNER JOIN productos p ON vd.producto_id = p.id
                                       WHERE vd.venta_id IN ($ventasIdsStr)
                                       GROUP BY vd.producto_id, p.codigo, p.nombre, vd.precio_unitario
                                       ORDER BY p.nombre";
                    $stmtProd = $conn->prepare($queryProductos);
                    $stmtProd->execute();
                    $parada['productos'] = $stmtProd->fetchAll(PDO::FETCH_ASSOC);
                }
            }
        }

        // Calcular porcentaje de cumplimiento para cada ruta
        foreach ($rutasAgrupadas as &$ruta) {
            $completados = $ruta['visitados'] + $ruta['en_curso'];
            $ruta['porcentaje_cumplimiento'] = $ruta['total_paradas'] > 0
                ? round(($ruta['visitados'] / $ruta['total_paradas']) * 100, 1)
                : 0;
        }

        // Calcular resumen general
        $totalRutas = count($rutasAgrupadas);
        $totalParadas = array_sum(array_column($rutasAgrupadas, 'total_paradas'));
        $totalVisitados = array_sum(array_column($rutasAgrupadas, 'visitados'));
        $totalPendientes = array_sum(array_column($rutasAgrupadas, 'pendientes'));
        $totalEnCurso = array_sum(array_column($rutasAgrupadas, 'en_curso'));
        $porcentajeGeneral = $totalParadas > 0 ? round(($totalVisitados / $totalParadas) * 100, 1) : 0;

        $resumen = [
            'fecha' => $fecha,
            'total_rutas' => $totalRutas,
            'total_paradas' => $totalParadas,
            'total_visitados' => $totalVisitados,
            'total_pendientes' => $totalPendientes,
            'total_en_curso' => $totalEnCurso,
            'porcentaje_cumplimiento' => $porcentajeGeneral
        ];

        sendResponse(true, "Reporte generado exitosamente", [
            'resumen' => $resumen,
            'rutas' => array_values($rutasAgrupadas)
        ]);

    } catch (Exception $e) {
        error_log("Error generando reporte de visitas: " . $e->getMessage());
        sendResponse(false, "Error generando reporte: " . $e->getMessage());
    }
}
