'use strict';
const { db } = require('../utils/dbName');
const express  = require('express');
const router   = express.Router();
const { getPool, sql } = require('../db');
const { sanitizeDate, sanitizeInt, sanitizeString } = require('../utils/validate');
const { localDateStr } = require('../utils/localDate');

function fmtDate(d) {
  return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
}

async function columnExists(pool, table, column) {
  try {
    const dbName = process.env.DB_NAME || 'softrestaurant11';
    const r = await pool.request()
      .input('table', sql.NVarChar(128), table)
      .input('column', sql.NVarChar(128), column)
      .query(`
        SELECT 1 AS ok
        FROM ${dbName}.INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_SCHEMA='dbo' AND TABLE_NAME=@table AND COLUMN_NAME=@column
      `);
    return r.recordset.length > 0;
  } catch (_) {
    return false;
  }
}

async function tableExists(pool, table) {
  try {
    const dbName = process.env.DB_NAME || 'softrestaurant11';
    const r = await pool.request()
      .input('table', sql.NVarChar(128), table)
      .query(`
        SELECT 1 AS ok
        FROM ${dbName}.INFORMATION_SCHEMA.TABLES
        WHERE TABLE_SCHEMA='dbo' AND TABLE_NAME=@table
      `);
    return r.recordset.length > 0;
  } catch (_) {
    return false;
  }
}

function ventasUnionSql(alias = 'v') {
  return `(
    SELECT 'C' AS origen, folio, fecha, idturno, idarearestaurant, cancelado, pagado, total
    FROM ${db()}cheques
    WHERE cancelado = 0 AND pagado = 1 AND total > 0
    UNION ALL
    SELECT 'T' AS origen, folio, fecha, idturno, idarearestaurant, cancelado, pagado, total
    FROM ${db()}tempcheques ${alias}tc
    WHERE ISNULL(${alias}tc.cancelado, 0) = 0
      AND ISNULL(${alias}tc.pagado, 0) = 1
      AND ISNULL(${alias}tc.total, 0) > 0
      AND NOT EXISTS (
        SELECT 1 FROM ${db()}cheques ${alias}ch
        WHERE ${alias}ch.folio = ${alias}tc.folio
          AND CAST(${alias}ch.fecha AS DATE) = CAST(${alias}tc.fecha AS DATE)
          AND ${alias}ch.cancelado = 0
          AND ${alias}ch.pagado = 1
      )
  )`;
}

function detalleUnionSql() {
  return `(
    SELECT 'C' AS origen, foliodet, idproducto, cantidad, precio, ISNULL(modificador, 0) AS modificador
    FROM ${db()}cheqdet
    UNION ALL
    SELECT 'T' AS origen, foliodet, idproducto, cantidad, precio, ISNULL(modificador, 0) AS modificador
    FROM ${db()}tempcheqdet
  )`;
}

// GET /api/productos/analytics
router.get('/analytics', async (req, res, next) => {
  try {
    const pool = await getPool();
    const periodo = (req.query.periodo || 'dia').toLowerCase();
    const turno = sanitizeInt(req.query.turno) || null;
    const grupo = sanitizeString(req.query.grupo, 100) || null;
    const estacion = sanitizeString(req.query.estacion, 50) || null;
    const area = sanitizeString(req.query.area, 50) || null;
    const compararIds = String(req.query.compararIds || '')
      .split(',')
      .map(s => sanitizeString(s, 50))
      .filter(Boolean)
      .slice(0, 6);
    const hoy = localDateStr();

    let desde, hasta;
    const d = new Date(hoy + 'T12:00:00');
    if (periodo === 'semana') {
      const dow = d.getDay();
      const mon = new Date(d);
      mon.setDate(d.getDate() - (dow === 0 ? 6 : dow - 1));
      desde = fmtDate(mon);
      hasta = hoy;
    } else if (periodo === 'mes') {
      desde = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-01`;
      hasta = hoy;
    } else if (periodo === 'anio') {
      const anio = parseInt(req.query.anio, 10) || d.getFullYear();
      desde = `${anio}-01-01`;
      hasta = anio === d.getFullYear() ? hoy : `${anio}-12-31`;
    } else if (periodo === 'rango') {
      desde = sanitizeDate(req.query.fechaDesde || req.query.desde) || hoy;
      hasta = sanitizeDate(req.query.fechaHasta || req.query.hasta) || desde;
      if (desde > hasta) [desde, hasta] = [hasta, desde];
    } else {
      desde = hoy;
      hasta = hoy;
    }

    // Construir WHERE con parámetros SQL — sin interpolación de valores de usuario
    // El filtro de turno y grupo va siempre como parámetro tipado (@an_turno, @an_grupo)
    const hasModificador = await columnExists(pool, 'cheqdet', 'modificador');
    const hasProdBloqueado = await columnExists(pool, 'productosdetalle', 'bloqueado');
    const hasProductArea = await columnExists(pool, 'productosdetalle', 'idarea');
    const hasAreasTable = hasProductArea ? await tableExists(pool, 'areas') : false;
    const areaLabelColumn = hasAreasTable && await columnExists(pool, 'areas', 'nombre')
      ? 'nombre'
      : (hasAreasTable && await columnExists(pool, 'areas', 'descripcion') ? 'descripcion' : null);

    const makeReq = () => {
      const r = pool.request();
      r.input('an_desde', sql.VarChar(10), desde);
      r.input('an_hasta', sql.VarChar(10), hasta);
      if (turno) r.input('an_turno', sql.Int,       turno);
      if (grupo) r.input('an_grupo', sql.NVarChar(100), grupo);
      if (estacion) r.input('an_estacion', sql.NVarChar(50), estacion);
      if (area) r.input('an_area', sql.NVarChar(50), area);
      compararIds.forEach((id, index) => {
        r.input(`cmp_id_${index}`, sql.NVarChar(50), id);
      });
      return r;
    };

    // Cláusula base: rango de fechas o turno
    const fechaWhere = turno
      ? `m.idturno = @an_turno AND m.cancelado=0 AND m.total>0`
      : `CAST(m.fecha AS DATE) BETWEEN CONVERT(date,@an_desde,23) AND CONVERT(date,@an_hasta,23) AND m.cancelado=0 AND m.total>0`;

    const modFilter = hasModificador ? 'AND (ISNULL(cd.modificador,0)=0 OR ISNULL(cd.precio,0)>0)' : '';
    const grupoFilter = grupo ? `AND g.descripcion = @an_grupo` : '';
    const estacionJoin = `LEFT JOIN ${db()}turnos t ON t.idturno = m.idturno`;
    const estacionFilter = estacion ? `AND CAST(ISNULL(t.idestacion,'') AS NVARCHAR(50)) = @an_estacion` : '';
    const areaFilter = area && hasProductArea
      ? `AND EXISTS (
          SELECT 1 FROM ${db()}productosdetalle pda
          WHERE pda.idproducto = p.idproducto
            AND CAST(ISNULL(pda.idarea,'') AS NVARCHAR(50)) = @an_area
        )`
      : '';
    const bloqueadoFilter = hasProdBloqueado
      ? `AND NOT EXISTS (
          SELECT 1 FROM ${db()}productosdetalle pd
          WHERE pd.idproducto=p.idproducto AND ISNULL(pd.bloqueado,0)<>0
        )`
      : '';
    const compareSelect = compararIds.length
      ? compararIds.map((_, index) => `
          SELECT
            COALESCE((SELECT TOP 1 producto FROM #VentasProductos WHERE idproducto = @cmp_id_${index}), p.descripcion, @cmp_id_${index}) AS termino,
            @cmp_id_${index} AS idproducto,
            ROUND(ISNULL(SUM(cantidad),0),0) AS cantidad,
            ROUND(ISNULL(SUM(venta),0),2) AS venta,
            CASE WHEN COUNT(v.idproducto) > 0 THEN 1 ELSE 0 END AS productos
          FROM ${db()}productos p
          LEFT JOIN #VentasProductos v ON v.idproducto = p.idproducto
          WHERE p.idproducto = @cmp_id_${index}
          GROUP BY p.descripcion
        `).join('\nUNION ALL\n')
      : `SELECT CAST(NULL AS NVARCHAR(80)) AS termino, CAST(NULL AS NVARCHAR(50)) AS idproducto, 0 AS cantidad, 0 AS venta, 0 AS productos WHERE 1=0`;
    const catalogSelect = `
      SELECT
        CAST(p.idproducto AS NVARCHAR(50)) AS idproducto,
        LTRIM(RTRIM(ISNULL(p.descripcion, CAST(p.idproducto AS NVARCHAR(80))))) AS producto,
        ISNULL(g.descripcion,'Sin grupo') AS grupo
      FROM ${db()}productos p
      LEFT JOIN ${db()}grupos g ON p.idgrupo=g.idgrupo
      WHERE 1=1
        ${grupoFilter}
        ${areaFilter}
        ${bloqueadoFilter}
      ORDER BY p.descripcion;
    `;
    const areasSelect = hasProductArea
      ? `
      SELECT DISTINCT
        CAST(pd.idarea AS NVARCHAR(50)) AS id,
        ${areaLabelColumn ? `COALESCE(NULLIF(LTRIM(RTRIM(a.${areaLabelColumn})), ''), CAST(pd.idarea AS NVARCHAR(50)))` : 'CAST(pd.idarea AS NVARCHAR(50))'} AS label
      FROM ${db()}productosdetalle pd
      ${areaLabelColumn ? `LEFT JOIN ${db()}areas a ON CAST(a.idarea AS NVARCHAR(50)) = CAST(pd.idarea AS NVARCHAR(50))` : ''}
      WHERE pd.idarea IS NOT NULL AND LTRIM(RTRIM(CAST(pd.idarea AS NVARCHAR(50)))) <> ''
      ORDER BY label;
      `
      : `SELECT CAST(NULL AS NVARCHAR(50)) AS id, CAST(NULL AS NVARCHAR(80)) AS label WHERE 1=0;`;

    const result = await makeReq().query(`
      SET NOCOUNT ON;

      IF OBJECT_ID('tempdb..#VentasProductos') IS NOT NULL DROP TABLE #VentasProductos;

      WITH Movimientos AS (
        SELECT
          'C' AS origen,
          c.folio,
          c.fecha,
          c.idturno,
          c.cancelado,
          c.total,
          cd.idproducto,
          cd.cantidad,
          cd.precio,
          ${hasModificador ? 'ISNULL(cd.modificador,0)' : '0'} AS modificador
        FROM ${db()}cheqdet cd
        INNER JOIN ${db()}cheques c ON cd.foliodet=c.folio
        WHERE c.pagado=1
        UNION ALL
        SELECT
          'T' AS origen,
          tc.folio,
          tc.fecha,
          tc.idturno,
          tc.cancelado,
          tc.total,
          td.idproducto,
          td.cantidad,
          td.precio,
          ${hasModificador ? 'ISNULL(td.modificador,0)' : '0'} AS modificador
        FROM ${db()}tempcheqdet td
        INNER JOIN ${db()}tempcheques tc ON td.foliodet=tc.folio
        WHERE ISNULL(tc.pagado,0)=1
          AND NOT EXISTS (
            SELECT 1 FROM ${db()}cheques ch
            WHERE ch.folio = tc.folio
              AND CAST(ch.fecha AS DATE) = CAST(tc.fecha AS DATE)
              AND ch.cancelado = 0
              AND ch.pagado = 1
          )
      )
      SELECT
        m.idproducto,
        LTRIM(RTRIM(ISNULL(p.descripcion, CAST(m.idproducto AS NVARCHAR(80))))) AS producto,
        ISNULL(g.descripcion,'Sin grupo') AS grupo,
        SUM(ISNULL(m.cantidad,0)) AS cantidad,
        SUM(ISNULL(m.cantidad,0) * ISNULL(m.precio,0)) AS venta,
        AVG(ISNULL(m.precio,0)) AS precio_promedio,
        COUNT(DISTINCT m.origen + ':' + CAST(m.folio AS NVARCHAR(80))) AS tickets,
        COUNT(DISTINCT CAST(m.fecha AS DATE)) AS dias_vendido
      INTO #VentasProductos
      FROM Movimientos m
      INNER JOIN ${db()}productos p ON m.idproducto=p.idproducto
      LEFT JOIN ${db()}grupos g ON p.idgrupo=g.idgrupo
      ${estacionJoin}
      WHERE ${fechaWhere} AND (m.modificador=0 OR ISNULL(m.precio,0)>0) ${grupoFilter} ${estacionFilter} ${areaFilter}
      GROUP BY m.idproducto, p.descripcion, g.descripcion;

      CREATE CLUSTERED INDEX IX_VentasProductos_Id ON #VentasProductos(idproducto);

      SELECT TOP 300
        producto,
        idproducto,
        grupo,
        ROUND(cantidad,2) AS cantidad,
        ROUND(venta,2) AS venta,
        ROUND(precio_promedio,2) AS precio_promedio,
        tickets,
        dias_vendido
      FROM #VentasProductos
      ORDER BY venta DESC;

      SELECT
        COUNT(*) AS productos_vendidos,
        ROUND(ISNULL(SUM(cantidad),0),0) AS unidades_totales,
        ROUND(ISNULL(SUM(venta),0),2) AS venta_total,
        ISNULL(SUM(tickets),0) AS tickets
      FROM #VentasProductos;

      SELECT TOP 20
        LTRIM(RTRIM(ISNULL(p.descripcion, CAST(p.idproducto AS NVARCHAR(80))))) AS producto,
        p.idproducto,
        ISNULL(g.descripcion,'Sin grupo') AS grupo
      FROM ${db()}productos p
      LEFT JOIN ${db()}grupos g ON p.idgrupo=g.idgrupo
      WHERE NOT EXISTS (SELECT 1 FROM #VentasProductos v WHERE v.idproducto=p.idproducto)
        ${grupoFilter}
        ${areaFilter}
        ${bloqueadoFilter}
      ORDER BY g.descripcion,p.descripcion;

      SELECT
        grupo AS categoria,
        ROUND(SUM(cantidad),0) AS cantidad,
        ROUND(SUM(venta),2) AS venta
      FROM #VentasProductos
      GROUP BY grupo
      ORDER BY venta DESC;

      SELECT DISTINCT t.idturno, t.apertura
      FROM ${db()}turnos t
      WHERE CAST(t.apertura AS DATE) BETWEEN CONVERT(date,@an_desde,23) AND CONVERT(date,@an_hasta,23)
      ORDER BY t.apertura;

      SELECT DISTINCT CAST(t.idestacion AS NVARCHAR(50)) AS idestacion
      FROM ${db()}turnos t
      WHERE CAST(t.apertura AS DATE) BETWEEN CONVERT(date,@an_desde,23) AND CONVERT(date,@an_hasta,23)
        AND t.idestacion IS NOT NULL
        AND LTRIM(RTRIM(CAST(t.idestacion AS NVARCHAR(50)))) <> ''
      ORDER BY idestacion;

      SELECT DISTINCT ISNULL(g.descripcion,'Sin grupo') AS grupo
      FROM ${db()}productos p
      LEFT JOIN ${db()}grupos g ON p.idgrupo=g.idgrupo
      WHERE g.descripcion IS NOT NULL
      ORDER BY grupo;

      ${areasSelect}

      ${catalogSelect}

      ${compareSelect}
    `);

    const allProds = { recordset: result.recordsets[0] || [] };
    const resumenQ = { recordset: result.recordsets[1] || [] };
    const sinMov = { recordset: result.recordsets[2] || [] };
    const cats = { recordset: result.recordsets[3] || [] };
    const turnosQ = { recordset: result.recordsets[4] || [] };
    const estacionesQ = { recordset: result.recordsets[5] || [] };
    const gruposQ = { recordset: result.recordsets[6] || [] };
    const areasQ = { recordset: result.recordsets[7] || [] };
    const catalogoQ = { recordset: result.recordsets[8] || [] };
    const comparativoQ = { recordset: result.recordsets[9] || [] };

    const r = resumenQ.recordset[0] || {};
    const totalVenta = r.venta_total || 0;

    const productos = allProds.recordset.map((p, i) => ({
      ...p,
      ranking: i + 1,
      porcentaje: totalVenta > 0 ? Math.round(p.venta / totalVenta * 10000) / 100 : 0,
    }));

    res.json({
      ok: true, periodo, desde, hasta, turno, grupo, estacion, area,
      resumen: {
        productos_vendidos: r.productos_vendidos || 0,
        unidades_totales: r.unidades_totales || 0,
        venta_total: totalVenta,
        tickets: r.tickets || 0,
        ticket_promedio: r.tickets > 0 ? Math.round((totalVenta / r.tickets) * 100) / 100 : 0,
      },
      productos, sin_movimiento: sinMov.recordset, categorias: cats.recordset,
      turnos: turnosQ.recordset.map((t, i) => ({ id: t.idturno, label: `Turno ${i+1}` })),
      estaciones: estacionesQ.recordset.map(e => ({ id: e.idestacion, label: `Estación ${e.idestacion}` })),
      grupos: gruposQ.recordset.map(g => g.grupo),
      areas: areasQ.recordset.filter(a => a.id).map(a => ({ id: a.id, label: a.label || a.id })),
      productos_catalogo: catalogoQ.recordset,
      comparativo_productos: comparativoQ.recordset.filter(r => r.termino),
    });
  } catch (err) { next(err); }
});

// GET /api/productos/top
router.get('/top', async (req, res, next) => {
  try {
    const pool = await getPool(); const hoy = localDateStr();
    const fecha = sanitizeDate(req.query.fecha) || null;
    const desde = sanitizeDate(req.query.fechaDesde || req.query.desde) || null;
    const hasta = sanitizeDate(req.query.fechaHasta || req.query.hasta) || null;
    const turno = sanitizeInt(req.query.turno) || null;
    const estacion = sanitizeString(req.query.estacion, 50) || null;
    const request = pool.request();
    let fechaFilter = 'CAST(c.fecha AS DATE)=@top_hoy';
    if (turno) {
      request.input('top_turno', sql.Int, turno);
      fechaFilter = 'c.idturno=@top_turno';
    } else if (desde && hasta) {
      request.input('top_desde', sql.VarChar(10), desde);
      request.input('top_hasta', sql.VarChar(10), hasta);
      fechaFilter = 'CAST(c.fecha AS DATE) BETWEEN CONVERT(date,@top_desde,23) AND CONVERT(date,@top_hasta,23)';
    } else {
      request.input('top_hoy', sql.VarChar(10), fecha || hoy);
      fechaFilter = 'CAST(c.fecha AS DATE)=CONVERT(date,@top_hoy,23)';
    }
    if (estacion) request.input('top_estacion', sql.NVarChar(50), estacion);
    const stationFilter = estacion ? `AND EXISTS (SELECT 1 FROM ${db()}turnos t WHERE t.idturno=c.idturno AND CAST(ISNULL(t.idestacion,'') AS NVARCHAR(50))=@top_estacion)` : '';
    const r = await request.query(`
      SELECT TOP 20
        p.descripcion AS producto,
        ISNULL(g.descripcion,'Sin grupo') AS grupo,
        ROUND(SUM(cd.cantidad),0) AS cantidad_vendida,
        ROUND(SUM(cd.cantidad*cd.precio),2) AS total_venta
      FROM ${detalleUnionSql()} cd
      INNER JOIN ${db()}productos p ON cd.idproducto=p.idproducto
      LEFT JOIN ${db()}grupos g ON p.idgrupo=g.idgrupo
      INNER JOIN ${ventasUnionSql('top_')} c ON cd.foliodet=c.folio AND cd.origen=c.origen
      WHERE ${fechaFilter} AND c.cancelado=0 AND c.pagado=1 AND c.total>0 AND (cd.modificador=0 OR ISNULL(cd.precio,0)>0) ${stationFilter}
      GROUP BY p.descripcion,g.descripcion
      ORDER BY cantidad_vendida DESC
    `);
    res.json({ ok: true, data: r.recordset });
  } catch(err) { next(err); }
});

// GET /api/productos/por-grupo
router.get('/por-grupo', async (req, res, next) => {
  try {
    const pool = await getPool(); const hoy = localDateStr();
    const fecha = sanitizeDate(req.query.fecha) || null;
    const desde = sanitizeDate(req.query.fechaDesde || req.query.desde) || null;
    const hasta = sanitizeDate(req.query.fechaHasta || req.query.hasta) || null;
    const turno = sanitizeInt(req.query.turno) || null;
    const estacion = sanitizeString(req.query.estacion, 50) || null;
    const request = pool.request();
    let fechaFilter = 'CAST(c.fecha AS DATE)=@pg_hoy';
    if (turno) {
      request.input('pg_turno', sql.Int, turno);
      fechaFilter = 'c.idturno=@pg_turno';
    } else if (desde && hasta) {
      request.input('pg_desde', sql.VarChar(10), desde);
      request.input('pg_hasta', sql.VarChar(10), hasta);
      fechaFilter = 'CAST(c.fecha AS DATE) BETWEEN CONVERT(date,@pg_desde,23) AND CONVERT(date,@pg_hasta,23)';
    } else {
      request.input('pg_hoy', sql.VarChar(10), fecha || hoy);
      fechaFilter = 'CAST(c.fecha AS DATE)=CONVERT(date,@pg_hoy,23)';
    }
    if (estacion) request.input('pg_estacion', sql.NVarChar(50), estacion);
    const stationFilter = estacion ? `AND EXISTS (SELECT 1 FROM ${db()}turnos t WHERE t.idturno=c.idturno AND CAST(ISNULL(t.idestacion,'') AS NVARCHAR(50))=@pg_estacion)` : '';
    const r = await request.query(`
      SELECT
        ISNULL(g.descripcion,'Sin grupo') AS grupo,
        ROUND(SUM(cd.cantidad),0) AS piezas,
        ROUND(SUM(cd.cantidad*cd.precio),2) AS total,
        ROUND(SUM(cd.cantidad*cd.precio)*100.0/NULLIF(SUM(SUM(cd.cantidad*cd.precio)) OVER(),0),2) AS porcentaje
      FROM ${detalleUnionSql()} cd
      INNER JOIN ${db()}productos p ON cd.idproducto=p.idproducto
      LEFT JOIN ${db()}grupos g ON p.idgrupo=g.idgrupo
      INNER JOIN ${ventasUnionSql('pg_')} c ON cd.foliodet=c.folio AND cd.origen=c.origen
      WHERE ${fechaFilter} AND c.cancelado=0 AND c.pagado=1 AND c.total>0 AND (cd.modificador=0 OR ISNULL(cd.precio,0)>0) ${stationFilter}
      GROUP BY g.descripcion
      ORDER BY total DESC
    `);
    res.json({ ok: true, data: r.recordset });
  } catch(err) { next(err); }
});

// GET /api/productos/:id/modificadores
router.get('/:id/modificadores', async (req, res, next) => {
  try {
    const pool = await getPool();
    const idproducto = sanitizeString(req.params.id, 20);
    if (!idproducto) return res.status(400).json({ ok: false, error: 'idproducto requerido' });
    const requiredTables = await Promise.all([
      tableExists(pool, 'gruposmodificadores'),
      tableExists(pool, 'gruposmodificadoresproductos'),
      tableExists(pool, 'modificadores'),
    ]);
    if (requiredTables.some((ok) => !ok)) {
      return res.json({
        ok: true,
        idproducto,
        supported: false,
        groups: [],
        warnings: ['Tablas de modificadores no detectadas en SoftRestaurant'],
      });
    }
    const has = {
      gmp_idempresa: await columnExists(pool, 'gruposmodificadoresproductos', 'idempresa'),
      gmp_prioridad: await columnExists(pool, 'gruposmodificadoresproductos', 'prioridad'),
      gmp_incluidos: await columnExists(pool, 'gruposmodificadoresproductos', 'incluidos'),
      gmp_forzarcaptura: await columnExists(pool, 'gruposmodificadoresproductos', 'forzarcaptura'),
      gmp_maximomodificadores: await columnExists(pool, 'gruposmodificadoresproductos', 'maximomodificadores'),
      gmp_captura_incluido: await columnExists(pool, 'gruposmodificadoresproductos', 'captura_incluido'),
      mod_idempresa: await columnExists(pool, 'modificadores', 'idempresa'),
      mod_idmodificador: await columnExists(pool, 'modificadores', 'idmodificador'),
      mod_precio: await columnExists(pool, 'modificadores', 'precio'),
    };
    const result = await pool.request()
      .input('idproducto', sql.NVarChar(20), idproducto)
      .query(`
        SELECT
          gmp.idgruposmodificadores AS idgrupo_modificador,
          gm.descripcion AS grupo,
          ${has.gmp_idempresa ? 'gmp.idempresa' : 'NULL'} AS idempresa,
          ${has.gmp_prioridad ? 'gmp.prioridad' : '0'} AS secuencia,
          ${has.gmp_incluidos ? 'gmp.incluidos' : '0'} AS incluidos,
          ${has.gmp_maximomodificadores ? 'gmp.maximomodificadores' : '0'} AS maximomodificadores,
          ${has.gmp_forzarcaptura ? 'gmp.forzarcaptura' : '0'} AS forzarcaptura,
          ${has.gmp_captura_incluido ? 'gmp.captura_incluido' : '0'} AS captura_incluido,
          m.idproducto AS idproducto_padre,
          ${has.mod_idmodificador ? 'm.idmodificador' : 'NULL'} AS idmodificador,
          ${has.mod_idmodificador ? 'm.idmodificador' : 'm.idproducto'} AS idproducto_modificador,
          ${has.mod_precio ? 'm.precio' : '0'} AS precio_modificador,
          ${has.mod_idempresa ? 'm.idempresa' : 'NULL'} AS idempresa_modificador,
          LTRIM(RTRIM(ISNULL(p.descripcion, CAST(${has.mod_idmodificador ? 'm.idmodificador' : 'm.idproducto'} AS NVARCHAR(80))))) AS modificador
        FROM ${db()}gruposmodificadoresproductos gmp
        LEFT JOIN ${db()}gruposmodificadores gm ON gm.idgruposmodificadores=gmp.idgruposmodificadores
        LEFT JOIN ${db()}modificadores m ON m.idgruposmodificadores=gmp.idgruposmodificadores
          AND m.idproducto=gmp.idproducto
          ${has.gmp_idempresa && has.mod_idempresa ? 'AND ISNULL(m.idempresa, \'\') = ISNULL(gmp.idempresa, \'\')' : ''}
        LEFT JOIN ${db()}productos p ON p.idproducto=${has.mod_idmodificador ? 'm.idmodificador' : 'm.idproducto'}
        WHERE gmp.idproducto=@idproducto
        ORDER BY ${has.gmp_prioridad ? 'gmp.prioridad' : 'gmp.idgruposmodificadores'}, gm.descripcion, p.descripcion
      `);
    const groups = [];
    const byGroup = new Map();
    for (const row of result.recordset) {
      const key = `${row.idempresa || ''}:${row.idgrupo_modificador || ''}`;
      if (!byGroup.has(key)) {
        const group = {
          idgrupo_modificador: row.idgrupo_modificador,
          descripcion: row.grupo,
          idempresa: row.idempresa,
          secuencia: row.secuencia,
          incluidos: row.incluidos,
          maximomodificadores: row.maximomodificadores,
          forzarcaptura: Boolean(row.forzarcaptura),
          captura_incluido: Boolean(row.captura_incluido),
          modifiers: [],
        };
        byGroup.set(key, group);
        groups.push(group);
      }
      if (row.idproducto_modificador) {
        byGroup.get(key).modifiers.push({
          idproducto: row.idproducto_modificador,
          idmodificador: row.idmodificador,
          descripcion: row.modificador,
          precio: row.precio_modificador,
          idempresa: row.idempresa_modificador,
        });
      }
    }
    res.json({ ok: true, idproducto, supported: true, groups });
  } catch (err) { next(err); }
});

module.exports = router;
