// EL SINCRONIZADOR: el hilo que abraza los dos mundos. Mantiene su PROPIA
// sesion con la nube (con paciencia infinita: si no hay internet, insiste),
// y cuando la nube esta viva: refresca el espejo del menu (cada 5 min), manda
// el LATIDO del cerebrito (cada minuto) y SUBE las ventas del buzon de salida
// (idempotente: reintentos no duplican nada gracias a los UUID).
import { createClient, type SupabaseClient } from '@supabase/supabase-js';
import { VERSION_CEREBRITO } from './actualizador.js';
import { ipLocal } from './escaner.js';
import { refrescarEspejo, type Espejo } from './espejo.js';
import type { Salon } from './salon.js';
import type { Config } from './tipos.js';

export interface Sincronizador {
  estado(): { nubeViva: boolean; ultimoLatido: string | null; ventasPorSubir: number };
  detener(): void;
}

export function iniciarSincronizador(deps: {
  cfg: Config;
  base: string;
  salon: Salon;
  espejoSet: (e: Espejo) => void;
  log: (m: string) => void;
}): Sincronizador {
  const { cfg, log } = deps;
  const supabase: SupabaseClient = createClient(cfg.supabaseUrl, cfg.anonKey, {
    auth: { persistSession: false, autoRefreshToken: true },
  });

  let nubeViva = false;
  let restauranteId: string | null = null;
  let ultimoLatido: string | null = null;
  let ultimoEspejo = 0;
  let ocupado = false;
  let detenido = false;
  let avisoCaida = false;

  async function asegurarSesion(): Promise<boolean> {
    const { data } = await supabase.auth.getSession();
    if (data.session && restauranteId) return true;
    const { data: ses, error } = await supabase.auth.signInWithPassword({
      email: cfg.correo,
      password: cfg.clave,
    });
    if (error || !ses.session) return false;
    const { data: perfil } = await supabase
      .from('perfiles')
      .select('restaurante_id')
      .eq('id', ses.session.user.id)
      .maybeSingle();
    restauranteId = perfil?.restaurante_id ?? null;
    return !!restauranteId;
  }

  async function tick(): Promise<void> {
    if (ocupado || detenido) return;
    ocupado = true;
    try {
      const ok = await asegurarSesion().catch(() => false);
      if (!ok || !restauranteId) {
        if (nubeViva || !avisoCaida) {
          log('Sin nube por ahora: el salon sigue local y las ventas esperan en el buzon.');
          avisoCaida = true;
        }
        nubeViva = false;
        return;
      }
      if (!nubeViva) {
        nubeViva = true;
        avisoCaida = false;
        log('Nube conectada: sincronizando.');
      }

      // Espejo del menu (cada 5 minutos).
      if (Date.now() - ultimoEspejo > 300_000) {
        try {
          const e = await refrescarEspejo(supabase, restauranteId, deps.base);
          deps.espejoSet(e);
          ultimoEspejo = Date.now();
        } catch (e) {
          log('No pude refrescar el espejo: ' + (e as Error).message);
        }
      }

      // Latido (cada minuto). Incluye la IP local: asi el PANEL de las tablets
      // aprende solo a donde esta la laptop y el boton "Trabajar sin internet"
      // funciona sin que nadie copie direcciones.
      if (!ultimoLatido || Date.now() - new Date(ultimoLatido).getTime() > 60_000) {
        const r = deps.salon.resumen();
        const { error } = await supabase.rpc('fn_cerebrito_latido', {
          p_version: VERSION_CEREBRITO,
          p_detalle: {
            ventas_por_subir: r.ventasPorSubir,
            mesas_abiertas: r.mesasAbiertas,
            ip: ipLocal(),
            pos_puerto: 8738,
          },
        });
        if (!error) ultimoLatido = new Date().toISOString();
      }

      // Subir el buzon de salida, venta por venta.
      for (const venta of deps.salon.ventasPendientes()) {
        const carga = {
          ...(venta.cuenta ? { cuenta: venta.cuenta } : {}),
          pedidos: venta.pedidos,
          pagos: venta.pagos,
        };
        const { data, error } = await supabase.rpc('sincronizar_venta_local', { p_venta: carga });
        if (error) {
          log(`No subio la venta ${venta.id.slice(0, 8)}: ${error.message} (reintento luego)`);
          if (/jwt|token|expired|401/i.test(error.message)) await supabase.auth.signOut().catch(() => {});
          break; // no martillar: al proximo tick
        }
        deps.salon.marcarSincronizada(venta.id);
        const d = (data ?? {}) as { pedidos_nuevos?: number };
        log(`Venta subida a la nube (${d.pedidos_nuevos ?? 0} pedido(s) nuevos).`);
      }
    } finally {
      ocupado = false;
    }
  }

  const timer = setInterval(() => void tick(), 15_000);
  void tick();

  return {
    estado() {
      return { nubeViva, ultimoLatido, ventasPorSubir: deps.salon.resumen().ventasPorSubir };
    },
    detener() {
      detenido = true;
      clearInterval(timer);
    },
  };
}
