const { Op } = require("sequelize");
const moment = require('moment-timezone');
const Asistencia = require('../models/asistencia');
const Usuario = require('../models/usuario');
const UsuarioHorario = require('../models/usuario_horario');
const Horario = require('../models/horario');
const TramoHorario = require('../models/tramo_horario');
const Historial_Empresa = require('../models/historial_empresa');
const Cargo = require('../models/cargo');
const Area = require('../models/area');

function validarIPEmpresa(ip) {
    const ipsAutorizadas = [
        '186.3.20.249',    // Base Producción CompanyPCA
        '181.199.77.234',  // Oficina PCA
        '181.39.208.51',   // Remanufactura
    ];

    if (ipsAutorizadas.includes(ip)) {
        return { valido: true, mensaje: 'Sucursal autorizada' };
    }

    return {
        valido: false,
        mensaje: 'No se puede realizar la marcación fuera de un establecimiento de CompanyPCA'
    };
}

// Registrar asistencia
async function registrarAsistencia(req, res) {
    try {
        let params = req.body;

        // Obtener IP - si viene del proxy local usar socket, si no usar x-forwarded-for
        let ipDirecta = req.socket?.remoteAddress || req.connection?.remoteAddress || '';
        if (ipDirecta.includes('::ffff:')) ipDirecta = ipDirecta.split('::ffff:')[1];

        let ipForwarded = req.headers['x-forwarded-for'] || '';
        if (ipForwarded.includes(',')) ipForwarded = ipForwarded.split(',')[0].trim();

        // Si viene del proxy (nginx/producción), la IP real está en x-forwarded-for
        let ipPublica = ipForwarded || ipDirecta;
        console.log('IP detectada:', ipPublica);

        // Validar IP
        const validacionIP = validarIPEmpresa(ipPublica);
        if (!validacionIP.valido) {
            return res.status(403).send({
                message: validacionIP.mensaje,
                errorTipo: 'IP_NO_VALIDA'
            });
        }

        // Obtener todas las marcaciones del día
        const fechaHoy = moment().tz('America/Guayaquil').format('YYYY-MM-DD');

        let marcacionesHoy = await Asistencia.findAll({
            where: {
                ID_USUARIO: params.ID_USUARIO,
                [Op.and]: [
                    Asistencia.sequelize.where(
                        Asistencia.sequelize.fn('DATE', Asistencia.sequelize.col('FECHA_REGISTRO')),
                        fechaHoy
                    )
                ],
                ESTADO: true
            },
            order: [['FECHA_REGISTRO', 'ASC']]
        });

        // Determinar si es Remanofactura por IP
        const esRemanofactura = ipPublica === '181.39.208.51';

        // Validar máximo de marcaciones por día
        if (marcacionesHoy.length >= 4) {
            return res.status(400).send({
                message: 'Ya alcanzaste el máximo de marcaciones por día',
                errorTipo: 'MAXIMO_MARCACIONES'
            });
        }

        let tipo_marcacion;

        if (esRemanofactura) {
            // Remanofactura: 4 tarjetas en secuencia fija
            const secuencia = ['ENTRADA', 'ALMUERZO_SALIDA', 'ALMUERZO_ENTRADA', 'SALIDA'];
            tipo_marcacion = secuencia[marcacionesHoy.length];
            if (!tipo_marcacion) {
                return res.status(400).send({
                    message: 'Ya alcanzaste el máximo de marcaciones por día',
                    errorTipo: 'MAXIMO_MARCACIONES'
                });
            }
        } else {
            const tipoExplicito = params.TIPO_MARCACION;

            if (tipoExplicito && ['ALMUERZO_SALIDA', 'ALMUERZO_ENTRADA'].includes(tipoExplicito)) {
                // Checkbox de almuerzo opcional
                const yaRegistrado = marcacionesHoy.some(m => m.TIPO_MARCACION === tipoExplicito);
                if (yaRegistrado) {
                    return res.status(400).send({
                        message: `Ya registraste ${tipoExplicito.replace(/_/g, ' ').toLowerCase()} hoy`,
                        errorTipo: 'TIPO_YA_REGISTRADO'
                    });
                }
                tipo_marcacion = tipoExplicito;
            } else {
                // Auto ENTRADA/SALIDA (botón principal)
                const entradas = marcacionesHoy.filter(m => m.TIPO_MARCACION === 'ENTRADA').length;
                const salidas  = marcacionesHoy.filter(m => m.TIPO_MARCACION === 'SALIDA').length;
                tipo_marcacion = entradas > salidas ? 'SALIDA' : 'ENTRADA';

                if (tipo_marcacion === 'ENTRADA' && entradas >= 1) {
                    return res.status(400).send({
                        message: 'Ya registraste tu entrada del día',
                        errorTipo: 'MAXIMO_ENTRADAS'
                    });
                }
                if (tipo_marcacion === 'SALIDA' && salidas >= 1) {
                    return res.status(400).send({
                        message: 'Ya registraste tu salida del día',
                        errorTipo: 'MAXIMO_SALIDAS'
                    });
                }
            }
        }

        // Buscar horario activo del usuario CON sus tramos
        let idHorario = null;
        let horarioCompleto = null;

        try {
            const usuarioHorarioActivo = await UsuarioHorario.findOne({
                where: {
                    ID_USUARIO: params.ID_USUARIO,
                    ESTADO: 1
                },
                include: [
                    {
                        model: Horario,
                        as: 'HORARIO',
                        required: true,
                        include: [
                            {
                                model: TramoHorario,
                                as: 'TRAMOS',
                                required: false
                            }
                        ]
                    }
                ],
                order: [['FECHA_REGISTRO', 'DESC']]
            });

            if (usuarioHorarioActivo) {
                idHorario = usuarioHorarioActivo.ID_HORARIO;
                horarioCompleto = usuarioHorarioActivo.HORARIO;
                console.log('Horario activo encontrado:', idHorario);
            } else {
                console.log('Usuario sin horario asignado');
            }
        } catch (error) {
            console.log('Error al buscar horario activo:', error);
            // Continuar sin horario
        }

        // VALIDACIÃ“N CRÃTICA: Verificar que estÃ© marcando dentro del rango de horario
        if (horarioCompleto && horarioCompleto.TRAMOS && horarioCompleto.TRAMOS.length > 0) {
            const horaActual = moment().tz('America/Guayaquil');
            const margenToleranciaMinutos = 120; // 2 horas de tolerancia
            let dentroDeRango = false;

            // Verificar cada tramo del horario
            for (const tramo of horarioCompleto.TRAMOS) {
                // Parsear horas del tramo
                const horaInicio = moment(tramo.HORA_INICIO, 'HH:mm:ss').subtract(margenToleranciaMinutos, 'minutes');
                const horaFin = moment(tramo.HORA_FIN, 'HH:mm:ss').add(margenToleranciaMinutos, 'minutes');

                // Ajustar la hora actual solo con HH:mm:ss para comparar
                const horaActualSolo = moment(horaActual.format('HH:mm:ss'), 'HH:mm:ss');

                // Verificar si estÃ¡ dentro del rango con tolerancia
                if (horaActualSolo.isBetween(horaInicio, horaFin, null, '[]')) {
                    dentroDeRango = true;
                    break;
                }
            }

            // Si NO estÃ¡ dentro del rango, BLOQUEAR la marcaciÃ³n
            if (!dentroDeRango) {
                const horasPermitidas = horarioCompleto.TRAMOS.map(t =>
                    `${t.HORA_INICIO.substring(0, 5)} - ${t.HORA_FIN.substring(0, 5)}`
                ).join(' y ');

                return res.status(400).send({
                    message: `Solo puede marcar asistencia dentro de su horario asignado: ${horasPermitidas}`,
                    errorTipo: 'FUERA_DE_HORARIO',
                    horariosPermitidos: horasPermitidas
                });
            }
        }

        // Registrar asistencia
        let asistencia = Asistencia.build();
        asistencia.ID_USUARIO = params.ID_USUARIO;
        asistencia.ID_HORARIO = idHorario;
        asistencia.TIPO_MARCACION = tipo_marcacion;
        asistencia.IP_REGISTRO = ipPublica;
        asistencia.FECHA_REGISTRO = moment().tz('America/Guayaquil').format('YYYY-MM-DD HH:mm:ss');
        asistencia.ESTADO = true;

        let asistenciaGuardada = await asistencia.save();

        if (asistenciaGuardada) {
            res.status(200).send({
                message: 'Registrado exitosamente',
                data: asistenciaGuardada
            });
        } else {
            res.status(402).send({
                message: 'No se ha podido registrar sus datos intenta nuevamente'
            });
        }

    } catch (e) {
        console.log(e);
        res.status(500).send({
            message: 'Error al registrar asistencia'
        });
    }
}

// Obtener asistencias con paginaciÃ³n
async function obtenerAsistencias(req, res) {
    try {
        let buscar = req.body.palabra || '';
        let limit = req.body.tamanioLote;
        let offset = req.body.numeroPagina;
        let mul = limit * offset;
        if (mul == 0) { mul = offset }

        let asistenciasEncontradas = await Asistencia.findAll({
            where: {
                [Op.and]: [
                    { ID_USUARIO: req.params.idUsuario },
                    { ESTADO: true },
                    {
                        [Op.or]: [
                            { TIPO_MARCACION: { [Op.substring]: buscar } },
                            { IP_REGISTRO: { [Op.substring]: buscar } }
                        ]
                    }
                ]
            },
            offset: mul,
            limit: limit,
            order: [['FECHA_REGISTRO', 'DESC']]
        });

        let asistenciasFormateadas = asistenciasEncontradas.map(asistencia => {
            let asistenciaObj = asistencia.toJSON();
            asistenciaObj.FECHA_REGISTRO = moment(asistenciaObj.FECHA_REGISTRO)
                .tz('America/Guayaquil')
                .format('DD/MM/YYYY HH:mm');
            return asistenciaObj;
        });

        res.status(200).send({
            data: asistenciasFormateadas
        });

    } catch (e) {
        console.log(e);
        res.status(500).send({
            message: e.name
        });
    }
}

// Contar total de asistencias
async function contarTotalAsistencias(req, res) {
    try {
        let buscar = req.body.palabra || '';

        let contadorAsistencias = await Asistencia.count({
            where: {
                [Op.and]: [
                    { ID_USUARIO: req.params.idUsuario },
                    { ESTADO: true },
                    {
                        [Op.or]: [
                            { TIPO_MARCACION: { [Op.substring]: buscar } },
                            { IP_REGISTRO: { [Op.substring]: buscar } }
                        ]
                    }
                ]
            }
        });

        res.status(200).send({
            data: contadorAsistencias
        });

    } catch (e) {
        console.log(e);
        res.status(500).send({
            message: e.name
        });
    }
}

// Obtener marcaciones del dÃ­a actual
async function obtenerMarcaciones(req, res) {
    try {
        const fechaHoy = moment().tz('America/Guayaquil').format('YYYY-MM-DD');

        const marcacionesEncontradas = await Asistencia.findAll({
            where: { 
                ID_USUARIO: req.params.idUsuario,
                FECHA_REGISTRO: {
                    [Op.like]: `${fechaHoy}%`
                },
                ESTADO: true
            },
            order: [['FECHA_REGISTRO', 'ASC']]
        });

        const marcacionesFormateadas = marcacionesEncontradas.map(marcacion => {
            const marcacionObj = marcacion.toJSON();
            const fecha = moment(marcacionObj.FECHA_REGISTRO).add(5, 'hours');

            return {
                ...marcacionObj,
                FECHA_MARCACION: fecha.format('YYYY-MM-DD'),
                HORA_MARCACION: fecha.format('HH:mm:ss'),
                FECHA_FORMATEADA: fecha.format('DD/MM/YYYY'),
                HORA_FORMATEADA: fecha.format('HH:mm:ss')
            };
        });

        res.status(200).send({
            data: marcacionesFormateadas,
        });

    } catch (e) {
        console.log(e);
        res.status(500).send({
            message: e.name
        });
    }
}

// Obtener IP pÃºblica
function obtenerIPPublica(req, res) {
    try {
        const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
        const ipLimpia = ip.replace('::ffff:', '');
        res.status(200).send({
            data: ipLimpia,
            esRemanofactura: ipLimpia === '181.39.208.51'
        });
    }
    catch (e) {
        console.log(e);
        res.status(500).send({
            message: e.name
        });
    }
}

// Obtener reporte de marcaciones
async function obtenerReporteMarcaciones(req, res) {
    try {
        let params = req.body;
        let idUsuario = req.body.idUsuario || null;
        let mostrarSinRegistrar = req.body.mostrarSinRegistrar || false; // NUEVO PARÃMETRO
        let limit = req.body.tamanioLote;
        let offset = req.body.numeroPagina;
        let mul = limit * offset;
        if (mul == 0) { mul = offset }

        // Configurar rango de fechas
        let fechaDesde, fechaHasta;

        if (params.fechaDesde && params.fechaHasta) {
            fechaDesde = moment(params.fechaDesde).tz('America/Guayaquil').startOf('day');
            fechaHasta = moment(params.fechaHasta).tz('America/Guayaquil').endOf('day');
        } else {
            fechaDesde = moment().tz('America/Guayaquil').startOf('month');
            fechaHasta = moment().tz('America/Guayaquil').endOf('day');
        }

        // 1. Obtener marcaciones existentes
        let whereAsistencia = {
            ESTADO: true,
            FECHA_REGISTRO: {
                [Op.between]: [fechaDesde.toDate(), fechaHasta.toDate()]
            }
        };

        if (idUsuario) {
            whereAsistencia.ID_USUARIO = idUsuario;
        }

        let marcacionesEncontradas = await Asistencia.findAll({
            where: whereAsistencia,
            include: [
                {
                    model: Usuario,
                    as: 'USUARIO',
                    attributes: ['NOMBRES', 'APELLIDOS', 'IDENTIFICACION', 'ID_USUARIO'],
                    required: true
                },
                {
                    model: Horario,
                    as: 'HORARIO',
                    attributes: ['NOMBRE_HORARIO'],
                    required: false
                }
            ],
            order: [['FECHA_REGISTRO', 'DESC']]
        });

        let registrosCompletos = [];

        // Si el filtro "sin registro" estÃ¡ activo, SOLO mostrar NO REGISTRADO
        if (mostrarSinRegistrar && params.fechaDesde && params.fechaHasta) {
            // Obtener usuarios activos (con horario)
            const usuariosConHorario = await UsuarioHorario.findAll({
                where: { ESTADO: 1 },
                include: [
                    {
                        model: Usuario,
                        as: 'USUARIO',
                        attributes: ['NOMBRES', 'APELLIDOS', 'IDENTIFICACION', 'ID_USUARIO'],
                        required: true,
                        where: { ESTADO: 1 }
                    },
                    {
                        model: Horario,
                        as: 'HORARIO',
                        attributes: ['NOMBRE_HORARIO'],
                        required: true
                    }
                ]
            });

            let usuariosFiltrados = usuariosConHorario;
            if (idUsuario) {
                usuariosFiltrados = usuariosConHorario.filter(uh => uh.ID_USUARIO === idUsuario);
            }

            const diasEnRango = fechaHasta.diff(fechaDesde, 'days') + 1;

            for (const usuarioHorario of usuariosFiltrados) {
                const usuario = usuarioHorario.USUARIO;

                for (let i = 0; i < diasEnRango; i++) {
                    const diaActual = moment(fechaDesde).add(i, 'days');
                    const fechaDia = diaActual.format('YYYY-MM-DD');

                    const tieneMarcaciones = marcacionesEncontradas.some(m =>
                        m.ID_USUARIO === usuario.ID_USUARIO &&
                        moment(m.FECHA_REGISTRO).format('YYYY-MM-DD') === fechaDia
                    );

                    if (!tieneMarcaciones) {
                        registrosCompletos.push({
                            ID_ASISTENCIA: null,
                            ID_USUARIO: usuario.ID_USUARIO,
                            NOMBRE_COMPLETO: `${usuario.NOMBRES} ${usuario.APELLIDOS}`,
                            CEDULA: usuario.IDENTIFICACION,
                            TIPO_MARCACION: 'NO REGISTRADO',
                            FECHA_REGISTRO: diaActual.format('DD/MM/YYYY'),
                            HORA_REGISTRO: '--:--:--',
                            HORARIO: usuarioHorario.HORARIO ? usuarioHorario.HORARIO.NOMBRE_HORARIO : 'Sin horario',
                            ESTADO_REGISTRO: 'NO_REGISTRADO'
                        });
                    }
                }
            }
        } else {
            // Si NO estÃ¡ activo el filtro, mostrar marcaciones normales
            marcacionesEncontradas.forEach(marcacion => {
                let marcacionObj = marcacion.toJSON();
                const fechaMoment = moment(marcacionObj.FECHA_REGISTRO).add(5, 'hours');

                registrosCompletos.push({
                    ID_ASISTENCIA: marcacionObj.ID_ASISTENCIA,
                    ID_USUARIO: marcacionObj.ID_USUARIO,
                    NOMBRE_COMPLETO: `${marcacionObj.USUARIO.NOMBRES} ${marcacionObj.USUARIO.APELLIDOS}`,
                    CEDULA: marcacionObj.USUARIO.IDENTIFICACION,
                    TIPO_MARCACION: marcacionObj.TIPO_MARCACION,
                    FECHA_REGISTRO: fechaMoment.format('DD/MM/YYYY'),
                    HORA_REGISTRO: fechaMoment.format('HH:mm:ss'),
                    HORARIO: marcacionObj.HORARIO ? marcacionObj.HORARIO.NOMBRE_HORARIO : 'Sin horario',
                    ESTADO_REGISTRO: 'REGISTRADO'
                });
            });
        }

        // Ordenar por fecha desc
        registrosCompletos.sort((a, b) => {
            const fechaA = moment(a.FECHA_REGISTRO, 'DD/MM/YYYY');
            const fechaB = moment(b.FECHA_REGISTRO, 'DD/MM/YYYY');
            return fechaB.diff(fechaA);
        });

        // Aplicar paginaciÃ³n
        const registrosPaginados = registrosCompletos.slice(mul, mul + limit);

        res.status(200).send({
            data: registrosPaginados,
            total: registrosCompletos.length
        });

    } catch (e) {
        console.log(e);
        res.status(500).send({
            message: e.name
        });
    }
}


// Contar total de marcaciones para reporte
async function contarTotalMarcacionesReporte(req, res) {
    try {
        let params = req.body;
        let idUsuario = req.body.idUsuario || null;
        let mostrarSinRegistrar = req.body.mostrarSinRegistrar || false; // NUEVO PARÃMETRO

        // Configurar rango de fechas
        let fechaDesde, fechaHasta;

        if (params.fechaDesde && params.fechaHasta) {
            fechaDesde = moment(params.fechaDesde).tz('America/Guayaquil').startOf('day');
            fechaHasta = moment(params.fechaHasta).tz('America/Guayaquil').endOf('day');
        } else {
            fechaDesde = moment().tz('America/Guayaquil').startOf('month');
            fechaHasta = moment().tz('America/Guayaquil').endOf('day');
        }

        // Contar marcaciones existentes
        let whereAsistencia = {
            ESTADO: true,
            FECHA_REGISTRO: {
                [Op.between]: [fechaDesde.toDate(), fechaHasta.toDate()]
            }
        };

        if (idUsuario) {
            whereAsistencia.ID_USUARIO = idUsuario;
        }

        let totalRegistros = 0;

        // Si el filtro estÃ¡ activo, SOLO contar NO REGISTRADO
        if (mostrarSinRegistrar && params.fechaDesde && params.fechaHasta) {
            const usuariosConHorario = await UsuarioHorario.findAll({
                where: { ESTADO: 1 },
                include: [
                    {
                        model: Usuario,
                        as: 'USUARIO',
                        required: true,
                        where: { ESTADO: 1 }
                    }
                ]
            });

            let usuariosFiltrados = usuariosConHorario;
            if (idUsuario) {
                usuariosFiltrados = usuariosConHorario.filter(uh => uh.ID_USUARIO === idUsuario);
            }

            const diasEnRango = fechaHasta.diff(fechaDesde, 'days') + 1;

            const todasMarcaciones = await Asistencia.findAll({
                where: whereAsistencia,
                attributes: ['ID_USUARIO', 'FECHA_REGISTRO']
            });

            for (const usuarioHorario of usuariosFiltrados) {
                for (let i = 0; i < diasEnRango; i++) {
                    const diaActual = moment(fechaDesde).add(i, 'days');
                    const fechaDia = diaActual.format('YYYY-MM-DD');

                    const tieneMarcaciones = todasMarcaciones.some(m =>
                        m.ID_USUARIO === usuarioHorario.ID_USUARIO &&
                        moment(m.FECHA_REGISTRO).format('YYYY-MM-DD') === fechaDia
                    );

                    if (!tieneMarcaciones) {
                        totalRegistros++;
                    }
                }
            }
        } else {
            // Si NO estÃ¡ activo, contar marcaciones normales
            totalRegistros = await Asistencia.count({
                where: whereAsistencia,
                distinct: true
            });
        }

        res.status(200).send({
            data: totalRegistros
        });

    } catch (e) {
        console.log(e);
        res.status(500).send({
            message: e.name
        });
    }
}

const IP_ORIGENES = {
    '186.3.20.249': 'PRODUCCIÓN',
    '181.199.77.234': 'OFICINA',
    '181.39.208.51': 'REMANOFACTURA'
};
const ORIGEN_IP = {
    'PRODUCCIÓN': '186.3.20.249',
    'OFICINA': '181.199.77.234',
    'REMANOFACTURA': '181.39.208.51'
};
const DIAS_SEMANA = ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'];

// Reporte agrupado por usuario y día
async function obtenerReporteAgrupado(req, res) {
    try {
        const params = req.body;
        const idUsuario = req.body.idUsuario || null;
        const origenFiltro = req.body.origen || null;   // 'PRODUCCIÓN' | 'OFICINA' | 'REMANOFACTURA'
        const estadoFiltro = req.body.estado || null;   // 'completas' | 'incompletas'
        const idAreaFiltro = req.body.idArea ? Number(req.body.idArea) : null;
        const limit = req.body.tamanioLote;
        const offset = req.body.numeroPagina;
        let mul = limit * offset;
        if (mul == 0) { mul = offset; }

        let fechaDesde, fechaHasta;
        if (params.fechaDesde && params.fechaHasta) {
            fechaDesde = moment(params.fechaDesde).tz('America/Guayaquil').startOf('day');
            fechaHasta = moment(params.fechaHasta).tz('America/Guayaquil').endOf('day');
        } else {
            fechaDesde = moment().tz('America/Guayaquil').startOf('month');
            fechaHasta = moment().tz('America/Guayaquil').endOf('day');
        }

        const whereAsistencia = {
            ESTADO: true,
            FECHA_REGISTRO: { [Op.between]: [fechaDesde.toDate(), fechaHasta.toDate()] }
        };
        if (idUsuario) whereAsistencia.ID_USUARIO = idUsuario;
        if (origenFiltro && ORIGEN_IP[origenFiltro]) whereAsistencia.IP_REGISTRO = ORIGEN_IP[origenFiltro];

        const marcaciones = await Asistencia.findAll({
            where: whereAsistencia,
            attributes: ['ID_ASISTENCIA', 'ID_USUARIO', 'TIPO_MARCACION', 'FECHA_REGISTRO', 'IP_REGISTRO'],
            include: [{
                model: Usuario,
                as: 'USUARIO',
                attributes: ['NOMBRES', 'APELLIDOS', 'IDENTIFICACION', 'ID_USUARIO'],
                required: true
            }],
            order: [['ID_USUARIO', 'ASC'], ['FECHA_REGISTRO', 'ASC']]
        });

        // Construir mapa de áreas por usuario
        const areaMap = new Map();
        try {
            const historiales = await Historial_Empresa.findAll({
                where: { ESTADO: true },
                attributes: ['ID_USUARIO'],
                include: [{
                    model: Cargo,
                    attributes: ['ID_CARGO'],
                    include: [{ model: Area, attributes: ['ID_AREA', 'DESCRIPCION'] }],
                    required: true
                }]
            });
            historiales.forEach(h => {
                const hj = h.toJSON();
                if (!areaMap.has(hj.ID_USUARIO) && hj.CARGO && hj.CARGO.AREA) {
                    areaMap.set(hj.ID_USUARIO, { id: hj.CARGO.AREA.ID_AREA, nombre: hj.CARGO.AREA.DESCRIPCION });
                }
            });
        } catch (errArea) {
            console.log('Error cargando áreas:', errArea.message);
        }

        // Agrupar por ID_USUARIO + fecha (hora Ecuador)
        const grupos = new Map();
        marcaciones.forEach(m => {
            const mObj = m.toJSON();
            const fechaEcuador = moment(mObj.FECHA_REGISTRO).add(5, 'hours');
            const fechaStr = fechaEcuador.format('YYYY-MM-DD');
            const clave = `${mObj.ID_USUARIO}_${fechaStr}`;

            if (!grupos.has(clave)) {
                grupos.set(clave, {
                    ID_USUARIO: mObj.ID_USUARIO,
                    NOMBRE_COMPLETO: `${mObj.USUARIO.NOMBRES} ${mObj.USUARIO.APELLIDOS}`,
                    CEDULA: mObj.USUARIO.IDENTIFICACION,
                    FECHA: fechaStr,
                    FECHA_DISPLAY: fechaEcuador.format('DD/MM/YYYY'),
                    IP: mObj.IP_REGISTRO,
                    marcaciones: []
                });
            }
            grupos.get(clave).marcaciones.push(mObj);
        });

        // Procesar cada grupo
        const registros = [];
        grupos.forEach(grupo => {
            const origen = IP_ORIGENES[grupo.IP] || 'DESCONOCIDO';
            const diaSemana = DIAS_SEMANA[moment(grupo.FECHA, 'YYYY-MM-DD').day()];

            const sorted = [...grupo.marcaciones].sort((a, b) =>
                moment(a.FECHA_REGISTRO).diff(moment(b.FECHA_REGISTRO))
            );

            // Buscar por tipo explícito (nuevo formato) y por posición (compatibilidad)
            const mEntrada       = sorted.find(m => m.TIPO_MARCACION === 'ENTRADA');
            const mSalidaAlm     = sorted.find(m => m.TIPO_MARCACION === 'ALMUERZO_SALIDA');
            const mEntradaAlm    = sorted.find(m => m.TIPO_MARCACION === 'ALMUERZO_ENTRADA');
            const mSalida        = sorted.find(m => m.TIPO_MARCACION === 'SALIDA');

            const fmtHora = (m) => m
                ? moment(m.FECHA_REGISTRO).add(5, 'hours').format('HH:mm:ss')
                : null;

            let hora_entrada    = fmtHora(mEntrada);
            let hora_salida     = fmtHora(mSalida);
            let salida_almuerzo = fmtHora(mSalidaAlm);
            let entrada_almuerzo = fmtHora(mEntradaAlm);
            let tiempo_total    = null;

            // Compatibilidad datos viejos formato 4 marcas ENTRADA/SALIDA/ENTRADA/SALIDA
            // (sin tipos de almuerzo explícitos, independiente del IP registrado)
            if (!mSalidaAlm && !mEntradaAlm) {
                const entradasViejas = sorted.filter(m => m.TIPO_MARCACION === 'ENTRADA');
                const salidasViejas  = sorted.filter(m => m.TIPO_MARCACION === 'SALIDA');
                if (entradasViejas.length >= 2 || salidasViejas.length >= 2) {
                    salida_almuerzo  = fmtHora(salidasViejas[0]);
                    entrada_almuerzo = fmtHora(entradasViejas[1]);
                    hora_salida      = salidasViejas.length > 1 ? fmtHora(salidasViejas[salidasViejas.length - 1]) : null;
                }
            }

            // Calcular tiempo total
            const mEntradaRef = mEntrada;
            const mSalidaRef  = mSalidaAlm && mEntradaAlm
                ? sorted.find(m => m.TIPO_MARCACION === 'SALIDA')    // nuevo formato: última SALIDA
                : (sorted.filter(m => m.TIPO_MARCACION === 'SALIDA').slice(-1)[0] || mSalida); // viejo: última SALIDA

            if (mEntradaRef && mSalidaRef) {
                let totalMin = moment(mSalidaRef.FECHA_REGISTRO).diff(moment(mEntradaRef.FECHA_REGISTRO), 'minutes');
                if (mSalidaAlm && mEntradaAlm) {
                    const almuerzoMin = moment(mEntradaAlm.FECHA_REGISTRO).diff(moment(mSalidaAlm.FECHA_REGISTRO), 'minutes');
                    if (almuerzoMin > 0) totalMin -= almuerzoMin;
                } else {
                    // Viejo formato: descontar tiempo entre salidasViejas[0] y entradasViejas[1]
                    const salidasViejas  = sorted.filter(m => m.TIPO_MARCACION === 'SALIDA');
                    const entradasViejas = sorted.filter(m => m.TIPO_MARCACION === 'ENTRADA');
                    if (salidasViejas.length >= 2 && entradasViejas.length >= 2) {
                        const almuerzoMin = moment(entradasViejas[1].FECHA_REGISTRO).diff(moment(salidasViejas[0].FECHA_REGISTRO), 'minutes');
                        if (almuerzoMin > 0) totalMin -= almuerzoMin;
                    }
                }
                if (totalMin >= 0) {
                    tiempo_total = `${String(Math.floor(totalMin / 60)).padStart(2, '0')}:${String(totalMin % 60).padStart(2, '0')}`;
                }
            }

            const areaInfo = areaMap.get(grupo.ID_USUARIO);
            registros.push({
                ID_USUARIO: grupo.ID_USUARIO,
                NOMBRE_COMPLETO: grupo.NOMBRE_COMPLETO,
                CEDULA: grupo.CEDULA,
                ID_AREA: areaInfo ? areaInfo.id : null,
                AREA: areaInfo ? areaInfo.nombre : null,
                FECHA: grupo.FECHA,
                FECHA_DISPLAY: grupo.FECHA_DISPLAY,
                DIA: diaSemana,
                ORIGEN: origen,
                HORA_ENTRADA: hora_entrada,
                SALIDA_ALMUERZO: salida_almuerzo,
                ENTRADA_ALMUERZO: entrada_almuerzo,
                HORA_SALIDA: hora_salida,
                TIEMPO_TOTAL: tiempo_total
            });
        });

        // Ordenar por fecha desc, luego nombre asc
        registros.sort((a, b) => {
            const dateDiff = moment(b.FECHA).diff(moment(a.FECHA));
            return dateDiff !== 0 ? dateDiff : a.NOMBRE_COMPLETO.localeCompare(b.NOMBRE_COMPLETO);
        });

        // Filtros post-agrupación
        let registrosFiltrados = registros;
        if (idAreaFiltro) {
            registrosFiltrados = registrosFiltrados.filter(r => r.ID_AREA === idAreaFiltro);
        }
        if (estadoFiltro === 'completas') {
            registrosFiltrados = registrosFiltrados.filter(r => r.HORA_ENTRADA && r.HORA_SALIDA);
        } else if (estadoFiltro === 'incompletas') {
            registrosFiltrados = registrosFiltrados.filter(r => !r.HORA_ENTRADA || !r.HORA_SALIDA);
        }

        const completas = registrosFiltrados.filter(r => r.HORA_ENTRADA && r.HORA_SALIDA).length;
        const paginados = registrosFiltrados.slice(mul, mul + limit);

        res.status(200).send({
            data: paginados,
            total: registrosFiltrados.length,
            completas,
            incompletas: registrosFiltrados.length - completas
        });

    } catch (e) {
        console.log(e);
        res.status(500).send({ message: e.name });
    }
}

// Contar total de grupos para reporte agrupado
async function contarTotalReporteAgrupado(req, res) {
    try {
        const params = req.body;
        const idUsuario = req.body.idUsuario || null;

        let fechaDesde, fechaHasta;
        if (params.fechaDesde && params.fechaHasta) {
            fechaDesde = moment(params.fechaDesde).tz('America/Guayaquil').startOf('day');
            fechaHasta = moment(params.fechaHasta).tz('America/Guayaquil').endOf('day');
        } else {
            fechaDesde = moment().tz('America/Guayaquil').startOf('month');
            fechaHasta = moment().tz('America/Guayaquil').endOf('day');
        }

        const whereAsistencia = {
            ESTADO: true,
            FECHA_REGISTRO: { [Op.between]: [fechaDesde.toDate(), fechaHasta.toDate()] }
        };
        if (idUsuario) whereAsistencia.ID_USUARIO = idUsuario;

        const grupos = await Asistencia.findAll({
            where: whereAsistencia,
            attributes: [
                'ID_USUARIO',
                [Asistencia.sequelize.fn('DATE', Asistencia.sequelize.col('FECHA_REGISTRO')), 'FECHA_DIA']
            ],
            group: [
                'ID_USUARIO',
                Asistencia.sequelize.fn('DATE', Asistencia.sequelize.col('FECHA_REGISTRO'))
            ],
            raw: true
        });

        res.status(200).send({ data: grupos.length });
    } catch (e) {
        console.log(e);
        res.status(500).send({ message: e.name });
    }
}

async function verificarContrasenia(req, res) {
    try {
        const { ID_USUARIO, CONTRASENIA } = req.body;
        if (!ID_USUARIO || !CONTRASENIA)
            return res.status(400).json({ message: 'Datos incompletos' });

        const usuario = await Usuario.findByPk(ID_USUARIO, {
            attributes: ['ID_USUARIO', 'CONTRASENIA']
        });
        if (!usuario)
            return res.status(404).json({ message: 'Usuario no encontrado' });

        const bcrypt = require('bcryptjs');
        const ok = bcrypt.compareSync(CONTRASENIA, usuario.CONTRASENIA);
        if (!ok)
            return res.status(401).json({ message: 'Contraseña incorrecta' });

        res.status(200).json({ message: 'OK' });
    } catch (e) {
        res.status(500).json({ message: e.message });
    }
}

module.exports = {
    registrarAsistencia,
    obtenerAsistencias,
    contarTotalAsistencias,
    obtenerMarcaciones,
    obtenerIPPublica,
    obtenerReporteMarcaciones,
    contarTotalMarcacionesReporte,
    obtenerReporteAgrupado,
    contarTotalReporteAgrupado,
    verificarContrasenia
};





