// src/server.js
const express = require('express');
const cors = require('cors');
const path = require('path');
const fs = require('fs');
const multer = require('multer');
const { spawn } = require('child_process');

const { query } = require('./db');
require('dotenv').config();

const api = express();
const API_PORT = process.env.PORT || 2800;

// Middlewares
api.use(cors());
api.use(express.json());

// Ping simple
api.get('/', (req, res) => {
  res.send('API AUNARSalud OK ✅');
});

// ==============================
// IMPORTACIÓN EXCEL (multer + estado)
// ==============================
const uploadsDir = path.join(__dirname, 'uploads');
if (!fs.existsSync(uploadsDir)) fs.mkdirSync(uploadsDir, { recursive: true });

const STATE_FILE = path.join(__dirname, 'import_state.json');

function leerEstado() {
  try {
    if (!fs.existsSync(STATE_FILE)) return {};
    return JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
  } catch (e) {
    return {};
  }
}

function guardarEstado(s) {
  fs.writeFileSync(STATE_FILE, JSON.stringify(s, null, 2), 'utf-8');
}

function estadoParaFrontend(s) {
  return {
    planilla_cargada: !!s.planilla_path,
    histor_cargada: !!s.histor_path,
    planilla_nombre: s.planilla_nombre || null,
    histor_nombre: s.histor_nombre || null,
    ultima_sync: s.ultima_sync || null,
  };
}

// storage: conserva nombre y agrega timestamp
const storage = multer.diskStorage({
  destination: (req, file, cb) => cb(null, uploadsDir),
  filename: (req, file, cb) => {
    const base = file.originalname.replace(/[^\w.\-() ]+/g, '_');
    const ts = Date.now();
    cb(null, `${ts}__${base}`);
  }
});

const upload = multer({ storage });

// control para no correr dos imports al tiempo
let importEnCurso = false;

function correrImportPython(planillaPath, historPath) {
  return new Promise((resolve, reject) => {
    const scriptPath = path.join(__dirname, 'importar_citas_cac.py');

    const pythonCmd = process.env.PYTHON || 'python'; // Windows: python
    const args = [
      scriptPath,
      '--planilla', planillaPath,
      '--histor', historPath
    ];

    const proc = spawn(pythonCmd, args, { cwd: __dirname });

    let stdout = '';
    let stderr = '';

    proc.stdout.on('data', (d) => stdout += d.toString());
    proc.stderr.on('data', (d) => stderr += d.toString());

    proc.on('close', (code) => {
      if (code === 0) resolve({ code, stdout, stderr });
      else reject({ code, stdout, stderr });
    });
  });
}

// GET estado
api.get('/api/importacion/estado', (req, res) => {
  const s = leerEstado();
  res.json(estadoParaFrontend(s));
});

// POST planilla
api.post('/api/importacion/planilla', upload.single('file'), async (req, res) => {
  try {
    if (!req.file) return res.status(400).json({ error: 'No llegó archivo (file).' });

    const s = leerEstado();
    s.planilla_path = req.file.path;
    s.planilla_nombre = req.file.originalname;
    guardarEstado(s);

    // si ya está histor => correr import
    if (s.histor_path) {
      if (importEnCurso) {
        return res.status(409).json({ ok: false, mensaje: 'Importación en curso, intenta en 30s.' });
      }

      importEnCurso = true;
      try {
        const r = await correrImportPython(s.planilla_path, s.histor_path);
        const s2 = leerEstado();
        s2.ultima_sync = new Date().toISOString();
        guardarEstado(s2);

        return res.json({
          ok: true,
          mensaje: '✅ Planilla cargada y sincronización ejecutada.',
          stdout: r.stdout,
          stderr: r.stderr
        });
      } catch (e) {
        return res.status(500).json({
          ok: false,
          mensaje: '❌ Falló la sincronización (Python).',
          stdout: e.stdout,
          stderr: e.stderr,
          detalle: e
        });
      } finally {
        importEnCurso = false;
      }
    }

    return res.json({ ok: true, mensaje: '✅ Planilla cargada. Falta HISTOR para sincronizar.' });
  } catch (err) {
    console.error('Error POST /api/importacion/planilla:', err);
    res.status(500).json({ ok: false, mensaje: 'Error subiendo planilla.', detalle: err?.message });
  }
});

// POST histor
api.post('/api/importacion/histor', upload.single('file'), async (req, res) => {
  try {
    if (!req.file) return res.status(400).json({ error: 'No llegó archivo (file).' });

    const s = leerEstado();
    s.histor_path = req.file.path;
    s.histor_nombre = req.file.originalname;
    guardarEstado(s);

    // si ya está planilla => correr import
    if (s.planilla_path) {
      if (importEnCurso) {
        return res.status(409).json({ ok: false, mensaje: 'Importación en curso, intenta en 30s.' });
      }

      importEnCurso = true;
      try {
        const r = await correrImportPython(s.planilla_path, s.histor_path);
        const s2 = leerEstado();
        s2.ultima_sync = new Date().toISOString();
        guardarEstado(s2);

        return res.json({
          ok: true,
          mensaje: '✅ Histor cargado y sincronización ejecutada.',
          stdout: r.stdout,
          stderr: r.stderr
        });
      } catch (e) {
        return res.status(500).json({
          ok: false,
          mensaje: '❌ Falló la sincronización (Python).',
          stdout: e.stdout,
          stderr: e.stderr,
          detalle: e
        });
      } finally {
        importEnCurso = false;
      }
    }

    return res.json({ ok: true, mensaje: '✅ Histor cargado. Falta PLANILLA para sincronizar.' });
  } catch (err) {
    console.error('Error POST /api/importacion/histor:', err);
    res.status(500).json({ ok: false, mensaje: 'Error subiendo histor.', detalle: err?.message });
  }
});

// ==============================
// HISTOGRAMAS (PLAN ANUAL REAL)
// ==============================

const PLAN_ANUAL = {
  1: ['ENFERMERIA', 'LABORATORIOS', 'PSICOLOGIA', 'TRABAJO SOCIAL', 'MEDICO EXPERTO', 'QUIMICO FARMACEUTICO'],
  2: ['MEDICO EXPERTO', 'NUTRICION'],
  3: ['INFECTOLOGIA', 'QUIMICO FARMACEUTICO'],
  4: ['MEDICO EXPERTO'],
  5: ['MEDICO EXPERTO', 'ENFERMERIA', 'LABORATORIOS'],
  6: ['MEDICO EXPERTO', 'ODONTOLOGIA', 'TRABAJO SOCIAL'],
  7: ['MEDICO EXPERTO'],
  8: ['MEDICO EXPERTO', 'OFTALMOLOGIA'],
  9: ['INFECTOLOGIA', 'NUTRICION'],
  10: ['MEDICO EXPERTO', 'PSIQUIATRIA'],
  11: ['MEDICO EXPERTO'],
  12: ['MEDICO EXPERTO'],
};

function toISO(d) {
  const x = new Date(d);
  const yyyy = x.getFullYear();
  const mm = String(x.getMonth() + 1).padStart(2, '0');
  const dd = String(x.getDate()).padStart(2, '0');
  return `${yyyy}-${mm}-${dd}`;
}

function addMonths(date, n) {
  const d = new Date(date);
  const day = d.getDate();
  d.setMonth(d.getMonth() + n);
  if (d.getDate() < day) d.setDate(0);
  return d;
}

function ajustarCicloAnual(inicio, hoy) {
  let start = new Date(inicio);
  let end = addMonths(start, 12);
  while (end <= hoy) {
    start = addMonths(start, 12);
    end = addMonths(start, 12);
  }
  return { start, end };
}

function mesPrograma(start, hoy) {
  for (let m = 1; m <= 12; m++) {
    const a = addMonths(start, m - 1);
    const b = addMonths(start, m);
    if (hoy >= a && hoy < b) return m;
  }
  return 12;
}

function normEsp(s) {
  if (!s) return '';
  return String(s)
    .trim()
    .toUpperCase()
    .replaceAll('Á','A').replaceAll('É','E').replaceAll('Í','I').replaceAll('Ó','O').replaceAll('Ú','U')
    .replaceAll('Ñ','N');
}

function obtenerMesPorFecha(rangos, fecha) {
  for (let m = 1; m <= 12; m++) {
    const a = rangos[m].inicio;
    const b = rangos[m].fin;
    if (fecha >= a && fecha < b) return m;
  }
  return null;
}

// ✅ Histograma general REAL: suma esperado vs asistido según plan anual por paciente
api.get('/api/histogramas/general', async (req, res) => {
  try {
    const hoy = new Date();

    // 1) pacientes + ingreso (tiempo) o primera cita
    const pacientesRes = await query(`
      SELECT
        p.numero_documento::text AS doc,
        t.fecha_ingreso_ips AS ingreso_tiempo,
        MIN(c.fecha_cita) AS primera_cita
      FROM paciente p
      LEFT JOIN tiempo t ON t.numero_documento = p.numero_documento
      LEFT JOIN cita c ON c.numero_documento = p.numero_documento
      GROUP BY p.numero_documento, t.fecha_ingreso_ips
    `);

    const mapaPac = new Map();
    let minStart = null;

    for (const r of pacientesRes.rows) {
      const doc = String(r.doc || '').trim();
      const ingreso = r.ingreso_tiempo || r.primera_cita;
      if (!doc || !ingreso) continue;

      const ciclo = ajustarCicloAnual(new Date(ingreso), hoy);
      const start = ciclo.start;
      const end = ciclo.end;
      const mesActual = mesPrograma(start, hoy);

      const rangos = {};
      for (let m = 1; m <= 12; m++) {
        rangos[m] = { inicio: addMonths(start, m - 1), fin: addMonths(start, m) };
      }

      mapaPac.set(doc, { doc, start, end, mesActual, rangos });

      if (!minStart || start < minStart) minStart = start;
    }

    if (!minStart) {
      return res.json({
        pacientes_incluidos: 0,
        rango: { inicio: null, hoy: toISO(hoy) },
        total_esperado: 0,
        total_asistidas: 0,
        porcentaje: 0,
        porEspecialidad: [],
        porMesPrograma: Array.from({ length: 12 }, (_, i) => ({ mes: i + 1, esperado: 0, asistidas: 0 }))
      });
    }

    // 2) asistidas desde minStart hasta hoy
    const asistidasRes = await query(
      `SELECT
         c.numero_documento::text AS doc,
         e.nombre AS especialidad,
         c.fecha_cita
       FROM cita c
       JOIN especialidad e ON e.id_especialidad = c.id_especialidad
       WHERE c.asistio = TRUE
         AND c.fecha_cita >= $1
         AND c.fecha_cita <= $2`,
      [toISO(minStart), toISO(hoy)]
    );

    // 3) laboratorios (para marcar LABORATORIOS)
    const labsRes = await query(
      `SELECT
         numero_documento::text AS doc,
         fecha_laboratorio_1, fecha_laboratorio_2, fecha_laboratorio_3, fecha_laboratorio_4,
         fecha_laboratorio_5, fecha_laboratorio_6, fecha_laboratorio_7, fecha_laboratorio_8
       FROM laboratorios`
    );

    // 4) cumplidas: máximo 1 por (doc|mes|especialidad)
    const cumplidas = new Set();

    for (const r of asistidasRes.rows) {
      const doc = String(r.doc || '').trim();
      const p = mapaPac.get(doc);
      if (!p) continue;

      const fecha = new Date(r.fecha_cita);
      if (!(fecha >= p.start && fecha <= hoy)) continue;

      const mes = obtenerMesPorFecha(p.rangos, fecha);
      if (!mes || mes > p.mesActual) continue;

      const esp = normEsp(r.especialidad);
      const planMes = (PLAN_ANUAL[mes] || []).map(normEsp);

      if (planMes.includes(esp)) {
        cumplidas.add(`${doc}|${mes}|${esp}`);
      }
    }

    // labs => LABORATORIOS
    for (const r of labsRes.rows) {
      const doc = String(r.doc || '').trim();
      const p = mapaPac.get(doc);
      if (!p) continue;

      const fechas = [
        r.fecha_laboratorio_1, r.fecha_laboratorio_2, r.fecha_laboratorio_3, r.fecha_laboratorio_4,
        r.fecha_laboratorio_5, r.fecha_laboratorio_6, r.fecha_laboratorio_7, r.fecha_laboratorio_8
      ].filter(Boolean).map(x => new Date(x));

      for (const f of fechas) {
        if (!(f >= p.start && f <= hoy)) continue;

        const mes = obtenerMesPorFecha(p.rangos, f);
        if (!mes || mes > p.mesActual) continue;

        const planMes = (PLAN_ANUAL[mes] || []).map(normEsp);
        if (planMes.includes('LABORATORIOS')) {
          cumplidas.add(`${doc}|${mes}|LABORATORIOS`);
        }
      }
    }

    // 5) esperado vs asistidas (sumado por especialidad y por mes del programa)
    const esperadoPorEsp = {};
    const asistidasPorEsp = {};
    const porMesPrograma = Array.from({ length: 12 }, (_, i) => ({ mes: i + 1, esperado: 0, asistidas: 0 }));

    let total_esperado = 0;
    let total_asistidas = 0;

    for (const p of mapaPac.values()) {
      for (let mes = 1; mes <= p.mesActual; mes++) {
        const lista = (PLAN_ANUAL[mes] || []).map(normEsp);

        for (const esp of lista) {
          esperadoPorEsp[esp] = (esperadoPorEsp[esp] || 0) + 1;
          porMesPrograma[mes - 1].esperado += 1;
          total_esperado += 1;

          const key = `${p.doc}|${mes}|${esp}`;
          if (cumplidas.has(key)) {
            asistidasPorEsp[esp] = (asistidasPorEsp[esp] || 0) + 1;
            porMesPrograma[mes - 1].asistidas += 1;
            total_asistidas += 1;
          }
        }
      }
    }

    const especialidades = Object.keys(esperadoPorEsp).sort();

    const porEspecialidad = especialidades.map((esp) => {
      const esperado = esperadoPorEsp[esp] || 0;
      const asistidas = asistidasPorEsp[esp] || 0;
      const pendientes = Math.max(0, esperado - asistidas);
      const porcentaje = esperado ? Math.round((asistidas / esperado) * 100) : 0;
      return { especialidad: esp, esperado, asistidas, pendientes, porcentaje };
    });

    const porcentaje = total_esperado ? Math.round((total_asistidas / total_esperado) * 100) : 0;

    res.json({
      pacientes_incluidos: mapaPac.size,
      rango: { inicio: toISO(minStart), hoy: toISO(hoy) },
      total_esperado,
      total_asistidas,
      porcentaje,
      porEspecialidad,
      porMesPrograma
    });

  } catch (err) {
    console.error('Error GET /api/histogramas/general:', err);
    res.status(500).json({ error: 'Error generando histograma general' });
  }
});

// Histograma anual por paciente (CICLO ANUAL segun ingreso)
api.get('/api/histogramas/paciente/:numero_documento', async (req, res) => {
  const { numero_documento } = req.params;

  try {
    // 1) fecha ingreso (tiempo) o primera cita
    let ingreso = null;
    const ingRes = await query(
      `SELECT fecha_ingreso_ips
       FROM tiempo
       WHERE numero_documento::text = $1
       LIMIT 1`,
      [numero_documento]
    );

    if (ingRes.rows.length && ingRes.rows[0].fecha_ingreso_ips) {
      ingreso = new Date(ingRes.rows[0].fecha_ingreso_ips);
    } else {
      const minRes = await query(
        `SELECT MIN(fecha_cita) AS fecha
         FROM cita
         WHERE numero_documento::text = $1`,
        [numero_documento]
      );
      if (minRes.rows.length && minRes.rows[0].fecha) ingreso = new Date(minRes.rows[0].fecha);
    }

    if (!ingreso) {
      return res.status(404).json({ error: 'No hay fecha de ingreso ni citas para construir histograma.' });
    }

    const hoy = new Date();
    const ciclo = ajustarCicloAnual(ingreso, hoy);
    const start = ciclo.start;
    const end = ciclo.end; // exclusivo
    const mesActual = mesPrograma(start, hoy);

    // rangos por mes
    const rangos = {};
    for (let m = 1; m <= 12; m++) {
      rangos[m] = { inicio: addMonths(start, m - 1), fin: addMonths(start, m) };
    }

    // 2) citas asistidas del paciente en ciclo
    const citasRes = await query(
      `SELECT e.nombre AS especialidad, c.fecha_cita, c.asistio
       FROM cita c
       JOIN especialidad e ON e.id_especialidad = c.id_especialidad
       WHERE c.numero_documento::text = $1
         AND c.fecha_cita >= $2
         AND c.fecha_cita < $3`,
      [numero_documento, toISO(start), toISO(end)]
    );

    // 3) labs del paciente
    const labsRes = await query(
      `SELECT fecha_laboratorio_1, fecha_laboratorio_2, fecha_laboratorio_3, fecha_laboratorio_4,
              fecha_laboratorio_5, fecha_laboratorio_6, fecha_laboratorio_7, fecha_laboratorio_8
       FROM laboratorios
       WHERE numero_documento::text = $1
       LIMIT 1`,
      [numero_documento]
    );

    const fechasLabs = [];
    if (labsRes.rows.length) {
      const row = labsRes.rows[0];
      Object.keys(row).forEach(k => { if (row[k]) fechasLabs.push(new Date(row[k])); });
    }

    // cumplidas por mes (máximo 1 por especialidad en el mes)
    const cumplidas = new Set();

    for (const r of citasRes.rows) {
      if (r.asistio !== true) continue;
      const fecha = new Date(r.fecha_cita);
      const mes = obtenerMesPorFecha(rangos, fecha);
      if (!mes) continue;
      const esp = normEsp(r.especialidad);
      const planMes = (PLAN_ANUAL[mes] || []).map(normEsp);
      if (planMes.includes(esp)) {
        cumplidas.add(`${mes}|${esp}`);
      }
    }

    for (const f of fechasLabs) {
      const mes = obtenerMesPorFecha(rangos, f);
      if (!mes) continue;
      const planMes = (PLAN_ANUAL[mes] || []).map(normEsp);
      if (planMes.includes('LABORATORIOS')) {
        cumplidas.add(`${mes}|LABORATORIOS`);
      }
    }

    const porMes = [];
    let esperadoHastaHoy = 0;
    let asistidasHastaHoy = 0;

    for (let m = 1; m <= 12; m++) {
      const a = rangos[m].inicio;
      const b = rangos[m].fin;

      const lista = (PLAN_ANUAL[m] || []).map(normEsp);
      const esperado = lista.length;

      let asistidas = 0;
      for (const esp of lista) {
        if (cumplidas.has(`${m}|${esp}`)) asistidas += 1;
      }

      const pendientes = Math.max(0, esperado - asistidas);

      porMes.push({
        mes: m,
        inicio: toISO(a),
        fin: toISO(new Date(b.getTime() - 86400000)),
        esperado,
        asistidas,
        pendientes
      });

      if (m <= mesActual) {
        esperadoHastaHoy += esperado;
        asistidasHastaHoy += asistidas;
      }
    }

    const esperadoMes = porMes.find(x => x.mes === mesActual)?.esperado || 0;
    const asistidasMes = porMes.find(x => x.mes === mesActual)?.asistidas || 0;

    const porcentajeMes = esperadoMes ? Math.round((asistidasMes / esperadoMes) * 100) : 0;
    const porcentajeHasta = esperadoHastaHoy ? Math.round((asistidasHastaHoy / esperadoHastaHoy) * 100) : 0;

    res.json({
      numero_documento,
      ingreso: {
        fecha: toISO(ingreso),
        fuente: (ingRes.rows.length && ingRes.rows[0].fecha_ingreso_ips) ? 'tiempo.fecha_ingreso_ips' : 'primera_cita'
      },
      ventana: {
        inicio: toISO(start),
        fin: toISO(new Date(end.getTime() - 86400000)),
        mes_programa: mesActual
      },
      cumplimiento: {
        esperado_mes_actual: esperadoMes,
        asistidas_mes_actual: asistidasMes,
        porcentaje_mes_actual: porcentajeMes,
        esperado_hasta_hoy: esperadoHastaHoy,
        asistidas_hasta_hoy: asistidasHastaHoy,
        porcentaje_hasta_hoy: porcentajeHasta
      },
      porMes
    });
  } catch (err) {
    console.error('Error GET /api/histogramas/paciente/:doc:', err);
    res.status(500).json({ error: 'Error generando histograma del paciente' });
  }
});

// ==============================
// TUS RUTAS EXISTENTES
// ==============================

/**
 * GET /api/pacientes
 */
api.get('/api/pacientes', async (req, res) => {
  const { tipo, termino } = req.query;

  try {
    const valor = (termino || '').toString().trim();

    if (!valor) {
      const resultado = await query(
        `SELECT
           tipo_documento,
           numero_documento::text AS numero_documento,
           nombre_completo,
           genero,
           edad,
           fecha_nacimiento,
           celular,
           correo
         FROM paciente
         ORDER BY nombre_completo
         LIMIT 50`
      );
      return res.json(resultado.rows);
    }

    let sql = '';
    const param = `%${valor}%`;

    if (tipo === 'documento') {
      sql = `
        SELECT
          tipo_documento,
          numero_documento::text AS numero_documento,
          nombre_completo,
          genero,
          edad,
          fecha_nacimiento,
          celular,
          correo
        FROM paciente
        WHERE numero_documento::text ILIKE $1
        ORDER BY nombre_completo
        LIMIT 50
      `;
    } else if (tipo === 'nombre') {
      sql = `
        SELECT
          tipo_documento,
          numero_documento::text AS numero_documento,
          nombre_completo,
          genero,
          edad,
          fecha_nacimiento,
          celular,
          correo
        FROM paciente
        WHERE nombre_completo ILIKE $1
        ORDER BY nombre_completo
        LIMIT 50
      `;
    } else {
      return res.status(400).json({ error: 'Tipo de búsqueda no válido' });
    }

    const resultado = await query(sql, [param]);
    res.json(resultado.rows);
  } catch (err) {
    console.error('Error en GET /api/pacientes:', err);
    res.status(500).json({ error: 'Error consultando pacientes' });
  }
});

/**
 * GET /api/pacientes/:numero_documento/citas
 */
api.get('/api/pacientes/:numero_documento/citas', async (req, res) => {
  const { numero_documento } = req.params;

  try {
    const resultado = await query(
      `SELECT
         c.id_cita,
         c.numero_documento::text AS numero_documento,
         e.nombre AS especialidad,
         c.fecha_cita,
         c.hora_cita,
         c.tipo_cita,
         c.modalidad,
         c.asistio,
         c.observaciones
       FROM cita c
       JOIN especialidad e
         ON c.id_especialidad = e.id_especialidad
       WHERE c.numero_documento::text = $1
       ORDER BY c.fecha_cita DESC, c.id_cita DESC`,
      [numero_documento]
    );

    res.json(resultado.rows);
  } catch (err) {
    console.error('Error en GET /api/pacientes/:numero_documento/citas:', err);
    res.status(500).json({ error: 'Error consultando citas del paciente' });
  }
});

/**
 * GET /api/especialidades
 */
api.get('/api/especialidades', async (req, res) => {
  try {
    const resultado = await query(
      `SELECT id_especialidad, nombre
       FROM especialidad
       ORDER BY id_especialidad`
    );
    res.json(resultado.rows);
  } catch (err) {
    console.error('Error en GET /api/especialidades:', err);
    res.status(500).json({ error: 'Error consultando especialidades' });
  }
});

/**
 * POST /api/citas
 */
api.post('/api/citas', async (req, res) => {
  const {
    numero_documento,
    id_especialidad,
    fecha_cita,
    hora_cita,
    tipo_cita,
    modalidad,
    asistio,
    observaciones
  } = req.body;

  if (!numero_documento || !id_especialidad || !fecha_cita || !hora_cita || !modalidad) {
    return res.status(400).json({
      error: 'numero_documento, id_especialidad, fecha_cita, hora_cita y modalidad son obligatorios'
    });
  }

  try {
    const conflictoMismaEsp = await query(
      `SELECT 1
       FROM cita
       WHERE numero_documento::text = $1
         AND id_especialidad = $2
         AND fecha_cita = $3
         AND hora_cita = $4::time
       LIMIT 1`,
      [numero_documento, id_especialidad, fecha_cita, hora_cita]
    );

    if (conflictoMismaEsp.rows.length > 0) {
      return res.status(400).json({
        error: 'Ya existe una cita para este paciente, en esta especialidad, fecha y hora.'
      });
    }

    const conflicto30m = await query(
      `SELECT 1
      FROM cita
      WHERE numero_documento::text = $1
        AND fecha_cita = $2
        AND hora_cita IS NOT NULL
        AND ABS(EXTRACT(EPOCH FROM (hora_cita - $3::time))) < 1800
      LIMIT 1`,
      [numero_documento, fecha_cita, hora_cita]
    );

    if (conflicto30m.rows.length > 0) {
      return res.status(400).json({
        error: 'El paciente ya tiene otra cita ese día en un horario muy cercano (menos de 30 minutos).'
      });
    }

    const countRes = await query(
      'SELECT COUNT(*) AS total FROM cita WHERE id_especialidad = $1',
      [id_especialidad]
    );
    const total = Number(countRes.rows[0].total) || 0;
    const consecutivo = total + 1;

    const nuevoId = id_especialidad * 1_000_000 + consecutivo;

    const valorAsistio = typeof asistio === 'boolean' ? asistio : false;

    const existeRes = await query('SELECT 1 FROM cita WHERE id_cita = $1', [nuevoId]);
    if (existeRes.rows.length > 0) {
      return res.status(500).json({
        error: 'Conflicto al generar id_cita, ya existe un registro con ese ID'
      });
    }

    const resultado = await query(
      `WITH nueva AS (
         INSERT INTO cita (
           id_cita,
           numero_documento,
           id_especialidad,
           fecha_cita,
           hora_cita,
           tipo_cita,
           modalidad,
           asistio,
           observaciones
         )
         VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
         RETURNING *
       )
       SELECT
         n.id_cita,
         n.numero_documento::text AS numero_documento,
         e.nombre AS especialidad,
         n.fecha_cita,
         n.hora_cita,
         n.tipo_cita,
         n.modalidad,
         n.asistio,
         n.observaciones
       FROM nueva n
       JOIN especialidad e ON n.id_especialidad = e.id_especialidad`,
      [
        nuevoId,
        numero_documento,
        id_especialidad,
        fecha_cita,
        hora_cita,
        tipo_cita || null,
        modalidad,
        valorAsistio,
        observaciones || null
      ]
    );

    res.status(201).json(resultado.rows[0]);
  } catch (err) {
    console.error('Error en POST /api/citas:', err);
    res.status(500).json({ error: 'Error creando la cita' });
  }
});

/**
 * PATCH /api/citas/:id_cita/asistencia
 */
api.patch('/api/citas/:id_cita/asistencia', async (req, res) => {
  const { id_cita } = req.params;
  const { asistio } = req.body;

  if (typeof asistio !== 'boolean') {
    return res.status(400).json({ error: 'El campo asistio debe ser booleano' });
  }

  try {
    const resultado = await query(
      `
      WITH datos AS (
        SELECT numero_documento
        FROM cita
        WHERE id_cita = $2
      ),
      ultimas AS (
        SELECT c.id_cita
        FROM cita c
        JOIN datos d ON c.numero_documento = d.numero_documento
        ORDER BY c.fecha_cita DESC, c.id_cita DESC
        LIMIT 2
      ),
      actualizada AS (
        UPDATE cita c
        SET asistio = $1
        WHERE c.id_cita IN (SELECT id_cita FROM ultimas)
          AND c.id_cita = $2
        RETURNING *
      )
      SELECT
        a.id_cita,
        a.numero_documento::text AS numero_documento,
        e.nombre AS especialidad,
        a.fecha_cita,
        a.hora_cita,
        a.tipo_cita,
        a.modalidad,
        a.asistio,
        a.observaciones
      FROM actualizada a
      JOIN especialidad e ON a.id_especialidad = e.id_especialidad;
      `,
      [asistio, id_cita]
    );

    if (resultado.rows.length === 0) {
      return res.status(400).json({
        error: 'Solo se puede modificar la asistencia de las dos últimas citas del paciente'
      });
    }

    res.json(resultado.rows[0]);
  } catch (err) {
    console.error('Error en PATCH /api/citas/:id_cita/asistencia:', err);
    res.status(500).json({ error: 'Error actualizando asistencia' });
  }
});

// Arrancar servidor
api.listen(API_PORT, () => {
  console.log(`Servidor escuchando en http://localhost:${API_PORT}`);
});
