// app/src/index.js
import chalk from 'chalk'; // Colores!
import favicon from 'serve-favicon'; // Favicon
import express from 'express';
import expressLayouts from 'express-ejs-layouts';
import cors from 'cors';
import { Pool } from 'pg';

// Rutas
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Variables de Entorno
import dotenv from 'dotenv';

// Cargar .env según entorno
if (process.env.NODE_ENV === 'development') {
  dotenv.config({ path: path.resolve(__dirname, '../.env.development') });
} else if (process.env.NODE_ENV === 'test') {
  dotenv.config({ path: path.resolve(__dirname, '../.env.test') });
} else if (process.env.NODE_ENV === 'production') {
  dotenv.config({ path: path.resolve(__dirname, '../.env.production') });
} else {
  dotenv.config(); // .env por defecto
}

// ----------------------------------------------------------
// App
// ----------------------------------------------------------
const app = express();
app.set('trust proxy', true);
app.use(cors());
app.use(express.json());
app.use(express.json({ limit: '1mb' }));
app.use(express.static(path.join(__dirname, 'pages')));


// ----------------------------------------------------------
// Motor de vistas EJS
// ----------------------------------------------------------
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");
app.use(expressLayouts);
app.set("layout", "layouts/main");

// Archivos estáticos
app.use(express.static(path.join(__dirname, "public")));

app.use('/favicon', express.static(path.join(__dirname, 'public', 'favicon'), {
  maxAge: '1y'
}));

app.use(favicon(path.join(__dirname, 'public', 'favicon', 'favicon.ico'), {
  maxAge: '1y'
}));

const url = v => !v ? "" : (v.startsWith("http") ? v : `/img/productos/${v}`);

// ----------------------------------------------------------
// Configuración de conexión PostgreSQL
// ----------------------------------------------------------
const dbConfig = {
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASS,
  database: process.env.DB_NAME,
  port: process.env.DB_LOCAL_PORT ? Number(process.env.DB_LOCAL_PORT) : undefined,
  ssl: process.env.PGSSL === 'true' ? { rejectUnauthorized: false } : undefined,
};

const pool = new Pool(dbConfig);

// ----------------------------------------------------------
// Seguridad: Tablas permitidas
// ----------------------------------------------------------
const ALLOWED_TABLES = [
  'roles','usuarios','usua_roles',
  'categorias','productos',
  'clientes','mesas',
  'comandas','deta_comandas',
  'proveedores','compras','deta_comp_producto',
  'mate_primas','deta_comp_materias',
  'prov_producto','prov_mate_prima',
  'receta_producto', 'asistencia_resumen_diario',
  'asistencia_intervalo', 'vw_compras'
];

const VALID_IDENT = /^[a-zA-Z_][a-zA-Z0-9_]*$/;

// Identificadores SQL -> comillas dobles y escape correcto
const q = (s) => `"${String(s).replace(/"/g, '""')}"`;

function ensureTable(name) {
  const t = String(name || '').toLowerCase();
  if (!ALLOWED_TABLES.includes(t)) throw new Error('Tabla no permitida');
  return t;
}

async function getClient() {
  const client = await pool.connect();
  return client;
}

// ----------------------------------------------------------
// Introspección de esquema
// ----------------------------------------------------------
async function loadColumns(client, table) {
  const sql = `
    SELECT
      c.column_name,
      c.data_type,
      c.is_nullable = 'YES' AS is_nullable,
      c.column_default,
      (SELECT EXISTS (
         SELECT 1 FROM pg_attribute a
         JOIN pg_class t ON t.oid = a.attrelid
         JOIN pg_index i ON i.indrelid = t.oid AND a.attnum = ANY(i.indkey)
         WHERE t.relname = $1 AND i.indisprimary AND a.attname = c.column_name
      )) AS is_primary,
      (SELECT a.attgenerated = 's' OR a.attidentity IN ('a','d')
       FROM pg_attribute a
       JOIN pg_class t ON t.oid = a.attrelid
       WHERE t.relname = $1 AND a.attname = c.column_name
      ) AS is_identity
    FROM information_schema.columns c
    WHERE c.table_schema='public' AND c.table_name=$1
    ORDER BY c.ordinal_position
  `;
  const { rows } = await client.query(sql, [table]);
  return rows;
}

async function loadForeignKeys(client, table) {
  const sql = `
    SELECT
      kcu.column_name,
      ccu.table_name AS foreign_table,
      ccu.column_name AS foreign_column
    FROM information_schema.table_constraints tc
    JOIN information_schema.key_column_usage kcu
      ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
    JOIN information_schema.constraint_column_usage ccu
      ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema
    WHERE tc.table_schema='public' AND tc.table_name=$1 AND tc.constraint_type='FOREIGN KEY'
  `;
  const { rows } = await client.query(sql, [table]);
  const map = {};
  for (const r of rows) map[r.column_name] = { foreign_table: r.foreign_table, foreign_column: r.foreign_column };
  return map;
}

async function loadPrimaryKey(client, table) {
  const sql = `
    SELECT a.attname AS column_name
    FROM pg_index i
    JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
    JOIN pg_class t ON t.oid = i.indrelid
    WHERE t.relname = $1 AND i.indisprimary
  `;
  const { rows } = await client.query(sql, [table]);
  return rows.map(r => r.column_name);
}

// label column for FK options
async function pickLabelColumn(client, refTable) {
  const preferred = ['nombre','raz_social','apodo','documento','correo','telefono'];
  const { rows } = await client.query(
    `SELECT column_name, data_type
     FROM information_schema.columns
     WHERE table_schema='public' AND table_name=$1
     ORDER BY ordinal_position`, [refTable]
  );
  for (const cand of preferred) {
    if (rows.find(r => r.column_name === cand)) return cand;
  }
  const textish = rows.find(r => /text|character varying|varchar/i.test(r.data_type));
  if (textish) return textish.column_name;
  return rows[0]?.column_name || 'id';
}

// ----------------------------------------------------------
// Middleware para datos globales
// ----------------------------------------------------------
app.use((req, res, next) => {
  res.locals.currentPath = req.path;
  res.locals.pageTitle = "SuiteCoffee"; 
  res.locals.pageId = "";               
  next();
});


// ----------------------------------------------------------
// Rutas de UI
// ----------------------------------------------------------

app.get("/", (req, res) => {
  res.locals.pageTitle = "Dashboard";
  res.locals.pageId = "home"; // para el sidebar contextual
  res.render("dashboard");
});

app.get("/dashboard", (req, res) => {
  res.locals.pageTitle = "Dashboard";
  res.locals.pageId = "dashboard";   // <- importante
  res.render("dashboard");
});

// app.get('/', (req, res) => {
//   res.sendFile(path.join(__dirname, 'pages', 'dashboard.html'));
// });

app.get("/comandas", (req, res) => {
  res.locals.pageTitle = "Comandas";
  res.locals.pageId = "comandas"; // <- importante para el sidebar contextual
  res.render("comandas");
});

// app.get('/comandas', (req, res) => {
//   res.sendFile(path.join(__dirname, 'pages', 'comandas.html'));
// });

app.get("/estadoComandas", (req, res) => {
  res.locals.pageTitle = "Estado de Comandas";
  res.locals.pageId = "estadoComandas";
  res.render("estadoComandas");
});

// app.get('/estadoComandas', (req, res) => {
//   res.sendFile(path.join(__dirname, 'pages', 'estadoComandas.html'));
// });

app.get("/productos", (req, res) => {
  res.locals.pageTitle = "Productos";
  res.locals.pageId = "productos";
  res.render("productos");
});

app.get('/usuarios', (req, res) => {
  res.locals.pageTitle = 'Usuarios';
  res.locals.pageId = 'usuarios';
  res.render('usuarios');
});

app.get('/reportes', (req, res) => {
  res.locals.pageTitle = 'Reportes';
  res.locals.pageId = 'reportes';
  res.render('reportes');
});

app.get('/compras', (req, res) => {
  res.locals.pageTitle = 'Compras';
  res.locals.pageId = 'compras';
  res.render('compras');
});

// ----------------------------------------------------------
// API
// ----------------------------------------------------------
app.get('/api/tables', async (_req, res) => {
  res.json(ALLOWED_TABLES);
});

app.get('/api/schema/:table', async (req, res) => {
  try {
    const table = ensureTable(req.params.table);
    const client = await getClient();
    try {
      const columns = await loadColumns(client, table);
      const fks = await loadForeignKeys(client, table);
      const enriched = columns.map(c => ({ ...c, foreign: fks[c.column_name] || null }));
      res.json({ table, columns: enriched });
    } finally { client.release(); }
  } catch (e) {
    res.status(400).json({ error: e.message });
  }
});

app.get('/api/options/:table/:column', async (req, res) => {
  try {
    const table = ensureTable(req.params.table);
    const column = req.params.column;
    if (!VALID_IDENT.test(column)) throw new Error('Columna inválida');

    const client = await getClient();
    try {
      const fks = await loadForeignKeys(client, table);
      const fk = fks[column];
      if (!fk) return res.json([]);

      const refTable = fk.foreign_table;
      const refId = fk.foreign_column;
      const labelCol = await pickLabelColumn(client, refTable);

      const sql = `SELECT ${q(refId)} AS id, ${q(labelCol)} AS label FROM ${q(refTable)} ORDER BY ${q(labelCol)} LIMIT 1000`;
      const result = await client.query(sql);
      res.json(result.rows);
    } finally { client.release(); }
  } catch (e) {
    res.status(400).json({ error: e.message });
  }
});

app.get('/api/table/:table', async (req, res) => {
  try {
    const table = ensureTable(req.params.table);
    const limit = Math.min(parseInt(req.query.limit || '100', 10), 1000);
    const client = await getClient();
    try {
      const pks = await loadPrimaryKey(client, table);
      const orderBy = pks.length ? `ORDER BY ${pks.map(q).join(', ')} DESC` : '';
      const sql = `SELECT * FROM ${q(table)} ${orderBy} LIMIT ${limit}`;
      const result = await client.query(sql);

      // Normalizar: siempre devolver objetos {col: valor}
      const colNames = result.fields.map(f => f.name);
      let rows = result.rows;
      if (rows.length && Array.isArray(rows[0])) {
        rows = rows.map(r => Object.fromEntries(r.map((v, i) => [colNames[i], v])));
      }
      res.json(rows);
    } finally { client.release(); }
  } catch (e) {
    res.status(400).json({ error: e.message, code: e.code, detail: e.detail });
  }
});

app.post('/api/table/:table', async (req, res) => {
  const table = ensureTable(req.params.table);
  const payload = req.body || {};
  try {
    const client = await getClient();
    try {
      const columns = await loadColumns(client, table);
      const insertable = columns.filter(c =>
        !c.is_primary && !c.is_identity && !(c.column_default || '').startsWith('nextval(')
      );
      const allowedCols = new Set(insertable.map(c => c.column_name));

      const cols = [];
      const vals = [];
      const params = [];
      let idx = 1;
      for (const [k, v] of Object.entries(payload)) {
        if (!allowedCols.has(k)) continue;
        if (!VALID_IDENT.test(k)) continue;
        cols.push(q(k));
        vals.push(`$${idx++}`);
        params.push(v);
      }

      if (!cols.length) {
        const { rows } = await client.query(`INSERT INTO ${q(table)} DEFAULT VALUES RETURNING *`);
        res.status(201).json({ inserted: rows[0] });
      } else {
        const { rows } = await client.query(
          `INSERT INTO ${q(table)} (${cols.join(', ')}) VALUES (${vals.join(', ')}) RETURNING *`,
          params
        );
        res.status(201).json({ inserted: rows[0] });
      }
    } catch (e) {
      if (e.code === '23503') return res.status(400).json({ error: 'Violación de clave foránea', detail: e.detail });
      if (e.code === '23505') return res.status(400).json({ error: 'Violación de unicidad', detail: e.detail });
      if (e.code === '23514') return res.status(400).json({ error: 'Violación de CHECK', detail: e.detail });
      if (e.code === '23502') return res.status(400).json({ error: 'Campo NOT NULL faltante', detail: e.detail });
      throw e;
    }
  } catch (e) {
    res.status(400).json({ error: e.message });
  }
});

app.get('/api/comandas', async (req, res, next) => {
  try {
    const estado = (req.query.estado || '').trim() || null;
    const limit  = Math.min(parseInt(req.query.limit || '200', 10), 1000);

    const { rows } = await pool.query(
      `SELECT * FROM public.f_comandas_resumen($1, $2)`,
      [estado, limit]
    );
    res.json(rows);
  } catch (e) { next(e); }
});


// app.get('/api/comandas', async (req, res, next) => {
//   try {
//     const estado = (req.query.estado || '').trim();
//     const limit = Math.min(parseInt(req.query.limit || '200', 10), 1000);
//     const params = [];
//     let where = '';
//     if (estado) { params.push(estado); where = `WHERE c.estado = $${params.length}`; }
//     params.push(limit);

//     const sql = `
//       WITH items AS (
//         SELECT d.id_comanda,
//                COUNT(*) AS items,
//                SUM(d.cantidad * d.pre_unitario) AS total
//         FROM deta_comandas d
//         GROUP BY d.id_comanda
//       )
//       SELECT
//         c.id_comanda, c.fec_creacion, c.estado, c.observaciones,
//         u.id_usuario, u.nombre AS usuario_nombre, u.apellido AS usuario_apellido,
//         m.id_mesa, m.numero AS mesa_numero, m.apodo AS mesa_apodo,
//         COALESCE(i.items, 0) AS items,
//         COALESCE(i.total, 0) AS total
//       FROM comandas c
//       JOIN usuarios u ON u.id_usuario = c.id_usuario
//       JOIN mesas m ON m.id_mesa = c.id_mesa
//       LEFT JOIN items i ON i.id_comanda = c.id_comanda
//       ${where}
//       ORDER BY c.id_comanda DESC
//       LIMIT $${params.length}
//     `;
//     const client = await pool.connect();
//     try {
//       const { rows } = await client.query(sql, params);
//       res.json(rows);
//     } finally { client.release(); }
//   } catch (e) { next(e); }
// });


// Detalle de una comanda (con nombres de productos)

// GET /api/comandas/:id/detalle
app.get('/api/comandas/:id/detalle', (req, res, next) =>
  pool.query(
    `SELECT id_det_comanda, id_producto, producto_nombre,
            cantidad, pre_unitario, subtotal, observaciones
       FROM public.v_comandas_detalle_items
      WHERE id_comanda = $1::int
      ORDER BY id_det_comanda`,
    [req.params.id]
  )
  .then(r => res.json(r.rows))
  .catch(next)
);


// app.get('/api/comandas/:id/detalle', async (req, res, next) => {
//   try {
//     const id = parseInt(req.params.id, 10);
//     if (!Number.isInteger(id) || id <= 0) {
//       return res.status(400).json({ error: 'id inválido' });
//     }

//     const sql = `
//       SELECT
//         id_det_comanda, id_producto, producto_nombre,
//         cantidad, pre_unitario, subtotal, observaciones
//       FROM public.v_comandas_detalle_items
//       WHERE id_comanda = $1::int
//       ORDER BY id_det_comanda
//     `;
//     const { rows } = await pool.query(sql, [id]);
//     res.json(rows);
//   } catch (e) { next(e); }
// });


// app.get('/api/comandas/:id/detalle', async (req, res, next) => {
//   try {
//     const id = parseInt(req.params.id, 10);
//     if (!id) return res.status(400).json({ error: 'id inválido' });

//     const sql = `
//       SELECT d.id_det_comanda, d.id_producto, p.nombre AS producto_nombre,
//              d.cantidad, d.pre_unitario, (d.cantidad * d.pre_unitario) AS subtotal,
//              d.observaciones
//       FROM deta_comandas d
//       JOIN productos p ON p.id_producto = d.id_producto
//       WHERE d.id_comanda = $1
//       ORDER BY d.id_det_comanda
//     `;
//     const { rows } = await pool.query(sql, [id]);
//     res.json(rows);
//   } catch (e) { next(e); }
// });



// Cerrar comanda (setea estado y fec_cierre en DB)
app.post('/api/comandas/:id/cerrar', async (req, res, next) => {
  try {
    const id = Number(req.params.id);
    if (!Number.isInteger(id) || id <= 0) {
      return res.status(400).json({ error: 'id inválido' });
    }
    const { rows } = await pool.query(
      `SELECT public.f_cerrar_comanda($1) AS data`,
      [id]
    );
    if (!rows.length || rows[0].data === null) {
      return res.status(404).json({ error: 'Comanda no encontrada' });
    }
    res.json(rows[0].data);
  } catch (err) { next(err); }
});

// Abrir (reabrir) comanda
app.post('/api/comandas/:id/abrir', async (req, res, next) => {
  try {
    const id = Number(req.params.id);
    if (!Number.isInteger(id) || id <= 0) {
      return res.status(400).json({ error: 'id inválido' });
    }
    const { rows } = await pool.query(
      `SELECT public.f_abrir_comanda($1) AS data`,
      [id]
    );
    if (!rows.length || rows[0].data === null) {
      return res.status(404).json({ error: 'Comanda no encontrada' });
    }
    res.json(rows[0].data);
  } catch (err) { next(err); }
});

// // Cambiar estado (abrir/cerrar)
// app.post('/api/comandas/:id/estado', async (req, res, next) => {
//   try {
//     const id = parseInt(req.params.id, 10);
//     let { estado } = req.body || {};
//     if (!id) return res.status(400).json({ error: 'id inválido' });

//     const allowed = new Set(['abierta','cerrada','pagada','anulada']);
//     if (!allowed.has(estado)) return res.status(400).json({ error: 'estado inválido' });

//     const { rows } = await pool.query(
//       `UPDATE comandas SET estado = $2 WHERE id_comanda = $1 RETURNING *`,
//       [id, estado]
//     );
//     if (!rows.length) return res.status(404).json({ error: 'comanda no encontrada' });
//     res.json({ updated: rows[0] });
//   } catch (e) { next(e); }
// });

// GET producto + receta
app.get('/api/rpc/get_producto/:id', async (req, res) => {
  const id = Number(req.params.id);
  const { rows } = await pool.query('SELECT public.get_producto($1) AS data', [id]);
  res.json(rows[0]?.data || {});
});

// POST guardar producto + receta

app.post('/api/rpc/save_producto', async (req, res) => {
  try {
    // console.debug('receta payload:', req.body?.receta); // habilitalo si lo necesitás
    const q = 'SELECT public.save_producto($1,$2,$3,$4,$5,$6,$7::jsonb) AS id_producto';
    const { id_producto=null, nombre, img_producto=null, precio=0, activo=true, id_categoria=null, receta=[] } = req.body || {};
    const params = [id_producto, nombre, img_producto, precio, activo, id_categoria, JSON.stringify(receta||[])];
    const { rows } = await pool.query(q, params);
    res.json(rows[0] || {});
  } catch(e) {
    console.error(e);
    res.status(500).json({ error: 'save_producto failed' });
  }
});


// app.post('/api/rpc/save_producto', async (req, res) => {
//   const { id_producto=null, nombre, img_producto=null, precio=0, activo=true, id_categoria=null, receta=[] } = req.body || {};
//   const q = 'SELECT * FROM public.save_producto($1,$2,$3,$4,$5,$6,$7::jsonb)';
//   const params = [id_producto, nombre, img_producto, precio, activo, id_categoria, JSON.stringify(receta||[])];
//   const { rows } = await pool.query(q, params);
//   res.json(rows[0] || {});
// });

// GET MP + proveedores
app.get('/api/rpc/get_materia/:id', async (req, res) => {
  const id = Number(req.params.id);
  try {
    const { rows } = await pool.query('SELECT public.get_materia_prima($1) AS data', [id]);
    res.json(rows[0]?.data || {});
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: 'get_materia failed' });
  }
});

// SAVE MP + proveedores (array)
app.post('/api/rpc/save_materia', async (req, res) => {
  const { id_mat_prima=null, nombre, unidad, activo=true, proveedores=[] } = req.body || {};
  try {
    const q = 'SELECT public.save_materia_prima($1,$2,$3,$4,$5::jsonb) AS id_mat_prima';
    const params = [id_mat_prima, nombre, unidad, activo, JSON.stringify(proveedores||[])];
    const { rows } = await pool.query(q, params);
    res.json(rows[0] || {});
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: 'save_materia failed' });
  }
});

// POST /api/rpc/find_usuarios_por_documentos  { docs: ["12345678","09123456", ...] }
app.post('/api/rpc/find_usuarios_por_documentos', async (req, res) => {
  try {
    const docs = Array.isArray(req.body?.docs) ? req.body.docs : [];
    const sql  = 'SELECT public.find_usuarios_por_documentos($1::jsonb) AS data';
    const { rows } = await pool.query(sql, [JSON.stringify(docs)]);
    res.json(rows[0]?.data || {});
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: 'find_usuarios_por_documentos failed' });
  }
});

// POST /api/rpc/import_asistencia  { registros: [...], origen?: "AGL_001.txt" }
app.post('/api/rpc/import_asistencia', async (req, res) => {
  try {
    const registros = Array.isArray(req.body?.registros) ? req.body.registros : [];
    const origen    = req.body?.origen || null;
    const sql = 'SELECT public.import_asistencia($1::jsonb,$2) AS data';
    const { rows } = await pool.query(sql, [JSON.stringify(registros), origen]);
    res.json(rows[0]?.data || {});
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: 'import_asistencia failed' });
  }
});

// Consultar datos de asistencia (raw + pares) para un usuario y rango
app.post('/api/rpc/asistencia_get', async (req, res) => {
  try {
    const { doc, desde, hasta } = req.body || {};
    const sql = 'SELECT public.asistencia_get($1::text,$2::date,$3::date) AS data';
    const { rows } = await pool.query(sql, [doc, desde, hasta]);
    res.json(rows[0]?.data || {});
  } catch (e) {
    console.error(e); res.status(500).json({ error: 'asistencia_get failed' });
  }
});

// Editar un registro crudo y recalcular pares
app.post('/api/rpc/asistencia_update_raw', async (req, res) => {
  try {
    const { id_raw, fecha, hora, modo } = req.body || {};
    const sql = 'SELECT public.asistencia_update_raw($1::bigint,$2::date,$3::text,$4::text) AS data';
    const { rows } = await pool.query(sql, [id_raw, fecha, hora, modo ?? null]);
    res.json(rows[0]?.data || {});
  } catch (e) {
    console.error(e); res.status(500).json({ error: 'asistencia_update_raw failed' });
  }
});

// Eliminar un registro crudo y recalcular pares
app.post('/api/rpc/asistencia_delete_raw', async (req, res) => {
  try {
    const { id_raw } = req.body || {};
    const sql = 'SELECT public.asistencia_delete_raw($1::bigint) AS data';
    const { rows } = await pool.query(sql, [id_raw]);
    res.json(rows[0]?.data || {});
  } catch (e) {
    console.error(e); res.status(500).json({ error: 'asistencia_delete_raw failed' });
  }
});

// POST /api/rpc/report_tickets { year }
app.post('/api/rpc/report_tickets', async (req, res) => {
  try {
    const y = parseInt(req.body?.year ?? req.query?.year, 10);
    const year = (Number.isFinite(y) && y >= 2000 && y <= 2100)
      ? y
      : (new Date()).getFullYear();

    const { rows } = await pool.query(
      'SELECT public.report_tickets_year($1::int) AS j', [year]
    );
    res.json(rows[0].j);
  } catch (e) {
    console.error('report_tickets error:', e);
    res.status(500).json({
      error: 'report_tickets failed',
      message: e.message, detail: e.detail, where: e.where, code: e.code
    });
  }
});

// POST /api/rpc/report_asistencia { desde: 'YYYY-MM-DD', hasta: 'YYYY-MM-DD' }
app.post('/api/rpc/report_asistencia', async (req, res) => {
  try {
    let { desde, hasta } = req.body || {};
    // defaults si vienen vacíos/invalidos
    const re = /^\d{4}-\d{2}-\d{2}$/;
    if (!re.test(desde) || !re.test(hasta)) {
      const end   = new Date();
      const start = new Date(end); start.setDate(end.getDate()-30);
      desde = start.toISOString().slice(0,10);
      hasta = end.toISOString().slice(0,10);
    }

    const { rows } = await pool.query(
      'SELECT public.report_asistencia($1::date,$2::date) AS j', [desde, hasta]
    );
    res.json(rows[0].j);
  } catch (e) {
    console.error('report_asistencia error:', e);
    res.status(500).json({
      error: 'report_asistencia failed',
      message: e.message, detail: e.detail, where: e.where, code: e.code
    });
  }
});


// app.post('/api/rpc/report_asistencia', async (req,res)=>{
//   try{
//     const {desde, hasta} = req.body||{};
//     const sql = 'SELECT * FROM public.report_asistencia($1::date,$2::date)';
//     const {rows} = await pool.query(sql,[desde, hasta]);
//     res.json(rows);
//   } catch (e) {
//     console.error(e);
//     res.status(500).json({ error: 'report_tickets failed' + e });
//   }
// });

// app.post('/api/rpc/report_tickets', async (req, res) => {
//   try {
//     const { year } = req.body || {};
//     const sql = 'SELECT public.report_tickets_year($1::int) AS data';
//     const { rows } = await pool.query(sql, [year]);
//     res.json(rows[0]?.data || {});
//   } catch (e) {
//     console.error(e);
//     res.status(500).json({ error: 'report_tickets failed' + e });
//   }
// });


// Guardar (insert/update)
app.post('/api/rpc/save_compra', async (req, res) => {
  try {
    const { id_compra, id_proveedor, fec_compra, detalles } = req.body || {};
    const sql  = 'SELECT * FROM public.save_compra($1::int,$2::int,$3::timestamptz,$4::jsonb)';
    const args = [id_compra ?? null, id_proveedor, fec_compra ? new Date(fec_compra) : null, JSON.stringify(detalles)];
    const { rows } = await pool.query(sql, args);
    res.json(rows[0]); // { id_compra, total }
  } catch (e) {
    console.error('save_compra error:', e);
    res.status(500).json({ error: 'save_compra failed', message: e.message, detail: e.detail, where: e.where, code: e.code });
  }
});


// Obtener para editar
app.post('/api/rpc/get_compra', async (req, res) => {
  try {
    const { id_compra } = req.body || {};
    const sql = `SELECT public.get_compra($1::int) AS data`;
    const { rows } = await pool.query(sql, [id_compra]);
    res.json(rows[0]?.data || {});
  } catch (e) {
    console.error(e); res.status(500).json({ error: 'get_compra failed' });
  }
});

// Eliminar
app.post('/api/rpc/delete_compra', async (req, res) => {
  try {
    const { id_compra } = req.body || {};
    await pool.query(`SELECT public.delete_compra($1::int)`, [id_compra]);
    res.json({ ok: true });
  } catch (e) {
    console.error(e); res.status(500).json({ error: 'delete_compra failed' });
  }
});


// POST /api/rpc/report_gastos  { year: 2025 }
app.post('/api/rpc/report_gastos', async (req, res) => {
  try {
    const year = parseInt(req.body?.year ?? new Date().getFullYear(), 10);
    const { rows } = await pool.query(
      'SELECT public.report_gastos($1::int) AS j', [year]
    );
    res.json(rows[0].j);
  } catch (e) {
    console.error('report_gastos error:', e);
    res.status(500).json({
      error: 'report_gastos failed',
      message: e.message, detail: e.detail, code: e.code
    });
  }
});

// (Opcional) GET para probar rápido desde el navegador:
// /api/rpc/report_gastos?year=2025
app.get('/api/rpc/report_gastos', async (req, res) => {
  try {
    const year = parseInt(req.query.year ?? new Date().getFullYear(), 10);
    const { rows } = await pool.query(
      'SELECT public.report_gastos($1::int) AS j', [year]
    );
    res.json(rows[0].j);
  } catch (e) {
    console.error('report_gastos error:', e);
    res.status(500).json({
      error: 'report_gastos failed',
      message: e.message, detail: e.detail, code: e.code
    });
  }
});


// ----------------------------------------------------------
// Verificación de conexión
// ----------------------------------------------------------
async function verificarConexion() {
  try {
    const client = await pool.connect();
    const res = await client.query('SELECT NOW() AS hora');
    console.log(`\nConexión con la base de datos ${chalk.green(process.env.DB_NAME)} fue exitosa.`);
    console.log('Fecha y hora actual de la base de datos:', res.rows[0].hora);
    client.release();
  } catch (error) {
    console.error('Error al conectar con la base de datos al iniciar:', error.message);
    console.error('Revisar credenciales y accesos de red.');
  }
}

// ----------------------------------------------------------
// Inicio del servidor
// ----------------------------------------------------------

const PORT = process.env.PORT ? Number(process.env.PORT) : 3000;
app.listen(PORT, () => {
  console.log(`Servidor de aplicación escuchando en ${chalk.yellow(`http://localhost:${PORT}`)}`);
  console.log(chalk.grey(`Comprobando accesibilidad a la db ${chalk.white(process.env.DB_NAME)} del host ${chalk.white(process.env.DB_HOST)} ...`));
  verificarConexion();
});

// Healthcheck
app.get('/health', async (_req, res) => {
  res.status(200).json({ status: 'ok' });
});
