'use strict';

const dashSvc     = require('./dashboardService');
const reportesSvc  = require('./reportesService');
const retirosSvc   = require('./retirosCajaService');
const inventarioSvc = require('./inventarioService');
const comprasSvc    = require('./comprasService');
const gastosSvc     = require('./gastosService');
const metasSvc      = require('./metasService');
const pagosSvc      = require('./pagosService');
const turnoSvc      = require('./turnoService');
const insumosSvc    = require('./insumosService');
const mantenimientoSvc = require('./mantenimientoService');
const productosAdminSvc = require('./productosAdminService');
const cache       = require('../utils/cache');
const { localDateStr } = require('../utils/localDate');

const TTL = {
  snapshot:        parseInt(process.env.CACHE_TTL_SNAPSHOT)   || 4000,
  // OPT-4: datos históricos (ayer, semana pasada) no cambian durante el día.
  // Cachearlos 1 hora elimina ~2 queries SQL por cada poll de ventas-por-hora.
  historico:       60 * 60 * 1000,
  resumen:         parseInt(process.env.CACHE_TTL_METRICAS)   || 5000,
  ventasHora:      parseInt(process.env.CACHE_TTL_GRAFICAS)   || 5000,
  ventasDia:       parseInt(process.env.CACHE_TTL_HISTORICO_DIARIO) || 60000,
  ventasMes:       parseInt(process.env.CACHE_TTL_MES)        || 60000,
  formasPago:      parseInt(process.env.CACHE_TTL_PAGOS)      || 10000,
  ventasServicio:  parseInt(process.env.CACHE_TTL_SERVICIO)   || 10000,
  topProductos:    parseInt(process.env.CACHE_TTL_PRODUCTOS)  || 10000,
  mesasAbiertas:   parseInt(process.env.CACHE_TTL_MESAS)      || 5000,
  cancelaciones:   parseInt(process.env.CACHE_TTL_CANCEL)     || 5000,
  movimientosCaja: parseInt(process.env.CACHE_TTL_CAJA)       || 15000,
};

function keyResumen(turno, fecha, desde, hasta, estacion) {
  return `dash:resumen:${turno||''}:${fecha||''}:${desde||''}:${hasta||''}:${estacion||''}`;
}

function keyVentasHora(turno, fecha, desde, hasta, estacion) {
  return `dash:ventas_hora:${turno||''}:${fecha||''}:${desde||''}:${hasta||''}:${estacion||''}`;
}

function keyVentasDia(turno, fecha, desde, hasta, estacion) {
  return `dash:ventas_dia:${turno||''}:${fecha||''}:${desde||''}:${hasta||''}:${estacion||''}`;
}

function keyServicio(turno, fecha, desde, hasta, estacion) {
  return `dash:servicio:${turno||''}:${fecha||''}:${desde||''}:${hasta||''}:${estacion||''}`;
}

function keyFormasPago(turno, fecha, desde, hasta, estacion) {
  return `dash:formas_pago:${turno||''}:${fecha||''}:${desde||''}:${hasta||''}:${estacion||''}`;
}

async function getResumen(turno, fecha, fechaDesde, fechaHasta, estacion) {
  const key = keyResumen(turno, fecha, fechaDesde, fechaHasta, estacion);
  return cache.getOrSet(key, () => dashSvc.getResumen(turno, fecha, fechaDesde, fechaHasta, estacion), TTL.resumen);
}

async function getMesasAbiertas() {
  return cache.getOrSet('dash:mesas', () => dashSvc.getMesasAbiertas(), TTL.mesasAbiertas);
}

async function getDomiciliosActivos() {
  return cache.getOrSet('dash:domicilios', () => dashSvc.getDomiciliosActivos(), TTL.mesasAbiertas);
}

async function getVentasPorHora(turno, fecha, fechaDesde, fechaHasta, estacion) {
  const key = keyVentasHora(turno, fecha, fechaDesde, fechaHasta, estacion);
  return cache.getOrSet(key, () => dashSvc.getVentasPorHora(turno, fecha, fechaDesde, fechaHasta, estacion), TTL.ventasHora);
}

async function getVentasPorDia(turno, fecha, fechaDesde, fechaHasta, estacion) {
  const key = keyVentasDia(turno, fecha, fechaDesde, fechaHasta, estacion);
  return cache.getOrSet(key, () => dashSvc.getVentasPorDia(turno, fecha, fechaDesde, fechaHasta, estacion), TTL.ventasDia);
}

function historicalWindow() {
  const days = Math.min(90, Math.max(7, Number(process.env.ARKHON_DASHBOARD_HISTORY_DAYS || 31)));
  const end = new Date();
  const start = new Date(end);
  start.setDate(start.getDate() - (days - 1));
  return { start: localDateStr(start), end: localDateStr(end) };
}

async function getTopProductos(turno, fecha, fechaDesde, fechaHasta, estacion) {
  const key = `dash:top_productos:${turno||''}:${fecha||''}:${fechaDesde||''}:${fechaHasta||''}:${estacion||''}`;
  return cache.getOrSet(key, () => dashSvc.getTopProductos(turno, fecha, fechaDesde, fechaHasta, estacion), TTL.topProductos);
}

async function getTopProductosPorGrupo(turno, fecha, fechaDesde, fechaHasta, estacion) {
  const key = `dash:top_grupos:${turno||''}:${fecha||''}:${fechaDesde||''}:${fechaHasta||''}:${estacion||''}`;
  return cache.getOrSet(key, () => dashSvc.getTopProductosPorGrupo(turno, fecha, fechaDesde, fechaHasta, estacion), TTL.topProductos);
}

async function getVentasPorServicio(turno, fecha, fechaDesde, fechaHasta, estacion) {
  const key = keyServicio(turno, fecha, fechaDesde, fechaHasta, estacion);
  return cache.getOrSet(key, () => dashSvc.getVentasPorServicio(turno, fecha, fechaDesde, fechaHasta, estacion), TTL.ventasServicio);
}

async function getFormasPago(turno, fecha, fechaDesde, fechaHasta, estacion) {
  const key = keyFormasPago(turno, fecha, fechaDesde, fechaHasta, estacion);
  return cache.getOrSet(key, () => dashSvc.getFormasPago(turno, fecha, fechaDesde, fechaHasta, estacion), TTL.formasPago);
}

async function getCancelacionesTurnoActual() {
  return cache.getOrSet(
    'dash:cancelaciones_turno',
    () => reportesSvc.getCancelacionesTurnoActual(),
    TTL.cancelaciones,
  );
}

async function getMovimientosCajaHoy() {
  return cache.getOrSet(
    'dash:movimientos_caja',
    () => retirosSvc.getEnVivo(),
    TTL.movimientosCaja,
  );
}

async function getVentasMes(fecha) {
  const key = `dash:ventas_mes:${fecha || 'hoy'}`;
  return cache.getOrSet(key, () => dashSvc.getVentasMes(fecha), TTL.ventasMes);
}

async function getTopProductosEnCurso() {
  return cache.getOrSet(
    'dash:top_productos_curso',
    () => dashSvc.getTopProductosEnCurso(),
    TTL.topProductos,
  );
}

async function getTopProductosPorGrupoEnCurso() {
  return cache.getOrSet(
    'dash:top_grupos_curso',
    () => dashSvc.getTopProductosEnCursoPorGrupo(),
    TTL.topProductos,
  );
}

async function safeModule(fn, fallback) {
  try {
    const value = await fn();
    return value === undefined ? fallback : value;
  } catch (_) {
    return fallback;
  }
}

async function getWebModulesSnapshot(base = {}) {
  const hoy = localDateStr();
  const periodo = { inicio: hoy, fin: hoy, page: 1, pageSize: 50 };
  const turnoActivo = await safeModule(() => turnoSvc.getTurnoActivo(), null);
  const idturno = turnoActivo?.idturno || null;
  const turnoParams = idturno ? { idturno, page: 1, pageSize: 50 } : periodo;

  const [
    inventarioResumen, inventarioEnVivo, inventarioExistencias, inventarioMovimientos,
    compras, proveedores, comprasStock, gastosResumen, gastosConcepto, gastosMovimientos,
    retirosResumen, retirosConcepto, retirosMovimientos, metas, pagosCatalogo, pagosPeriodo, turnoMetricas,
    turnoHoras, turnoMeseros, insumosLista, insumosGrupos, insumosRecetas, productosCatalogo,
    productosGrupos, productosAreas, mantenimientoCuentas, reportesVentas, reportesCorteX, reportesCancelados,
    reportesProductosCancelados, reportesCortesias, reportesCortesiasCatalogos,
  ] = await Promise.all([
    safeModule(() => inventarioSvc.getResumenInventario(), null),
    safeModule(() => inventarioSvc.getSnapshotEnVivo(), null),
    safeModule(() => inventarioSvc.getExistencias(null), []),
    safeModule(() => inventarioSvc.getMovimientos(null, 7, 1, 100), { movimientos: [], total: 0 }),
    safeModule(() => comprasSvc.getCompras(periodo), []),
    safeModule(() => comprasSvc.getProveedores(), []),
    safeModule(() => comprasSvc.getStockActual(), []),
    safeModule(() => gastosSvc.getResumen(turnoParams), {}),
    safeModule(() => gastosSvc.getPorConcepto(turnoParams), []),
    safeModule(() => gastosSvc.getMovimientos(turnoParams), { movimientos: [], total: 0 }),
    safeModule(() => retirosSvc.getResumen(turnoParams), {}),
    safeModule(() => retirosSvc.getPorConcepto(turnoParams), []),
    safeModule(() => retirosSvc.getMovimientos(turnoParams), { movimientos: [], total: 0 }),
    safeModule(() => metasSvc.getMetas(), {}),
    safeModule(() => pagosSvc.getCatalogoFormasPago(), []),
    safeModule(() => pagosSvc.getFormasPago({ ini: hoy, fin: hoy }), { resumen: [], horas: [], detalle: [], totales: {} }),
    safeModule(() => turnoSvc.getMetricasTurno(idturno), null),
    safeModule(() => turnoSvc.getVentasPorHora(idturno), []),
    safeModule(() => turnoSvc.getTopMeseros(idturno), []),
    safeModule(() => insumosSvc.getInsumos(), []),
    safeModule(() => insumosSvc.getGrupos(), []),
    safeModule(() => insumosSvc.getRecetas(), []),
    safeModule(() => productosAdminSvc.getCatalogo({ page: 1, pageSize: 100 }), {}),
    safeModule(() => productosAdminSvc.getGrupos(), []),
    safeModule(() => productosAdminSvc.getAreas(), []),
    safeModule(() => mantenimientoSvc.getCuentasPeriodo({ inicio: hoy, fin: hoy }), []),
    safeModule(() => reportesSvc.getReporteData(hoy, hoy), null),
    safeModule(() => reportesSvc.getCorteX(idturno), null),
    safeModule(() => reportesSvc.getCancelacionesTurnoActual(), []),
    safeModule(() => reportesSvc.getProductosCancelados(hoy, hoy, null, 1, 50), []),
    safeModule(() => reportesSvc.getCortesias({ inicio: hoy, fin: hoy }), []),
    safeModule(() => reportesSvc.getCortesiasCatalogos(), {}),
  ]);

  return {
    version: 1,
    generated_at: new Date().toISOString(),
    dashboard: {
      resumen: base.resumen || null,
      ventas_por_hora: base.ventas_por_hora || [],
      ventas_por_dia: base.ventas_por_dia || [],
      ventas_por_hora_metricas: base.ventas_por_hora_metricas || {},
      top_productos: base.top_productos || [],
      top_productos_curso: base.top_productos_curso || [],
      por_grupo: base.por_grupo || [],
      por_grupo_curso: base.por_grupo_curso || [],
      ventas_servicio: base.ventas_servicio || [],
      formas_pago: base.formas_pago || [],
      ventas_mes: base.ventas_mes || [],
      domicilios_activos: base.domicilios_activos || [],
      movimientos_caja: base.movimientos_caja || {},
      cancelaciones_turno: base.cancelaciones_turno || [],
    },
    mesas: {
      abiertas: base.mesas || [],
    },
    inventario: {
      resumen: inventarioResumen,
      en_vivo: inventarioEnVivo,
      existencias: inventarioExistencias,
      movimientos: inventarioMovimientos,
    },
    compras: {
      compras,
      proveedores,
      stock: comprasStock,
    },
    gastos: {
      resumen: gastosResumen,
      por_concepto: gastosConcepto,
      movimientos: gastosMovimientos,
    },
    retiros_caja: {
      resumen: retirosResumen,
      por_concepto: retirosConcepto,
      movimientos: retirosMovimientos,
    },
    metas,
    pagos: {
      catalogo: pagosCatalogo,
      formas_pago: pagosPeriodo,
    },
    turnos: {
      activo: turnoActivo,
      metricas: turnoMetricas,
      ventas_por_hora: turnoHoras,
      meseros: turnoMeseros,
    },
    insumos: {
      lista: insumosLista,
      grupos: insumosGrupos,
      recetas: insumosRecetas,
    },
    productos_admin: {
      catalogo: productosCatalogo,
      grupos: productosGrupos,
      areas: productosAreas,
    },
    mantenimiento: {
      cuentas: mantenimientoCuentas,
    },
    reportes: {
      ventas: reportesVentas,
      corte_x: reportesCorteX,
      cancelaciones: reportesCancelados,
      productos_cancelados: reportesProductosCancelados,
      cortesias: reportesCortesias,
      cortesias_catalogos: reportesCortesiasCatalogos,
    },
  };
}

async function getSnapshotCompleto() {
  // OPT-LOWMEM: en equipos con ≤4GB, 12 queries SQL paralelas causan un pico de
  // memoria alto (~40-60MB de objetos temporales de mssql + recordsets simultáneos).
  // Solución: ejecutar en 3 oleadas de 4 queries. El cache de getOrSet evita queries
  // reales si los datos están frescos, así que el costo de secuencializar es mínimo.
  const _lm = process.env.LOW_MEMORY === 'true';

  let resumen, mesasAbiertas, ventasHoraResult, ventasDia, topProductos, ventasServicio;
  let formasPago, ventasMes, porGrupo, cancelaciones;
  let domicilios, movimientosCaja, topProductosCurso, porGrupoCurso;
  const history = historicalWindow();

  if (_lm) {
    // Oleada 1: datos críticos del dashboard principal
    [resumen, mesasAbiertas, ventasHoraResult, ventasDia, topProductos, topProductosCurso] = await Promise.all([
      getResumen(), getMesasAbiertas(), getVentasPorHora(), getVentasPorDia(null, null, history.start, history.end), getTopProductos(), getTopProductosEnCurso(),
    ]);
    // Oleada 2: datos secundarios
    [ventasServicio, formasPago, cancelaciones, movimientosCaja] = await Promise.all([
      getVentasPorServicio(), getFormasPago(), getCancelacionesTurnoActual(), getMovimientosCajaHoy(),
    ]);
    // Oleada 3: datos terciarios (menos urgentes)
    [ventasMes, porGrupo, domicilios, porGrupoCurso] = await Promise.all([
      getVentasMes(), getTopProductosPorGrupo(), getDomiciliosActivos(), getTopProductosPorGrupoEnCurso(),
    ]);
  } else {
    // Normal: 12 queries en paralelo (equipos con 8GB+)
    [resumen, mesasAbiertas, ventasHoraResult, ventasDia, topProductos,
     ventasServicio, formasPago, ventasMes, porGrupo, cancelaciones,
     domicilios, movimientosCaja, topProductosCurso, porGrupoCurso] = await Promise.all([
      getResumen(),
      getMesasAbiertas(),
      getVentasPorHora(),
      getVentasPorDia(null, null, history.start, history.end),
      getTopProductos(),
      getVentasPorServicio(),
      getFormasPago(),
      getVentasMes(),
      getTopProductosPorGrupo(),
      getCancelacionesTurnoActual(),
      getDomiciliosActivos(),
      getMovimientosCajaHoy(),
      getTopProductosEnCurso(),
      getTopProductosPorGrupoEnCurso(),
    ]);
  }

  const snapshotMesas = Array.isArray(mesasAbiertas) ? mesasAbiertas : [];

  const snapshot = {
    tipo:  'completa',
    hora:  new Date().toLocaleTimeString('es-MX'),
    fecha: resumen.fecha_hoy,
    resumen: { ...resumen, mesas: snapshotMesas },
    mesas: snapshotMesas,
    ventas_por_hora:          ventasHoraResult.filas,
    ventas_por_dia:           Array.isArray(ventasDia) ? ventasDia : [],
    ventas_por_hora_metricas: ventasHoraResult.metricas,
    top_productos:            topProductos,
    top_productos_curso:      topProductosCurso,
    por_grupo_curso:          porGrupoCurso,
    ventas_servicio:          ventasServicio,
    formas_pago:              formasPago,
    ventas_mes:               ventasMes,
    por_grupo:                porGrupo,
    cancelaciones_turno:      cancelaciones,
    domicilios_activos:       domicilios,
    movimientos_caja:         movimientosCaja,
    _cache: {
      activo:   process.env.CACHE_ENABLED !== 'false',
      generado: new Date().toISOString(),
    },
  };
  if (process.env.ARKHON_SOCKET_WEB_MODULES === 'false') {
    snapshot.web_modules = {
      version: 1,
      generated_at: new Date().toISOString(),
      dashboard: {
        resumen: snapshot.resumen,
        ventas_por_hora: snapshot.ventas_por_hora,
        ventas_por_dia: snapshot.ventas_por_dia,
        top_productos: snapshot.top_productos,
        top_productos_curso: snapshot.top_productos_curso,
        ventas_servicio: snapshot.ventas_servicio,
        formas_pago: snapshot.formas_pago,
      },
      mesas: { abiertas: snapshot.mesas },
      deferred: true,
    };
  } else {
    snapshot.web_modules = await getWebModulesSnapshot(snapshot);
  }
  return snapshot;
}

function invalidarCache() {
  cache.invalidate('dash:');
}

function invalidarMesas() {
  cache.invalidateKey('dash:mesas');
}

function invalidarDomicilios() {
  cache.invalidateKey('dash:domicilios');
}

module.exports = {
  getResumen,
  getMesasAbiertas,
  getDomiciliosActivos,
  getVentasPorHora,
  getVentasPorDia,
  getTopProductos,
  getTopProductosEnCurso,
  getTopProductosPorGrupo,
  getTopProductosPorGrupoEnCurso,
  getVentasPorServicio,
  getFormasPago,
  getVentasMes,
  getSnapshotCompleto,
  getCancelacionesTurnoActual,
  getMovimientosCajaHoy,
  getWebModulesSnapshot,
  invalidarCache,
  invalidarMesas,
  invalidarDomicilios,
};
