<?php
require '../../vendor/autoload.php';
require_once __DIR__ . '/../config/database.php';
use PhpOffice\PhpSpreadsheet\IOFactory;

// Ampliar tiempo de ejecución en entorno local
@set_time_limit(300);
@ini_set('max_execution_time', '300');

/**
 * Limpia y normaliza un valor
 * @param mixed $v Valor a limpiar
 * @return string Valor limpio
 */
function limpiar($v) {
    return trim($v ?? '');
}

/**
 * Normaliza texto para comparaciones (sin acentos y en mayúsculas)
 * @param mixed $v
 * @return string
 */
function normalizarTexto($v) {
    $texto = strtoupper(limpiar($v));
    $texto = strtr($texto, [
        'Á' => 'A',
        'É' => 'E',
        'Í' => 'I',
        'Ó' => 'O',
        'Ú' => 'U',
        'Ü' => 'U',
        'Ñ' => 'N'
    ]);
    return $texto;
}

/**
 * Formatea una incidencia agregando la etiqueta de goce de sueldo cuando aplique
 * @param string $incidencia
 * @param string $goceDeSueldo
 * @return string
 */
function formatearIncidenciaConGoce($incidencia, $goceDeSueldo) {
    $incidencia = limpiar($incidencia);
    $goceDeSueldo = limpiar($goceDeSueldo);

    if ($incidencia === '' || $goceDeSueldo === '') {
        return $incidencia;
    }

    $goceTexto = $goceDeSueldo;
    if ($goceDeSueldo === 'Con') {
        $goceTexto = 'Con goce de sueldo';
    } else if ($goceDeSueldo === 'Sin') {
        $goceTexto = 'Sin goce de sueldo';
    }

    return $incidencia . ' - ' . $goceTexto;
}

/**
 * Indica si una fila del Excel contiene el valor FESTIVO en alguno de sus campos
 * @param array $row Fila del Excel
 * @return bool
 */
function filaEsFestivo($row) {
    if (!is_array($row)) {
        return false;
    }

    $marcadoresFestivo = [
        'FESTIVO',
        'DIA INHABIL'
    ];

    foreach ($row as $valor) {
        $texto = normalizarTexto($valor);
        if ($texto === '') {
            continue;
        }
        foreach ($marcadoresFestivo as $marcador) {
            if (strpos($texto, $marcador) !== false) {
                return true;
            }
        }
    }

    return false;
}

/**
 * Valida el archivo subido
 * @return string Ruta del archivo temporal
 */
function validarArchivoSubido() {
    if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_FILES['excel_file'])) {
        echo '<div class="alert alert-danger">Archivo no recibido.</div>';
        exit;
    }

    $tmpFile = $_FILES['excel_file']['tmp_name'];
    if (!file_exists($tmpFile)) {
        echo '<div class="alert alert-danger">No se pudo cargar el archivo.</div>';
        exit;
    }

    // Validar tipo de archivo (opcional)
    $fileType = strtolower(pathinfo($_FILES['excel_file']['name'], PATHINFO_EXTENSION));
    if (!in_array($fileType, ['xls', 'xlsx', 'csv'])) {
        echo '<div class="alert alert-danger">Formato de archivo no válido. Solo se aceptan Excel (.xls, .xlsx) o CSV.</div>';
        exit;
    }

    return $tmpFile;
}

/**
 * Carga datos desde el archivo Excel
 * @param string $tmpFile Ruta del archivo temporal
 * @return array Datos del Excel
 */
function cargarDatosExcel($tmpFile) {
    try {
        $spreadsheet = IOFactory::load($tmpFile);
        $sheet = $spreadsheet->getActiveSheet();
        return $sheet->toArray(null, true, true, true);
    } catch (Exception $e) {
        // Registrar error en log
        $logFile = __DIR__ . '/debug_comparar_excel.log';
        $errorMsg = date('Y-m-d H:i:s') . "\n";
        $errorMsg .= "Error al leer el archivo: " . $e->getMessage() . "\n";
        $errorMsg .= $e->getTraceAsString() . "\n";
        file_put_contents($logFile, $errorMsg, FILE_APPEND);
        // Mostrar error detallado en pantalla
        echo '<div class="alert alert-danger">Error al leer el archivo: ' . htmlspecialchars($e->getMessage()) . '<br><pre>' . htmlspecialchars($e->getTraceAsString()) . '</pre></div>';
        exit;
    }
}

/**
 * Obtiene datos de la base de datos para un rango de fechas
 * @param PDO $conn Conexión a la base de datos
 * @param string $fecha_inicio Fecha inicial (YYYY-MM-DD)
 * @param string $fecha_fin Fecha final (YYYY-MM-DD)
 * @return array Datos de la base de datos
 */
function obtenerDatosBaseDatos($conn, $fecha_inicio, $fecha_fin) {
    if (!$fecha_inicio || !$fecha_fin) {
        echo '<div class="alert alert-danger">No se seleccionó un rango de fechas válido.</div>';
        exit;
    }

    // Consulta modificada con COLLATE uniforme y horasPermiso
    $sql = 'SELECT eh.nombre_completo, eh.fecha, eh.hora_entrada, eh.incidencias, 
                   p.`DEPARTAMENTO INTERNO` as departamento_interno, 
                   r.`Region` as region,
                   p.`CURP`,
                   (
                       SELECT ic.horasPermiso 
                       FROM IncidenciasCaptur ic 
                       WHERE ic.CURP = p.`CURP` 
                       AND ic.Estatus = "Validado por RH"
                       AND eh.fecha BETWEEN ic.fechaInicio AND ic.fechaFin
                       LIMIT 1
                   ) as horas_permiso
            FROM ExportacionesHorarios eh
            LEFT JOIN Plantilla p ON eh.nombre_completo COLLATE latin1_general_ci = p.`NOMBRE COMPLETO` COLLATE latin1_general_ci
            LEFT JOIN PDVXRegion r ON TRIM(LOWER(p.`DEPARTAMENTO INTERNO`)) COLLATE latin1_general_ci = TRIM(LOWER(r.`Tienda`)) COLLATE latin1_general_ci
            WHERE eh.fecha BETWEEN ? AND ?';
    
    $stmt = $conn->prepare($sql);
    $stmt->execute([$fecha_inicio, $fecha_fin]);
    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // Fallback: usar DetalleHorariosSemana cuando no exista ExportacionesHorarios (no Matriz)
    $sqlFallback = '
        SELECT p.`NOMBRE COMPLETO` AS nombre_completo,
               d.fecha,
               d.hora_entrada,
               NULL AS incidencias,
               p.`DEPARTAMENTO INTERNO` as departamento_interno,
               r.`Region` as region,
               p.`CURP` AS CURP,
               NULL AS horas_permiso
        FROM HorariosAsignados h
        JOIN DetalleHorariosSemana d ON h.id_horario = d.id_horario
        JOIN Plantilla p ON h.curp = p.CURP
        LEFT JOIN PDVXRegion r ON TRIM(LOWER(p.`DEPARTAMENTO INTERNO`)) COLLATE latin1_general_ci = TRIM(LOWER(r.`Tienda`)) COLLATE latin1_general_ci
        WHERE d.fecha BETWEEN ? AND ?
          AND p.`DEPARTAMENTO INTERNO` <> "GRP TDA ADMINISTRACION MER"
    ';
    try {
        $stmtF = $conn->prepare($sqlFallback);
        $stmtF->execute([$fecha_inicio, $fecha_fin]);
        $fallbackData = $stmtF->fetchAll(PDO::FETCH_ASSOC);
        if (!empty($fallbackData)) {
            $existentes = [];
            foreach ($data as $d) {
                $key = limpiar($d['nombre_completo']) . '||' . $d['fecha'];
                $existentes[$key] = true;
            }
            foreach ($fallbackData as $f) {
                $key = limpiar($f['nombre_completo']) . '||' . $f['fecha'];
                if (!isset($existentes[$key])) {
                    $data[] = $f;
                }
            }
        }
    } catch (Exception $e) {
        $logFile = __DIR__ . '/debug_comparar_excel.log';
        $msg = date('Y-m-d H:i:s') . "\n";
        $msg .= "Error Fallback: " . $e->getMessage() . "\n";
        $msg .= $sqlFallback . "\n\n";
        file_put_contents($logFile, $msg, FILE_APPEND);
    }

    // Complemento para Región Matriz (miembros_area_admin) usando DetalleHorariosSemana
        $sqlMatriz = '
         SELECT p.`NOMBRE COMPLETO` AS nombre_completo,
             d.fecha,
             d.hora_entrada,
             eh.incidencias AS incidencias,
             a.area AS departamento_interno,
             "Matriz" AS region,
             p.`CURP` AS CURP,
             NULL AS horas_permiso
         FROM miembros_area_admin ma
         JOIN areas_admin a ON a.id = ma.area_id
         JOIN Plantilla p ON ma.curp = p.CURP
         JOIN HorariosAsignados h ON h.curp = p.CURP
         JOIN DetalleHorariosSemana d ON h.id_horario = d.id_horario
         LEFT JOIN ExportacionesHorarios eh
           ON eh.nombre_completo COLLATE latin1_general_ci = p.`NOMBRE COMPLETO` COLLATE latin1_general_ci
          AND eh.fecha = d.fecha
         WHERE d.fecha BETWEEN ? AND ?
           AND p.`DEPARTAMENTO INTERNO` = "GRP TDA ADMINISTRACION MER"
        ';
    $matrizData = [];
    try {
        $stmtM = $conn->prepare($sqlMatriz);
        $stmtM->execute([$fecha_inicio, $fecha_fin]);
        $matrizData = $stmtM->fetchAll(PDO::FETCH_ASSOC);
    } catch (Exception $e) {
        $logFile = __DIR__ . '/debug_comparar_excel.log';
        $msg = date('Y-m-d H:i:s') . "\n";
        $msg .= "Error Matriz: " . $e->getMessage() . "\n";
        $msg .= $sqlMatriz . "\n\n";
        file_put_contents($logFile, $msg, FILE_APPEND);
    }

    if (!empty($matrizData)) {
        $existentes = [];
        foreach ($data as $d) {
            $key = limpiar($d['nombre_completo']) . '||' . $d['fecha'];
            $existentes[$key] = true;
        }
        foreach ($matrizData as $m) {
            $key = limpiar($m['nombre_completo']) . '||' . $m['fecha'];
            if (!isset($existentes[$key])) {
                $data[] = $m;
            }
        }
    }

    return $data;
}

/**
 * Procesa y compara los datos entre Excel y Base de Datos
 * @param array $rows Datos del Excel
 * @param array $dbData Datos de la base de datos
 * @param array $departamentoPorNombre Mapeo de nombre a departamento
 * @param array $regionPorNombre Mapeo de nombre a región
 * @param array $permisosMap Mapeo de permisos por nombre+fecha
 * @return array Resultados de la comparación
 */
function compararDatos($rows, $dbData, $departamentoPorNombre = [], $regionPorNombre = [], $permisosMap = [], $descansosMap = [], $curpPorNombre = [], $vacacionesMap = [], $incidenciasCurpFechaMap = []) {
    // Indexar datos de la base de datos por nombre+fecha
    $dbMap = [];
    $departamentoRegionMap = [];
    foreach ($dbData as $d) {
        $key = $d['nombre_completo'] . '||' . $d['fecha'];
        $dbMap[$key] = $d;
        $departamento = limpiar($d['departamento_interno']);
        $region = limpiar($d['region']);
        if ($departamento && $region) {
            $departamentoRegionMap[$departamento] = $region;
        }
    }
    $resultados = [];
    $usados_db_keys = [];
    // Obtener todas las fechas del rango seleccionado
    global $fecha_inicio, $fecha_fin;
    $fechas_rango = [];
    $start = strtotime($fecha_inicio);
    $end = strtotime($fecha_fin);
    for ($d = $start; $d <= $end; $d = strtotime('+1 day', $d)) {
        $fechas_rango[] = date('Y-m-d', $d);
    }

    // Obtener todos los nombres únicos del Excel y la BD
    $nombres = [];
    foreach ($rows as $i => $row) {
        if ($i == 1) continue;
        $nombre = limpiar($row['C']);
        if ($nombre) $nombres[$nombre] = true;
    }
    foreach ($dbData as $d) {
        $nombre = limpiar($d['nombre_completo']);
        if ($nombre) $nombres[$nombre] = true;
    }
    $nombres = array_keys($nombres);

    // Indexar datos del Excel por nombre+fecha para acceso rápido
    $excelMap = [];
    $festivoMap = [];
    $inhabilPorFecha = [];
    foreach ($rows as $i => $row) {
        if ($i == 1) continue;
        $nombre = limpiar($row['C']);
        $fecha = limpiar($row['F']);
        if ($fecha && filaEsFestivo($row)) {
            $inhabilPorFecha[$fecha] = true;
        }
        if ($nombre && $fecha) {
            $keyExcel = $nombre . '||' . $fecha;
            $excelMap[$keyExcel] = $row;
            if (!isset($festivoMap[$keyExcel])) {
                $festivoMap[$keyExcel] = false;
            }
            if (filaEsFestivo($row)) {
                $festivoMap[$keyExcel] = true;
            }
        }
    }

    // Procesar por bloques de 100 nombres
    $bloque = 100;
    $totalNombres = count($nombres);
    for ($offset = 0; $offset < $totalNombres; $offset += $bloque) {
        $nombresBloque = array_slice($nombres, $offset, $bloque);
        foreach ($nombresBloque as $nombre) {
            foreach ($fechas_rango as $fecha) {
                $key = $nombre . '||' . $fecha;
                $hora_excel = '';
                $esFestivoExcel = false;
                if (isset($excelMap[$key])) {
                    $hora_excel = limpiar($excelMap[$key]['N']);
                    $esFestivoExcel = !empty($festivoMap[$key]);
                }
                if (!$esFestivoExcel && isset($inhabilPorFecha[$fecha])) {
                    $esFestivoExcel = true;
                }
                $departamento = isset($departamentoPorNombre[$nombre]) ? $departamentoPorNombre[$nombre] : 'SIN DEPARTAMENTO';
                $region = isset($regionPorNombre[$nombre]) ? $regionPorNombre[$nombre] : (isset($dbMap[$key]['region']) ? limpiar($dbMap[$key]['region']) : 'SIN REGIÓN');
                if (isset($dbMap[$key])) {
                    $hora_db = limpiar($dbMap[$key]['hora_entrada']);
                    $horas_permiso = isset($dbMap[$key]['horas_permiso']) ? limpiar($dbMap[$key]['horas_permiso']) : '';
                    $usados_db_keys[$key] = true;
                    $curpDb = $dbMap[$key]['CURP'] ?? '';
                    $keyDescanso = $curpDb ? ($curpDb . '||' . $fecha) : '';
                    $esDescansoProgramado = $keyDescanso && isset($descansosMap[$keyDescanso]);
                    $incidenciaExportada = limpiar($dbMap[$key]['incidencias']);
                    $incidencia = '';

                    if ($curpDb && isset($vacacionesMap[$curpDb . '||' . $fecha])) {
                        $incidencia = 'De Vacaciones';
                    } else if ($curpDb && isset($incidenciasCurpFechaMap[$curpDb . '||' . $fecha])) {
                        $incidencia = limpiar($incidenciasCurpFechaMap[$curpDb . '||' . $fecha]);
                    } else if ($incidenciaExportada !== '') {
                        $incidencia = $incidenciaExportada;
                    }

                    if ($esFestivoExcel) {
                        $resultados[] = [
                            $nombre,
                            $fecha,
                            $hora_excel,
                            $hora_db,
                            'INCIDENCIA',
                            'Día inhábil',
                            $horas_permiso,
                            $departamento,
                            $region,
                            $curpDb
                        ];
                    } else if ($incidencia) {
                        $resultados[] = [
                            $nombre,
                            $fecha,
                            $hora_excel,
                            $hora_db,
                            'INCIDENCIA',
                            $incidencia,
                            $horas_permiso,
                            $departamento,
                            $region,
                            $curpDb
                        ];
                    } else {
                        if ($esDescansoProgramado) {
                            $resultados[] = [
                                $nombre,
                                $fecha,
                                $hora_excel,
                                $hora_db,
                                'INCIDENCIA',
                                'Día de descanso',
                                $horas_permiso,
                                $departamento,
                                $region,
                                $curpDb
                            ];
                        } else
                        if ($hora_excel !== '' && $hora_db !== '') {
                            $ok = ($hora_excel <= $hora_db);
                            $resultados[] = [
                                $nombre,
                                $fecha,
                                $hora_excel,
                                $hora_db,
                                $ok ? 'OK' : 'DIFERENCIA',
                                '',
                                $horas_permiso,
                                $departamento,
                                $region,
                                $curpDb
                            ];
                        } else if ($hora_excel === '' && $hora_db !== '') {
                            $resultados[] = [
                                $nombre,
                                $fecha,
                                $hora_excel,
                                $hora_db,
                                'NO REGISTRÓ ENTRADA EN TACLOUD',
                                '',
                                $horas_permiso,
                                $departamento,
                                $region,
                                $curpDb
                            ];
                        } else {
                            $resultados[] = [
                                $nombre,
                                $fecha,
                                $hora_excel,
                                $hora_db,
                                'HORARIO NO ENCONTRADO',
                                '',
                                $horas_permiso,
                                $departamento,
                                $region,
                                $curpDb
                            ];
                        }
                    }
                } else {
                    // No hay datos en la BD, pero verificar si hay permiso
                    $resultadoTexto = ($hora_excel === '') ? 'DATOS NO ENCONTRADOS' : 'HORARIO NO ENCONTRADO';
                    $horas_permiso_no_db = '';
                    $incidencia_no_db = '';
                    $curpNoDb = isset($curpPorNombre[$nombre]) ? limpiar($curpPorNombre[$nombre]) : '';
                    if ($esFestivoExcel) {
                        $resultadoTexto = 'INCIDENCIA';
                        $incidencia_no_db = 'Día inhábil';
                    }
                    if (!$esFestivoExcel && $curpNoDb && isset($vacacionesMap[$curpNoDb . '||' . $fecha])) {
                        $resultadoTexto = 'INCIDENCIA';
                        $incidencia_no_db = 'De Vacaciones';
                    }
                    if (!$esFestivoExcel && !$incidencia_no_db && $curpNoDb && isset($incidenciasCurpFechaMap[$curpNoDb . '||' . $fecha])) {
                        $resultadoTexto = 'INCIDENCIA';
                        $incidencia_no_db = limpiar($incidenciasCurpFechaMap[$curpNoDb . '||' . $fecha]);
                    }
                    if (isset($permisosMap[$key])) {
                        $horas_permiso_no_db = $permisosMap[$key]['horas_permiso'];
                        if (!$esFestivoExcel && !$incidencia_no_db) {
                            $incidencia_no_db = $permisosMap[$key]['incidencia'];
                            if ($incidencia_no_db) {
                                $resultadoTexto = 'INCIDENCIA';
                            }
                        }
                    }
                    $resultados[] = [
                        $nombre,
                        $fecha,
                        $hora_excel,
                        '-',
                        $resultadoTexto,
                        $incidencia_no_db,
                        $horas_permiso_no_db,
                        $departamento,
                        $region,
                        $curpNoDb
                    ];
                }
            }
        }
    }
    // Agregar registros de la base de datos no encontrados en el Excel (por si hay fechas fuera del rango)
    foreach ($dbMap as $key => $d) {
        if (!isset($usados_db_keys[$key])) {
            $nombreDb = limpiar($d['nombre_completo']);
            $departamento = isset($departamentoPorNombre[$nombreDb]) ? $departamentoPorNombre[$nombreDb] : (isset($d['departamento_interno']) ? limpiar($d['departamento_interno']) : 'SIN DEPARTAMENTO');
            $region = isset($regionPorNombre[$nombreDb]) ? $regionPorNombre[$nombreDb] : (isset($d['region']) ? limpiar($d['region']) : 'SIN REGIÓN');
            $horas_permiso = isset($d['horas_permiso']) ? limpiar($d['horas_permiso']) : '';
            $curpDb = $d['CURP'] ?? '';
            $incidenciaExtra = !empty($d['incidencias']) ? limpiar($d['incidencias']) : '';
            if ($curpDb && isset($vacacionesMap[$curpDb . '||' . $d['fecha']])) {
                $incidenciaExtra = 'De Vacaciones';
            } else if ($curpDb && isset($incidenciasCurpFechaMap[$curpDb . '||' . $d['fecha']])) {
                $incidenciaExtra = limpiar($incidenciasCurpFechaMap[$curpDb . '||' . $d['fecha']]);
            }
            $resultados[] = [
                $d['nombre_completo'],
                $d['fecha'],
                '-',
                limpiar($d['hora_entrada']),
                'NO REGISTRÓ ENTRADA EN TACLOUD',
                $incidenciaExtra,
                $horas_permiso,
                $departamento,
                $region,
                $curpDb
            ];
        }
    }
    return $resultados;
}

/**
 * Ordena y filtra resultados duplicados
 * @param array $resultados Resultados a procesar
 * @return array Resultados ordenados y únicos
 */
function procesarResultados($resultados) {
    if (empty($resultados)) {
        echo '<div class="alert alert-warning">No se encontraron coincidencias para comparar.</div>';
        exit;
    }
    // Eliminar duplicados
    $agrupados = [];
    foreach ($resultados as $r) {
        $key = $r[0] . '||' . $r[1]; // nombre completo + fecha
        if (!isset($agrupados[$key])) {
            $agrupados[$key] = $r;
        }
    }
    // Ordenar por nombre y fecha
    $resultados = array_values($agrupados);
    usort($resultados, function($a, $b) {
        $cmp = strcmp($a[0], $b[0]);
        return ($cmp !== 0) ? $cmp : strcmp($a[1], $b[1]);
    });
    return $resultados;
}

/**
 * Convierte una hora a formato TIME válido o null
 * @param mixed $hora
 * @return string|null
 */
function normalizarHoraParaDb($hora) {
    $valor = limpiar($hora);
    if ($valor === '' || $valor === '-') {
        return null;
    }

    if (preg_match('/^\d{2}:\d{2}$/', $valor)) {
        return $valor . ':00';
    }

    if (preg_match('/^\d{2}:\d{2}:\d{2}$/', $valor)) {
        return $valor;
    }

    return null;
}

/**
 * Obtiene CURP con horario libre desde la fuente compartida
 * @return array<string, bool>
 */
function obtenerCurpsHorariosLibres() {
    $rutaLibres = __DIR__ . '/horarios_libres_admin.php';
    if (!file_exists($rutaLibres)) {
        return [];
    }

    if (!defined('RETURN_HORARIOS_LIBRES_CURPS')) {
        define('RETURN_HORARIOS_LIBRES_CURPS', true);
    }

    $curps = require $rutaLibres;
    if (!is_array($curps)) {
        return [];
    }

    $set = [];
    foreach ($curps as $curp) {
        $curpLimpio = strtoupper(limpiar($curp));
        if ($curpLimpio !== '') {
            $set[$curpLimpio] = true;
        }
    }

    return $set;
}

/**
 * Ajusta resultados para personal con horario libre
 * @param array $resultados
 * @param array<string, bool> $curpsHorariosLibresSet
 * @return array
 */
function aplicarReglaHorariosLibres($resultados, $curpsHorariosLibresSet) {
    if (empty($resultados) || empty($curpsHorariosLibresSet)) {
        return $resultados;
    }

    foreach ($resultados as &$r) {
        $resultadoActual = limpiar($r[4] ?? '');
        $curp = strtoupper(limpiar($r[9] ?? ''));

        if ($curp === '' || !isset($curpsHorariosLibresSet[$curp])) {
            continue;
        }

        if ($resultadoActual === 'DIFERENCIA' || strpos($resultadoActual, 'NO REGISTRÓ ENTRADA') === 0) {
            $r[4] = 'OK';
        }
    }
    unset($r);

    return $resultados;
}

/**
 * Persiste resultados en tabla temporal tmp_match_tacloud
 * @param PDO $conn
 * @param array $resultados
 * @return bool
 */
function guardarResultadosTmpMatchTacloud($conn, $resultados) {
    try {
        // TRUNCATE en MySQL realiza commit implícito, por eso se ejecuta fuera de la transacción de inserts
        $conn->exec('TRUNCATE TABLE tmp_match_tacloud');

        $filas = [];
        foreach ($resultados as $r) {
            $nombre = mb_substr(limpiar($r[0] ?? ''), 0, 100);
            $fecha = limpiar($r[1] ?? '');
            $horaTacloud = normalizarHoraParaDb($r[2] ?? null);
            $horaSistema = normalizarHoraParaDb($r[3] ?? null);
            $resultado = mb_substr(limpiar($r[4] ?? ''), 0, 50);
            $incidencia = mb_substr(limpiar($r[5] ?? ''), 0, 50);
            $horasPermisoRaw = limpiar($r[6] ?? '');
            $horasPermiso = is_numeric($horasPermisoRaw) ? (int)$horasPermisoRaw : null;
            $departamento = mb_substr(limpiar($r[7] ?? ''), 0, 100);
            $region = mb_substr(limpiar($r[8] ?? ''), 0, 50);

            if ($nombre === '' || $fecha === '') {
                continue;
            }

            $filas[] = [
                $nombre,
                $fecha,
                $horaTacloud,
                $horaSistema,
                $resultado,
                $incidencia,
                $horasPermiso,
                $departamento,
                $region
            ];
        }

        if (!empty($filas)) {
            $conn->beginTransaction();
            $tamanoLote = 500;

            for ($i = 0; $i < count($filas); $i += $tamanoLote) {
                $lote = array_slice($filas, $i, $tamanoLote);
                $placeholders = [];
                $params = [];

                foreach ($lote as $fila) {
                    $placeholders[] = '(?, ?, ?, ?, ?, ?, ?, ?, ?)';
                    foreach ($fila as $valor) {
                        $params[] = $valor;
                    }
                }

                $sqlInsert = 'INSERT INTO tmp_match_tacloud (
                    nombre_completo,
                    fecha,
                    hora_entrada_tacloud,
                    hora_entrada_sistema,
                    resultado,
                    incidencia,
                    horas_permiso,
                    departamento_interno,
                    region
                ) VALUES ' . implode(', ', $placeholders);

                $stmtInsert = $conn->prepare($sqlInsert);
                $stmtInsert->execute($params);
            }

            $conn->commit();
        }

        return true;
    } catch (Exception $e) {
        if ($conn->inTransaction()) {
            $conn->rollBack();
        }

        $logFile = __DIR__ . '/debug_comparar_excel.log';
        $errorMsg = date('Y-m-d H:i:s') . "\n";
        $errorMsg .= "Error al guardar tmp_match_tacloud: " . $e->getMessage() . "\n";
        $errorMsg .= $e->getTraceAsString() . "\n\n";
        file_put_contents($logFile, $errorMsg, FILE_APPEND);

        return false;
    }
}

// --- Proceso principal ---

// 1. Validar y cargar archivo Excel
$tmpFile = validarArchivoSubido();
$rows = cargarDatosExcel($tmpFile);

// 1.1 Filtrar los datos del Excel por el rango de fechas seleccionado (columna F)
$fecha_inicio = isset($_POST['fecha_inicio']) ? $_POST['fecha_inicio'] : '';
$fecha_fin = isset($_POST['fecha_fin']) ? $_POST['fecha_fin'] : '';
if ($fecha_inicio && $fecha_fin) {
    $rows_filtrados = [];
    foreach ($rows as $i => $row) {
        if ($i == 1) { $rows_filtrados[$i] = $row; continue; } // Mantener encabezado
        $fecha_excel = isset($row['F']) ? trim($row['F']) : '';
        if ($fecha_excel && $fecha_excel >= $fecha_inicio && $fecha_excel <= $fecha_fin) {
            $rows_filtrados[$i] = $row;
        }
    }
    $rows = $rows_filtrados;
}

// 2. Conectar a la base de datos con collation definida
$db = new Database();
$conn = $db->connect();

// Forzar UTF-8 para que los acentos se muestren bien en la web
$conn->exec("SET NAMES 'utf8'");

// 3. Obtener datos de la base de datos
$fecha_inicio = isset($_POST['fecha_inicio']) ? $_POST['fecha_inicio'] : '';
$fecha_fin = isset($_POST['fecha_fin']) ? $_POST['fecha_fin'] : '';
$dbData = obtenerDatosBaseDatos($conn, $fecha_inicio, $fecha_fin);

// 3.1 Obtener TODOS los permisos por horas en el rango de fechas (independiente de ExportacionesHorarios)
$sqlPermisos = 'SELECT p.`NOMBRE COMPLETO`, ic.CURP, ic.fechaInicio, ic.fechaFin, ic.horasPermiso, ic.Incidencia, ic.goceDeSueldo
                FROM IncidenciasCaptur ic
                INNER JOIN Plantilla p ON ic.CURP = p.`CURP`
                WHERE ic.Estatus = "Validado por RH"
                AND (ic.fechaInicio <= ? AND ic.fechaFin >= ?)';
$stmtPermisos = $conn->prepare($sqlPermisos);
$stmtPermisos->execute([$fecha_fin, $fecha_inicio]);
$permisosData = $stmtPermisos->fetchAll(PDO::FETCH_ASSOC);

// Indexar permisos por nombre+fecha
$permisosMap = [];
$incidenciasCurpFechaMap = [];
foreach ($permisosData as $permiso) {
    $nombrePermiso = limpiar($permiso['NOMBRE COMPLETO']);
    $fechaInicio = $permiso['fechaInicio'];
    $fechaFin = $permiso['fechaFin'];
    $horasPermiso = limpiar($permiso['horasPermiso']);
    $incidenciaBase = limpiar($permiso['Incidencia']);
    $goceDeSueldo = isset($permiso['goceDeSueldo']) ? limpiar($permiso['goceDeSueldo']) : '';
    $incidencia = formatearIncidenciaConGoce($incidenciaBase, $goceDeSueldo);
    $curpPermiso = '';
    if (isset($permiso['CURP'])) {
        $curpPermiso = limpiar($permiso['CURP']);
    }
    
    // Generar todas las fechas del rango del permiso
    $start = strtotime($fechaInicio);
    $end = strtotime($fechaFin);
    for ($d = $start; $d <= $end; $d = strtotime('+1 day', $d)) {
        $fechaActual = date('Y-m-d', $d);
        $key = $nombrePermiso . '||' . $fechaActual;
        $permisosMap[$key] = [
            'horas_permiso' => $horasPermiso,
            'incidencia' => $incidencia
        ];
        if ($curpPermiso && $incidencia) {
            $incidenciasCurpFechaMap[$curpPermiso . '||' . $fechaActual] = $incidencia;
        }
    }
}

// 3.1.1 Obtener vacaciones por CURP y rango de fechas para calcular "De Vacaciones" de forma independiente
$vacacionesMap = [];
$sqlVacaciones = 'SELECT CURP, `FECHA INICIAL` AS fecha_inicial, `FECHA FINAL` AS fecha_final
                  FROM VacationsHist
                  WHERE `FECHA INICIAL` <= ? AND `FECHA FINAL` >= ?';
$stmtVacaciones = $conn->prepare($sqlVacaciones);
$stmtVacaciones->execute([$fecha_fin, $fecha_inicio]);
$vacacionesData = $stmtVacaciones->fetchAll(PDO::FETCH_ASSOC);
foreach ($vacacionesData as $vac) {
    $curpVac = isset($vac['CURP']) ? limpiar($vac['CURP']) : '';
    $fechaInicioVac = isset($vac['fecha_inicial']) ? $vac['fecha_inicial'] : '';
    $fechaFinVac = isset($vac['fecha_final']) ? $vac['fecha_final'] : '';
    if (!$curpVac || !$fechaInicioVac || !$fechaFinVac) {
        continue;
    }
    $startVac = strtotime($fechaInicioVac);
    $endVac = strtotime($fechaFinVac);
    if ($startVac === false || $endVac === false) {
        continue;
    }
    for ($d = $startVac; $d <= $endVac; $d = strtotime('+1 day', $d)) {
        $fechaActual = date('Y-m-d', $d);
        if ($fechaActual < $fecha_inicio || $fechaActual > $fecha_fin) {
            continue;
        }
        $vacacionesMap[$curpVac . '||' . $fechaActual] = true;
    }
}

// 3.2 Obtener descansos programados (DiasDescanso) para todo el personal
$descansosMap = [];
$sqlDescMatriz = '
    SELECT p.CURP, d.fecha_descanso
    FROM DiasDescanso d
    JOIN HorariosAsignados h ON d.id_horario = h.id_horario
    JOIN Plantilla p ON h.curp = p.CURP
    WHERE d.fecha_descanso BETWEEN ? AND ?
';
$stmtDescM = $conn->prepare($sqlDescMatriz);
$stmtDescM->execute([$fecha_inicio, $fecha_fin]);
foreach ($stmtDescM->fetchAll(PDO::FETCH_ASSOC) as $row) {
    $curp = limpiar($row['CURP']);
    $fechaDesc = limpiar($row['fecha_descanso']);
    if ($curp && $fechaDesc) {
        $descansosMap[$curp . '||' . $fechaDesc] = true;
    }
}

// 2.1 Obtener mapeo de nombre a departamento y región desde Plantilla y PDVXRegion
$departamentoPorNombre = [];
$regionPorNombre = [];
$curpPorNombre = [];
$sqlPlantilla = 'SELECT p.`NOMBRE COMPLETO`, p.`CURP`, p.`DEPARTAMENTO INTERNO`, r.`Region` FROM Plantilla p LEFT JOIN PDVXRegion r ON TRIM(LOWER(p.`DEPARTAMENTO INTERNO`)) COLLATE latin1_general_ci = TRIM(LOWER(r.`Tienda`)) COLLATE latin1_general_ci';
$stmtPlantilla = $conn->prepare($sqlPlantilla);
$stmtPlantilla->execute();
$plantillaRows = $stmtPlantilla->fetchAll(PDO::FETCH_ASSOC);
foreach ($plantillaRows as $row) {
    $nombre = limpiar($row['NOMBRE COMPLETO']);
    $curp = isset($row['CURP']) ? limpiar($row['CURP']) : '';
    $departamento = limpiar($row['DEPARTAMENTO INTERNO']);
    $region = limpiar($row['Region']);
    if ($nombre && $departamento) {
        $departamentoPorNombre[$nombre] = $departamento;
        if ($region) {
            $regionPorNombre[$nombre] = $region;
        }
    }
    if ($nombre && $curp) {
        $curpPorNombre[$nombre] = $curp;
    }
}

// 2.2 Para Región Matriz (GRP TDA ADMINISTRACION MER), usar áreas_admin como departamento
$sqlAreasMatriz = '
    SELECT p.`NOMBRE COMPLETO`, p.`CURP`, a.area
    FROM miembros_area_admin ma
    JOIN areas_admin a ON a.id = ma.area_id
    JOIN Plantilla p ON ma.curp = p.CURP
    WHERE p.`DEPARTAMENTO INTERNO` = "GRP TDA ADMINISTRACION MER"
';
$stmtAreas = $conn->prepare($sqlAreasMatriz);
$stmtAreas->execute();
$areasRows = $stmtAreas->fetchAll(PDO::FETCH_ASSOC);
foreach ($areasRows as $row) {
    $nombre = limpiar($row['NOMBRE COMPLETO']);
    $curp = isset($row['CURP']) ? limpiar($row['CURP']) : '';
    $departamento = limpiar($row['area']);
    if ($nombre && $departamento) {
        $departamentoPorNombre[$nombre] = $departamento;
        $regionPorNombre[$nombre] = 'Matriz';
    }
    if ($nombre && $curp) {
        $curpPorNombre[$nombre] = $curp;
    }
}

// 4. Comparar datos (pasando el mapeo de departamento, región y permisos)
$resultados = compararDatos(
    $rows,
    $dbData,
    $departamentoPorNombre,
    $regionPorNombre,
    $permisosMap,
    $descansosMap,
    $curpPorNombre,
    $vacacionesMap,
    $incidenciasCurpFechaMap
);
$resultados = procesarResultados($resultados);

// Ajustar resultado para personal con horario libre antes de mostrar y guardar en temporal
$curpsHorariosLibresSet = obtenerCurpsHorariosLibres();
$resultados = aplicarReglaHorariosLibres($resultados, $curpsHorariosLibresSet);

// Guardar siempre la última comparación realizada en tabla temporal
$guardadoTmpOk = guardarResultadosTmpMatchTacloud($conn, $resultados);

// Calcular el periodo real encontrado en el archivo Excel (columna F)
$fechas_excel = [];
foreach ($rows as $i => $row) {
    if ($i == 1) continue; // Saltar encabezado
    $fecha_excel = isset($row['F']) ? trim($row['F']) : '';
    if ($fecha_excel) $fechas_excel[] = $fecha_excel;
}
$periodo_excel_min = $fechas_excel ? min($fechas_excel) : '';
$periodo_excel_max = $fechas_excel ? max($fechas_excel) : '';

// 5. Mostrar resultados
?>
<!-- Div oculto para el frontend con el periodo encontrado en el Excel -->
<div id="periodoExcelInfo" data-inicio="<?php echo htmlspecialchars($periodo_excel_min); ?>" data-fin="<?php echo htmlspecialchars($periodo_excel_max); ?>" style="display:none"></div>

<style>
.table-responsive {
    max-height: 500px;
    overflow-y: auto;
}
table th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: #f8f9fa;
}
.search-box {
    margin-bottom: 10px;
}
td, th {
    padding-top: 0.75rem !important;
    padding-bottom: 0.75rem !important;
}
</style>

<?php if (!$guardadoTmpOk): ?>
<div class="alert alert-warning">
    Se mostró la comparación, pero no se pudo guardar temporalmente el resultado.
</div>
<?php endif; ?>

<div class="table-responsive">
    <table class="table table-bordered table-sm align-middle" id="tablaResultados">
        <thead class="table-light">
            <tr>
                <th>Nombre completo</th>
                <th style="min-width: 130px; width: 160px;">Fecha</th>
                <th style="min-width: 90px; width: 110px;">Hora entrada (Tacloud)</th>
                <th>Hora entrada (Sistema)</th>
                <th>Resultado</th>
                <th>Incidencia</th>
                <th>Horas permiso</th>
                <th>Departamento interno</th>
                <th>Región</th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($resultados as $r): ?>
            <tr data-curp="<?php echo htmlspecialchars($r[9] ?? ''); ?>" class="<?php
                if ($r[4] === 'OK') echo 'table-success';
                else if ($r[4] === 'DIFERENCIA') echo 'table-warning';
                else if ($r[4] === 'INCIDENCIA') echo 'table-primary';
                else if ($r[4] === 'NO REGISTRÓ ENTRADA EN TACLOUD') echo 'table-secondary';
                else if ($r[4] === 'DATOS NO ENCONTRADOS') echo 'table-info';
                else echo 'table-danger';
            ?>">
                <td><?php echo htmlspecialchars($r[0]); ?></td>
                <td><?php echo htmlspecialchars($r[1]); ?></td>
                <td><?php echo htmlspecialchars($r[2]); ?></td>
                <td><?php echo htmlspecialchars($r[3]); ?></td>
                <td><?php echo htmlspecialchars($r[4]); ?></td>
                <td><?php echo htmlspecialchars($r[5]); ?></td>
                <td><?php echo htmlspecialchars($r[6]); ?></td>
                <td><?php echo htmlspecialchars($r[7]); ?></td>
                <td><?php echo htmlspecialchars($r[8]); ?></td>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
</div>

<script>
document.addEventListener('DOMContentLoaded', function() {
    // Agregar funcionalidad de búsqueda si hay un elemento con id 'searchNombre'
    var searchInput = document.getElementById('searchNombre');
    if (searchInput) {
        searchInput.addEventListener('input', function() {
            var filtro = this.value.toLowerCase();
            var tabla = document.getElementById('tablaResultados');
            var filas = tabla.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
            
            for (var i = 0; i < filas.length; i++) {
                var nombre = filas[i].getElementsByTagName('td')[0];
                if (nombre) {
                    var texto = nombre.textContent || nombre.innerText;
                    filas[i].style.display = texto.toLowerCase().indexOf(filtro) > -1 ? '' : 'none';
                }
            }
        });
    }
});
</script>