// Verifica marcar_perdida_mesa: dueño/encargado pueden condonar deuda de mesa
// dejando registro (metodo='perdida', motivo obligatorio); un mesero raso NO
// puede; no se puede marcar dos veces el mismo pedido; los reportes
// (rendimiento_meseros, mis_cuentas_mesa) NO cuentan la perdida como venta.
// Usa el restaurante DEMO, mesa TP99, 1 dueño temporal via perfil + 1 staff.
import { createClient } from '@supabase/supabase-js';

const URL = process.env.SUPABASE_URL || process.env.VITE_SUPABASE_URL;
const ANON = process.env.SUPABASE_ANON_KEY || process.env.VITE_SUPABASE_ANON_KEY;
const SERVICE = process.env.SUPABASE_SERVICE_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!URL || !ANON || !SERVICE) {
  console.log('Faltan SUPABASE_URL / SUPABASE_ANON_KEY / SUPABASE_SERVICE_KEY');
  process.exit(1);
}

let fallos = 0;
const check = (cond, msg) => {
  console.log((cond ? '  OK  ' : '  XX  ') + msg);
  if (!cond) fallos++;
};
const nuevo = () => createClient(URL, ANON, { auth: { persistSession: false } });
const svc = createClient(URL, SERVICE, { auth: { persistSession: false } });
const MESA = 'TP99';

const { data: rest } = await svc.from('restaurantes').select('id,abierto').eq('slug', 'demo-gustito').maybeSingle();
if (!rest?.id || !rest.abierto) {
  console.log('Demo no disponible');
  process.exit(1);
}
const REST = rest.id;
const { data: prods } = await svc.from('productos').select('id').eq('restaurante_id', REST).eq('disponible', true).limit(1);
const PROD_ID = prods?.[0]?.id;

const EMAIL_DUENO = 'dueno.test.perdida@gustitoxpress.app';
const EMAIL_STAFF = 'staff.test.perdida@gustitoxpress.app';
const CLAVE = 'PerdidaTest2026!';
let uidDueno, uidStaff, cuentaId;

const limpiar = async () => {
  if (cuentaId) {
    await svc.from('pagos_mesa').delete().eq('cuenta_id', cuentaId);
    await svc.from('pedidos').update({ cuenta_id: null }).eq('cuenta_id', cuentaId);
    await svc.from('cuentas_mesa').delete().eq('id', cuentaId);
  }
  if (uidDueno) await svc.auth.admin.deleteUser(uidDueno).catch(() => {});
  if (uidStaff) await svc.auth.admin.deleteUser(uidStaff).catch(() => {});
};

try {
  await limpiar();
  if (!PROD_ID) throw new Error('Demo sin productos disponibles');

  // El dueño real del demo ya existe; para no tocarlo, creamos un dueño de
  // prueba propio... pero fn_es_dueno_de mira perfiles.restaurante_id+rol, así
  // que un perfil rol='dueno' del mismo restaurante alcanza para la prueba.
  const { data: uD, error: eD } = await svc.auth.admin.createUser({ email: EMAIL_DUENO, password: CLAVE, email_confirm: true });
  if (eD) throw new Error('createUser dueño: ' + eD.message);
  uidDueno = uD.user.id;
  await svc.from('perfiles').upsert({ id: uidDueno, rol: 'dueno', restaurante_id: REST, nombre: 'Dueño Test' });

  const { data: uS, error: eS } = await svc.auth.admin.createUser({ email: EMAIL_STAFF, password: CLAVE, email_confirm: true });
  if (eS) throw new Error('createUser staff: ' + eS.message);
  uidStaff = uS.user.id;
  await svc.from('perfiles').upsert({ id: uidStaff, rol: 'staff', restaurante_id: REST, nombre: 'Mesero Test' });

  const dueno = nuevo();
  await dueno.auth.signInWithPassword({ email: EMAIL_DUENO, password: CLAVE });
  const mesero = nuevo();
  await mesero.auth.signInWithPassword({ email: EMAIL_STAFF, password: CLAVE });

  // 3 pedidos de "comensales" distintos en la misma mesa (7-personas-1-QR: cada
  // quien pide aparte, cae en la misma cuenta).
  const { data: cId } = await mesero.rpc('abrir_mesa', { p_mesa: MESA, p_comensales: 3, p_modo: 'unica' });
  cuentaId = cId;
  const pedidoIds = [];
  for (const nombre of ['Ana', 'Beto', 'Caro']) {
    const { data: ped, error: eP } = await mesero.rpc('crear_pedido', {
      p_restaurante_id: REST,
      p_cliente_nombre: nombre,
      p_cliente_telefono: '04120001111',
      p_tipo_entrega: 'mesa',
      p_metodo_pago: 'efectivo',
      p_items: [{ producto_id: PROD_ID, cantidad: 1 }],
      p_mesa: MESA,
    });
    if (eP) throw new Error(`crear_pedido ${nombre}: ` + eP.message);
    pedidoIds.push(ped[0].pedido_id);
  }
  check(pedidoIds.length === 3, 'se crearon 3 pedidos (Ana, Beto, Caro) en la misma mesa');

  // El staff (mesero raso) NO puede marcar perdida.
  const { error: eStaffPerdida } = await mesero.rpc('marcar_perdida_mesa', {
    p_cuenta_id: cuentaId,
    p_pedido_ids: [pedidoIds[0]],
    p_motivo: 'intento mesero',
  });
  check(!!eStaffPerdida, 'el mesero raso NO puede marcar_perdida_mesa' + (eStaffPerdida ? '' : ' (FALLO: no dio error)'));

  // Sin motivo, falla.
  const { error: eSinMotivo } = await dueno.rpc('marcar_perdida_mesa', {
    p_cuenta_id: cuentaId,
    p_pedido_ids: [pedidoIds[0]],
    p_motivo: '  ',
  });
  check(!!eSinMotivo, 'sin motivo, marcar_perdida_mesa rechaza' + (eSinMotivo ? '' : ' (FALLO: no dio error)'));

  // El dueño marca Ana+Beto como perdida (2 de 3), con motivo.
  const { data: fila, error: ePerdida } = await dueno.rpc('marcar_perdida_mesa', {
    p_cuenta_id: cuentaId,
    p_pedido_ids: [pedidoIds[0], pedidoIds[1]],
    p_motivo: 'Se fueron sin pagar, no se pudo cobrar',
  });
  check(!ePerdida && fila?.[0]?.monto_usd > 0, 'el dueño marca 2 pedidos como pérdida' + (ePerdida ? ' -> ' + ePerdida.message : ''));

  // No se puede volver a marcar/cobrar el mismo pedido (ya tiene pago_mesa_id).
  const { error: eDoble } = await dueno.rpc('marcar_perdida_mesa', {
    p_cuenta_id: cuentaId,
    p_pedido_ids: [pedidoIds[0]],
    p_motivo: 'de nuevo',
  });
  check(!!eDoble, 'no se puede marcar como pérdida un pedido ya resuelto (anti doble)');

  // Caro (el 3ro) SI se cobra de verdad, en efectivo.
  const { error: eCobrarCaro } = await dueno.rpc('cobrar_parte_mesa', {
    p_cuenta_id: cuentaId,
    p_pedido_ids: [pedidoIds[2]],
    p_metodo: 'efectivo',
  });
  check(!eCobrarCaro, 'el pedido de Caro SI se cobra normal en efectivo' + (eCobrarCaro ? ' -> ' + eCobrarCaro.message : ''));

  // mis_cuentas_mesa: pagado_usd = solo Caro (real); perdida_usd = Ana+Beto.
  const { data: listaMesas, error: eLista } = await dueno.rpc('mis_cuentas_mesa', { p_restaurante_id: REST });
  const filaMesa = (listaMesas ?? []).find((c) => c.id === cuentaId);
  check(!eLista && !!filaMesa, 'mis_cuentas_mesa devuelve la cuenta' + (eLista ? ' -> ' + eLista.message : ''));
  if (filaMesa) {
    const soloCaro = Number(filaMesa.pagado_usd) > 0 && Number(filaMesa.pagado_usd) < Number(filaMesa.total_usd);
    check(soloCaro, `pagado_usd (${filaMesa.pagado_usd}) NO incluye la pérdida, solo lo real de Caro`);
    check(Number(filaMesa.perdida_usd) > 0, `perdida_usd (${filaMesa.perdida_usd}) refleja lo marcado como pérdida`);
  }

  // rendimiento_meseros: ventas_usd = solo lo cobrado real (Caro); perdidas_usd aparte.
  const desde = new Date(Date.now() - 60 * 60 * 1000).toISOString();
  const { data: rend, error: eRend } = await dueno.rpc('rendimiento_meseros', { p_restaurante_id: REST, p_desde: desde });
  const filaMesero = (rend ?? []).find((m) => m.mesero_id === uidStaff);
  check(!eRend && !!filaMesero, 'rendimiento_meseros devuelve al mesero' + (eRend ? ' -> ' + eRend.message : ''));
  if (filaMesero) {
    check(Number(filaMesero.perdidas_usd) > 0, `rendimiento_meseros.perdidas_usd (${filaMesero.perdidas_usd}) refleja la pérdida`);
    check(
      Number(filaMesero.ventas_usd) > 0 && Number(filaMesero.ventas_usd) < Number(filaMesero.perdidas_usd) + Number(filaMesero.ventas_usd),
      `rendimiento_meseros.ventas_usd (${filaMesero.ventas_usd}) NO incluye la pérdida`,
    );
  }

  // Ahora se cobra y cierra: al quedar todo resuelto (Caro real + Ana/Beto perdida
  // verificada), cobrar_y_cerrar_mesa debe poder cerrar la mesa sin nada pendiente.
  const { data: cierre, error: eCierre } = await dueno.rpc('cobrar_y_cerrar_mesa', { p_cuenta_id: cuentaId, p_metodo: 'efectivo' });
  check(!eCierre && cierre?.[0]?.cerrada === true, 'la mesa cierra sola (Caro cobrado + Ana/Beto en pérdida = nada pendiente)' + (eCierre ? ' -> ' + eCierre.message : ''));

  // historial_mesas: cobrado_usd excluye la perdida, perdida_usd la muestra aparte.
  const { data: hist, error: eHist } = await dueno.rpc('historial_mesas', { p_restaurante_id: REST, p_dias: 1 });
  const filaHist = (hist ?? []).find((h) => h.id === cuentaId);
  check(!eHist && !!filaHist, 'historial_mesas devuelve la mesa cerrada' + (eHist ? ' -> ' + eHist.message : ''));
  if (filaHist) {
    check(Number(filaHist.perdida_usd) > 0, `historial_mesas.perdida_usd (${filaHist.perdida_usd}) refleja la pérdida`);
    check(
      Number(filaHist.cobrado_usd) < Number(filaHist.total_usd),
      `historial_mesas.cobrado_usd (${filaHist.cobrado_usd}) NO incluye la pérdida (total ${filaHist.total_usd})`,
    );
  }
} catch (e) {
  console.log('  XX  Excepción: ' + e.message);
  fallos++;
} finally {
  await limpiar();
  console.log('\nLimpieza hecha (mesa, pedidos, pagos y usuarios de prueba fuera)');
}

console.log(fallos === 0 ? '\nMARCAR PERDIDA OK' : `\nMARCAR PERDIDA: ${fallos} FALLO(S)`);
process.exit(fallos === 0 ? 0 : 1);
