'use strict';
const logger = require('../utils/logger');
const { db } = require('../utils/dbName');

const { getPool, sql } = require('../db');
const { sanitizeInt }  = require('../utils/validate');
async function getAniosDisponibles() {
  try {

  const pool = await getPool();
  const r = await pool.request().query(`
    SELECT DISTINCT YEAR(fecha) AS anio, COUNT(*) AS tickets,
           ROUND(SUM(total),2) AS venta_total
    FROM ${db()}cheques
    WHERE cancelado=0 AND pagado=1 AND total>0
    GROUP BY YEAR(fecha)
    ORDER BY anio DESC
  `);
  return r.recordset;
  } catch(_) { return []; }
}
async function getGrupos() {
  const pool = await getPool();
  try {
    const r = await pool.request().query(`
      SELECT idgrupo, descripcion
      FROM ${db()}grupos
      ORDER BY descripcion
    `);
    return r.recordset;
  } catch(_) { return []; }
}

async function getTopProductos({ anio, limite = 10, orden = 'mas', criterio = 'cantidad', idgrupo = null }) {
  const pool      = await getPool();

  const limiteInt = Math.min(Math.max(sanitizeInt(limite) ?? 10, 1), 1000);
  const orderDir  = orden === 'mas' ? 'DESC' : 'ASC';
  const orderCol  = criterio === 'importe' ? 'total_importe' : 'total_cantidad';

  const req = pool.request();
  req.input('tp_anio',   sql.Int, parseInt(anio));
  req.input('tp_limite', sql.Int, limiteInt);

  let grupoWhere = '';
  if (idgrupo !== null && idgrupo !== '') {
    req.input('tp_idgrupo', sql.VarChar(50), String(idgrupo).replace(/[^a-zA-Z0-9_\-]/g, ''));
    grupoWhere = 'AND p.idgrupo = @tp_idgrupo';
  }

  const r = await req.query(`
    SELECT TOP (@tp_limite)
      p.descripcion                       AS producto,
      g.descripcion                       AS grupo,
      SUM(cd.cantidad)                    AS total_cantidad,
      ROUND(SUM(cd.cantidad * cd.precio), 2) AS total_importe
    FROM ${db()}cheqdet cd
    INNER JOIN ${db()}cheques   c ON cd.foliodet   = c.folio
    INNER JOIN ${db()}productos p ON cd.idproducto = p.idproducto
    LEFT  JOIN ${db()}grupos    g ON p.idgrupo     = g.idgrupo
    WHERE YEAR(c.fecha) = @tp_anio
      AND c.cancelado = 0 AND c.pagado = 1
      AND (ISNULL(cd.modificador, 0) = 0 OR ISNULL(cd.precio, 0) > 0)
      ${grupoWhere}
    GROUP BY p.descripcion, g.descripcion
    ORDER BY ${orderCol} ${orderDir}
  `);
  return r.recordset;
}

async function getComparativoProductos({ anioActual, anioAnterior, limite = 10, idgrupo = null }) {
  const pool      = await getPool();

  const limiteInt = Math.min(Math.max(sanitizeInt(limite) ?? 10, 1), 1000);

  const reqActual = pool.request();
  reqActual.input('cp_anio_actual', sql.Int, parseInt(anioActual));
  reqActual.input('cp_limite',      sql.Int, limiteInt);

  let grupoWhere = '';
  if (idgrupo !== null && idgrupo !== '') {
    reqActual.input('cp_idgrupo', sql.VarChar(50), String(idgrupo).replace(/[^a-zA-Z0-9_\-]/g, ''));
    grupoWhere = 'AND p.idgrupo = @cp_idgrupo';
  }

  
  const rActual = await reqActual.query(`
    SELECT TOP (@cp_limite)
      p.descripcion                          AS producto,
      SUM(cd.cantidad)                       AS cantidad,
      ROUND(SUM(cd.cantidad * cd.precio),2)  AS importe
    FROM ${db()}cheqdet cd
    INNER JOIN ${db()}cheques   c ON cd.foliodet   = c.folio
    INNER JOIN ${db()}productos p ON cd.idproducto = p.idproducto
    WHERE YEAR(c.fecha) = @cp_anio_actual
      AND c.cancelado=0 AND c.pagado=1 AND (ISNULL(cd.modificador, 0)=0 OR ISNULL(cd.precio, 0)>0) ${grupoWhere}
    GROUP BY p.descripcion
    ORDER BY SUM(cd.cantidad) DESC
  `);

  const productos = rActual.recordset.map(r => r.producto);
  if (productos.length === 0) return [];

  
  const reqAnterior = pool.request();
  reqAnterior.input('cp_anio_anterior', sql.Int, parseInt(anioAnterior));

  
  const inParams = productos.map((p, i) => {
    const paramName = `cp_prod_${i}`;
    reqAnterior.input(paramName, sql.VarChar(255), p);
    return `@${paramName}`;
  }).join(',');

  const rAnterior = await reqAnterior.query(`
    SELECT p.descripcion AS producto,
      ISNULL(SUM(cd.cantidad),0)             AS cantidad,
      ROUND(ISNULL(SUM(cd.cantidad * cd.precio),0),2) AS importe
    FROM ${db()}cheqdet cd
    INNER JOIN ${db()}cheques   c ON cd.foliodet   = c.folio
    INNER JOIN ${db()}productos p ON cd.idproducto = p.idproducto
    WHERE YEAR(c.fecha) = @cp_anio_anterior
      AND c.cancelado=0 AND c.pagado=1 AND (ISNULL(cd.modificador, 0)=0 OR ISNULL(cd.precio, 0)>0)
      AND p.descripcion IN (${inParams})
    GROUP BY p.descripcion
  `);

  const mapAnterior = {};
  rAnterior.recordset.forEach(r => { mapAnterior[r.producto] = r; });

  return rActual.recordset.map(r => ({
    producto:          r.producto,
    cantidad_actual:   r.cantidad,
    importe_actual:    r.importe,
    cantidad_anterior: mapAnterior[r.producto]?.cantidad || 0,
    importe_anterior:  mapAnterior[r.producto]?.importe  || 0,
  }));
}

function parseSearchTerms(q) {
  return String(q || '')
    .split(',')
    .map(term => term.trim().replace(/\s+/g, ' '))
    .filter(term => term.length >= 2)
    .slice(0, 8);
}

async function getComparativoBusqueda({ anioActual, anioAnterior, q, criterio = 'importe', idgrupo = null }) {
  const terms = parseSearchTerms(q);
  if (!terms.length) return [];

  const pool = await getPool();
  const req = pool.request();
  req.input('cb_anio_actual', sql.Int, parseInt(anioActual));
  req.input('cb_anio_anterior', sql.Int, parseInt(anioAnterior));

  let grupoWhere = '';
  if (idgrupo !== null && idgrupo !== '') {
    req.input('cb_idgrupo', sql.VarChar(50), String(idgrupo).replace(/[^a-zA-Z0-9_\-]/g, ''));
    grupoWhere = 'AND p.idgrupo = @cb_idgrupo';
  }

  const selects = terms.map((term, index) => {
    req.input(`cb_term_${index}`, sql.NVarChar(120), term);
    req.input(`cb_like_${index}`, sql.NVarChar(140), `%${term}%`);
    return `
      SELECT
        @cb_term_${index} AS termino,
        SUM(CASE WHEN YEAR(c.fecha) = @cb_anio_actual THEN cd.cantidad ELSE 0 END) AS cantidad_actual,
        ROUND(SUM(CASE WHEN YEAR(c.fecha) = @cb_anio_actual THEN cd.cantidad * cd.precio ELSE 0 END), 2) AS importe_actual,
        SUM(CASE WHEN YEAR(c.fecha) = @cb_anio_anterior THEN cd.cantidad ELSE 0 END) AS cantidad_anterior,
        ROUND(SUM(CASE WHEN YEAR(c.fecha) = @cb_anio_anterior THEN cd.cantidad * cd.precio ELSE 0 END), 2) AS importe_anterior
      FROM ${db()}cheqdet cd
      INNER JOIN ${db()}cheques c ON cd.foliodet = c.folio
      INNER JOIN ${db()}productos p ON cd.idproducto = p.idproducto
      WHERE YEAR(c.fecha) IN (@cb_anio_actual, @cb_anio_anterior)
        AND c.cancelado = 0 AND c.pagado = 1
        AND (ISNULL(cd.modificador, 0) = 0 OR ISNULL(cd.precio, 0) > 0)
        AND p.descripcion LIKE @cb_like_${index}
        ${grupoWhere}
    `;
  }).join('\nUNION ALL\n');

  const r = await req.query(selects);
  const orderKey = criterio === 'cantidad' ? 'cantidad_actual' : 'importe_actual';
  return r.recordset
    .map(row => ({
      termino: row.termino,
      cantidad_actual: Number(row.cantidad_actual || 0),
      importe_actual: Number(row.importe_actual || 0),
      cantidad_anterior: Number(row.cantidad_anterior || 0),
      importe_anterior: Number(row.importe_anterior || 0),
    }))
    .sort((a, b) => b[orderKey] - a[orderKey]);
}

async function getVentasMesAMes({ anioActual, anioAnterior }) {
  const pool  = await getPool();
  const meses = ['Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic'];

  const query = (anio, prefix) => {
    const req = pool.request();
    req.input(`${prefix}_anio`, sql.Int, parseInt(anio));
    return req.query(`
      SELECT MONTH(fecha) AS mes, ROUND(SUM(total),2) AS venta, COUNT(*) AS tickets
      FROM ${db()}cheques
      WHERE YEAR(fecha)=@${prefix}_anio AND cancelado=0 AND pagado=1 AND total>0
      GROUP BY MONTH(fecha) ORDER BY mes
    `);
  };

  const [rActual, rAnterior] = await Promise.all([query(anioActual, 'mma'), query(anioAnterior, 'mmb')]);

  const mapActual   = {};
  const mapAnterior = {};
  rActual.recordset.forEach(r   => { mapActual[r.mes]   = r; });
  rAnterior.recordset.forEach(r => { mapAnterior[r.mes] = r; });

  return meses.map((nombre, i) => {
    const m = i + 1;
    return {
      mes:              nombre,
      venta_actual:     mapActual[m]?.venta    || 0,
      tickets_actual:   mapActual[m]?.tickets  || 0,
      venta_anterior:   mapAnterior[m]?.venta   || 0,
      tickets_anterior: mapAnterior[m]?.tickets || 0,
    };
  });
}
async function getVentasPorAnio() {
  const pool = await getPool();
  try {
    const r = await pool.request().query(`
    SELECT YEAR(fecha) AS anio, COUNT(*) AS tickets,
      ROUND(SUM(total),2) AS venta_total, ROUND(AVG(total),2) AS ticket_promedio
    FROM ${db()}cheques
    WHERE cancelado=0 AND pagado=1 AND total>0
    GROUP BY YEAR(fecha)
    ORDER BY anio ASC
  `);
    return r.recordset;
  } catch(_) { return []; }
}

async function getVentasDiaComparativo({ anioActual, anioAnterior, mes = null }) {
  const pool = await getPool();

  const query = (anio, prefix) => {
    const req = pool.request();
    req.input(`${prefix}_anio`, sql.Int, parseInt(anio));
    let mesWhere = '';
    if (mes) {
      req.input(`${prefix}_mes`, sql.Int, parseInt(mes));
      mesWhere = `AND MONTH(fecha) = @${prefix}_mes`;
    }
    return req.query(`
      SELECT DAY(fecha) AS dia, ROUND(SUM(total),2) AS venta, COUNT(*) AS tickets
      FROM ${db()}cheques
      WHERE YEAR(fecha)=@${prefix}_anio ${mesWhere} AND cancelado=0 AND pagado=1 AND total>0
      GROUP BY DAY(fecha) ORDER BY dia
    `);
  };

  const [rActual, rAnterior] = await Promise.all([query(anioActual, 'dca'), query(anioAnterior, 'dcb')]);

  const mapActual   = {};
  const mapAnterior = {};
  rActual.recordset.forEach(r   => { mapActual[r.dia]   = r; });
  rAnterior.recordset.forEach(r => { mapAnterior[r.dia] = r; });

  return Array.from({ length: 31 }, (_, i) => {
    const d = i + 1;
    if (!mapActual[d] && !mapAnterior[d]) return null;
    return {
      dia:              d,
      venta_actual:     mapActual[d]?.venta    || 0,
      tickets_actual:   mapActual[d]?.tickets  || 0,
      venta_anterior:   mapAnterior[d]?.venta   || 0,
      tickets_anterior: mapAnterior[d]?.tickets || 0,
    };
  }).filter(Boolean);
}

async function getVentasGrupoComparativo({ anioActual, anioAnterior }) {
  const pool = await getPool();

  const query = (anio, prefix) => {
    const req = pool.request();
    req.input(`${prefix}_anio`, sql.Int, parseInt(anio));
    return req.query(`
      SELECT ISNULL(g.descripcion,'Sin grupo') AS grupo,
        SUM(cd.cantidad) AS cantidad, ROUND(SUM(cd.cantidad * cd.precio),2) AS importe
      FROM ${db()}cheqdet cd
      INNER JOIN ${db()}cheques   c ON cd.foliodet   = c.folio
      INNER JOIN ${db()}productos p ON cd.idproducto = p.idproducto
      LEFT  JOIN ${db()}grupos    g ON p.idgrupo     = g.idgrupo
      WHERE YEAR(c.fecha)=@${prefix}_anio AND c.cancelado=0 AND c.pagado=1 AND (ISNULL(cd.modificador, 0)=0 OR ISNULL(cd.precio, 0)>0)
      GROUP BY g.descripcion
      ORDER BY importe DESC
    `);
  };

  const [rActual, rAnterior] = await Promise.all([query(anioActual, 'gca'), query(anioAnterior, 'gcb')]);

  const grupos = [...new Set([
    ...rActual.recordset.map(r => r.grupo),
    ...rAnterior.recordset.map(r => r.grupo),
  ])];

  const mapActual   = {};
  const mapAnterior = {};
  rActual.recordset.forEach(r   => { mapActual[r.grupo]   = r; });
  rAnterior.recordset.forEach(r => { mapAnterior[r.grupo] = r; });

  return grupos.map(g => ({
    grupo:            g,
    cantidad_actual:  mapActual[g]?.cantidad  || 0,
    importe_actual:   mapActual[g]?.importe   || 0,
    cantidad_anterior:mapAnterior[g]?.cantidad || 0,
    importe_anterior: mapAnterior[g]?.importe  || 0,
  })).sort((a, b) => b.importe_actual - a.importe_actual);
}

module.exports = {
  getAniosDisponibles,
  getGrupos,
  getTopProductos,
  getComparativoProductos,
  getComparativoBusqueda,
  getVentasMesAMes,
  getVentasPorAnio,
  getVentasDiaComparativo,
  getVentasGrupoComparativo,
};
