/**
 * ================================================================
 *  SIMULADOR GPS - Lógica central
 *
 *  Cada vez que el Cron Job llama a `ejecutarSimulacion()`:
 *  1. Obtiene todas las rutas activas con sus posiciones
 *  2. Calcula en qué positionId debería estar cada camión
 *     según la hora actual y los offset_seg del JSON
 *  3. Actualiza estado_ruta en la BD
 *  4. Si cruzó un umbral de notificación (positionId 2, 4 u 8)
 *     publica un evento en Redis para que P2 lo consuma
 * ================================================================
 */

const pool  = require('../config/db');
const redis = require('../config/redis');

// positionIds que disparan notificación push (del notificaciones.json)
const TRIGGERS = new Set([2, 4, 8]);

// Multiplicador de tiempo para demo:
// 1   = tiempo real (el camión tarda horas)
// 60  = 1 minuto real = 1 hora simulada (recomendado para demo)
// 120 = aún más rápido
const TIME_MULTIPLIER = parseInt(process.env.TIME_MULTIPLIER || '60');

/**
 * Convierte un TIME de Postgres ("06:30:00") a segundos desde medianoche
 */
function timeToSegundos(timeStr) {
  const [h, m, s = 0] = timeStr.split(':').map(Number);
  return h * 3600 + m * 60 + s;
}

/**
 * Calcula cuántos segundos simulados han pasado desde que la ruta debió iniciar hoy
 */
function segundosTranscurridos(horaInicioStr) {
  const ahora       = new Date();
  const segAhora    = ahora.getHours() * 3600 + ahora.getMinutes() * 60 + ahora.getSeconds();
  const segInicio   = timeToSegundos(horaInicioStr);
  const segReales   = segAhora - segInicio;
  return segReales * TIME_MULTIPLIER;
}

/**
 * Dado los segundos transcurridos y las posiciones de la ruta,
 * devuelve la posición actual (la última cuyo offset_seg ya fue alcanzado)
 */
function calcularPosicionActual(posiciones, segTranscurridos) {
  // posiciones ordenadas por position_id ASC
  const ordenadas = [...posiciones].sort((a, b) => a.position_id - b.position_id);

  let actual = ordenadas[0]; // posición 1 por defecto

  for (const pos of ordenadas) {
    if (segTranscurridos >= pos.offset_seg) {
      actual = pos;
    } else {
      break;
    }
  }

  return actual;
}

/**
 * Publica un evento en Redis para que P2 (FCM service) lo consuma
 */
async function publicarTrigger(ruta, posicion) {
  const eventos = {
    2: 'ROUTE_START',
    4: 'TRUCK_PROXIMITY',
    8: 'ROUTE_COMPLETED',
  };

  const evento = {
    trigger_event: eventos[posicion.position_id],
    routeId:       ruta.route_id,
    ruta_id:       ruta.id,
    position_id:   posicion.position_id,
    lat:           posicion.lat,
    lng:           posicion.lng,
    timestamp:     new Date().toISOString(),
  };

  // Canal: 'notificaciones:trigger' — P2 se suscribe a este canal
  await redis.publish('notificaciones:trigger', JSON.stringify(evento));
  console.log(`  📡 Trigger publicado: ${evento.trigger_event} para ${ruta.route_id}`);
}

/**
 * Función principal del simulador — llamada por el Cron Job
 */
async function ejecutarSimulacion() {
  const client = await pool.connect();

  try {
    // ── 1. Obtener rutas activas con sus posiciones ────────────
    const { rows: rutas } = await client.query(
      `SELECT r.id, r.route_id, r.hora_inicio,
              e.estado, e.position_id_actual
       FROM rutas r
       JOIN estado_ruta e ON e.ruta_id = r.id
       WHERE r.activa = TRUE`
    );

    for (const ruta of rutas) {
      const segTrans = segundosTranscurridos(ruta.hora_inicio);

      // Ruta que no ha iniciado aún
      if (segTrans < 0) {
        if (ruta.estado !== 'INACTIVA') {
          await client.query(
            `UPDATE estado_ruta SET estado = 'INACTIVA', position_id_actual = 1,
             actualizado_at = NOW() WHERE ruta_id = $1`,
            [ruta.id]
          );
        }
        continue;
      }

      // ── 2. Obtener posiciones de esta ruta ─────────────────
      const { rows: posiciones } = await client.query(
        `SELECT position_id, lat, lng, offset_seg
         FROM ruta_posiciones
         WHERE ruta_id = $1
         ORDER BY position_id ASC`,
        [ruta.id]
      );

      if (posiciones.length === 0) continue;

      const posActual    = calcularPosicionActual(posiciones, segTrans);
      const posIdAnterior = ruta.position_id_actual;
      const posIdActual   = posActual.position_id;
      const ultimaPosId   = posiciones[posiciones.length - 1].position_id;

      // Estado según posición
      const nuevoEstado = posIdActual >= ultimaPosId ? 'FINALIZADA' : 'EN_RUTA';

      // ── 3. Calcular ETA estimado de fin ────────────────────
      const ultimaPos    = posiciones[posiciones.length - 1];
      const segRestantes = Math.max(0, (ultimaPos.offset_seg - segTrans) / TIME_MULTIPLIER);
      const horaEstimFin = new Date(Date.now() + segRestantes * 1000);

      // ── 4. Actualizar estado_ruta ──────────────────────────
      await client.query(
        `UPDATE estado_ruta
         SET estado             = $1,
             position_id_actual = $2,
             lat_actual         = $3,
             lng_actual         = $4,
             hora_estim_fin     = $5,
             actualizado_at     = NOW()
         WHERE ruta_id = $6`,
        [nuevoEstado, posIdActual, posActual.lat, posActual.lng,
         horaEstimFin.toISOString(), ruta.id]
      );

      console.log(
        `  🚛 ${ruta.route_id} → pos ${posIdActual}/${ultimaPosId} ` +
        `[${nuevoEstado}] (${Math.round(segTrans / 60)} min simulados)`
      );

      // ── 5. Disparar trigger si cruzamos un umbral ──────────
      if (TRIGGERS.has(posIdActual) && posIdActual !== posIdAnterior) {
        await publicarTrigger(ruta, posActual);
      }
    }

  } catch (err) {
    console.error('❌ Error en simulación:', err.message);
  } finally {
    client.release();
  }
}

module.exports = { ejecutarSimulacion };
