import { useEffect, useState } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import {
  bsDesdeUsd,
  etiquetaReferencia,
  faltaReferencia,
  formatBs,
  formatUsd,
  necesitaReferencia,
  necesitaVerificacion,
  type Restaurante,
} from '@gustito/shared';
import { Icono } from '@gustito/shared/iconos';
import { supabase } from '../lib/supabase';
import { subirComprobante } from '../lib/storage';
import {
  DatosFactura,
  SIN_DATOS_FISCALES,
  guardarDatosFiscales,
  hayDatosFiscales,
  useFacturaActiva,
  type DatosFiscales,
} from '../components/DatosFactura';
import { useAuth } from '../lib/auth';
import { useCambiosRestaurante } from '../lib/tiemporeal';
import PuntoDeVenta from './PuntoDeVenta';

const PROD = import.meta.env.PROD;
const TIENDA = PROD ? 'https://pedir.gustitoxpress.app' : 'http://localhost:5173';

interface CuentaFila {
  id: string;
  mesa: string;
  estado: string;
  total_usd: number;
  tasa_bs: number | null;
  abierta_en: string;
  pago_solicitado_en: string | null;
  metodo_pago: string | null;
  referencia: string | null;
  comprobante_url: string | null;
  n_pedidos: number;
  mesero_id: string | null;
  mesero_nombre: string | null;
  pagado_usd: number;
  abierta_por: string | null;
  todo_entregado: boolean;
  perdida_usd?: number;
  /** Cuando se sirvio lo ultimo de la mesa: con eso sabemos hace CUANTO esta lista para cobrar. */
  servido_en: string | null;
}

// "hace 3 h 10 min" / "hace 45 min". Sirve para que la mesa diga sola cuanto
// tiempo lleva, en vez de que el dueño lo tenga que adivinar.
function haceCuanto(desde: string, ahora: number): string {
  const min = Math.max(0, Math.floor((ahora - new Date(desde).getTime()) / 60000));
  if (min < 60) return `${min} min`;
  const h = Math.floor(min / 60);
  const m = min % 60;
  return m === 0 ? `${h} h` : `${h} h ${m} min`;
}
const minutosDesde = (desde: string | null, ahora: number): number | null =>
  desde ? Math.max(0, Math.floor((ahora - new Date(desde).getTime()) / 60000)) : null;

// Cuando una mesa pasa de esto sin que nadie cobre, deja de ser normal y hay que
// gritarlo: se sirvio y no cobraron, o lleva demasiado abierta con saldo.
const MIN_SERVIDA_SIN_COBRAR = 20;
const MIN_ABIERTA_SIN_COBRAR = 120;

interface HistorialFila {
  id: string;
  mesa: string;
  abierta_en: string;
  cerrada_en: string;
  total_usd: number;
  cobrado_usd: number;
  n_pedidos: number;
  mesero_nombre: string | null;
  comensales: number;
  modo_cuenta: string;
  metodo_pago: string | null;
  tasa_bs: number | null;
}

interface PedidoParte {
  id: string;
  codigo: string;
  cliente_nombre: string;
  total_usd: number;
  estado: string;
  pago_mesa_id: string | null;
}

interface PagoParte {
  id: string;
  monto_usd: number;
  metodo: string;
  referencia: string | null;
  nota: string | null;
  mesero_nombre: string | null;
  creado_en: string;
  verificado: boolean;
  comprobante_url: string | null;
  comensal_id: string | null;
}

const METODO: Record<string, string> = {
  pago_movil: 'Pago Movil',
  efectivo: 'Efectivo',
  transferencia: 'Transferencia',
  zelle: 'Zelle',
  punto_venta: 'Punto de venta',
  perdida: 'Pérdida (no cobrado)',
};
// 'perdida' no es un metodo de cobro elegible (es la accion aparte "marcar como
// perdida"): los selectores de "con que cobro" excluyen esa entrada de METODO.
const METODOS_COBRO: [string, string][] = Object.entries(METODO).filter(([v]) => v !== 'perdida');

// Ojo: esto es '¿hay que ir a comprobar que la plata llego?'. El punto de
// venta NO entra aqui (lo aprobo el banco), pero SI pide referencia.
const esPagoOnline = (m: string | null) => necesitaVerificacion(m);

function beep() {
  try {
    const ctx = new AudioContext();
    const o = ctx.createOscillator();
    o.frequency.value = 880;
    o.connect(ctx.destination);
    o.start();
    setTimeout(() => {
      o.stop();
      ctx.close();
    }, 220);
  } catch {
    /* sin audio */
  }
}

export default function Mesas({
  restaurante,
  marca,
  txtMarca,
  acento,
}: {
  restaurante: Restaurante;
  marca: string;
  txtMarca: string;
  acento: string;
}) {
  const qc = useQueryClient();
  const { perfil } = useAuth();
  const esStaff = perfil?.rol === 'staff';
  const esEncargado = perfil?.rol === 'encargado';
  const [num, setNum] = useState(restaurante.num_mesas || 0);
  const [promedio, setPromedio] = useState(restaurante.sala_promedio_min ?? 10);
  const [partes, setPartes] = useState<CuentaFila | null>(null);
  const [detalle, setDetalle] = useState<{ id: string; mesa: string; total_usd: number } | null>(null);
  const [vista, setVista] = useState<'activas' | 'historial'>('activas');
  const [dias, setDias] = useState(1);
  // Reloj propio: el tiempo que lleva cada mesa tiene que correr solo en pantalla,
  // aunque no llegue ningun cambio del servidor (mismo patron que Cocina).
  const [ahora, setAhora] = useState(() => Date.now());
  useEffect(() => {
    const t = setInterval(() => setAhora(Date.now()), 30000);
    return () => clearInterval(t);
  }, []);
  const [abrir, setAbrir] = useState<string | null>(null);
  const [tomarMesa, setTomarMesa] = useState<string | null>(null);
  const [cobrarTodo, setCobrarTodo] = useState<CuentaFila | null>(null);
  const tasa = restaurante.tasa_bs;

  const cuentasQ = useQuery({
    queryKey: ['cuentas-mesa', restaurante.id],
    queryFn: async () => {
      const { data, error } = await supabase.rpc('mis_cuentas_mesa', { p_restaurante_id: restaurante.id });
      if (error) throw error;
      return (data as unknown as CuentaFila[]) ?? [];
    },
    refetchInterval: 6000,
  });

  const historialQ = useQuery({
    queryKey: ['historial-mesas', restaurante.id, dias],
    enabled: vista === 'historial',
    queryFn: async () => {
      const { data, error } = await supabase.rpc('historial_mesas', { p_restaurante_id: restaurante.id, p_dias: dias });
      if (error) throw error;
      return (data as unknown as HistorialFila[]) ?? [];
    },
  });

  // Tiempo real: suena cuando una mesa pide la cuenta (pasa a por_pagar).
  useCambiosRestaurante(restaurante.id, {
    onCambio: (c) => {
      if (c.tabla !== 'cuentas_mesa') return;
      qc.invalidateQueries({ queryKey: ['cuentas-mesa', restaurante.id] });
      if (c.nuevo?.estado === 'por_pagar' && c.viejo?.estado !== 'por_pagar') beep();
    },
    onResync: () => qc.invalidateQueries({ queryKey: ['cuentas-mesa', restaurante.id] }),
  });

  async function guardarNum(v: number) {
    const val = Math.max(0, Math.min(50, v));
    setNum(val);
    await supabase.from('restaurantes').update({ num_mesas: val }).eq('id', restaurante.id);
    qc.invalidateQueries({ queryKey: ['mi-restaurante'] });
  }

  async function guardarPromedio(v: number) {
    const val = Math.max(1, Math.min(120, v));
    setPromedio(val);
    await supabase.from('restaurantes').update({ sala_promedio_min: val }).eq('id', restaurante.id);
    qc.invalidateQueries({ queryKey: ['mi-restaurante'] });
  }

  async function verComprobante(path: string) {
    const { data, error } = await supabase.storage.from('comprobantes').createSignedUrl(path, 120);
    if (error) alert(error.message);
    else window.open(data.signedUrl, '_blank');
  }

  const cuentas = cuentasQ.data ?? [];
  const porMesa = new Map(cuentas.map((c) => [c.mesa, c]));
  const ocupadas = cuentas.length;
  const libres = Math.max(0, num - ocupadas);
  const porCobrar = cuentas.filter((c) => c.estado === 'por_pagar').length;

  // Lo que falta por cobrar en una mesa (lo marcado como perdida ya no se cobra).
  const saldoDe = (c: CuentaFila) => Math.max(0, c.total_usd - (c.pagado_usd ?? 0) - (c.perdida_usd ?? 0));
  // Mesa "olvidada": tiene plata sin cobrar y ya paso el tiempo razonable. Es el
  // caso que se comio 48 horas en Arepa Power sin que nadie se enterara.
  const olvidada = (c: CuentaFila) => {
    if (c.estado === 'por_pagar' || saldoDe(c) <= 0) return false;
    const servida = c.todo_entregado ? minutosDesde(c.servido_en, ahora) : null;
    const abierta = minutosDesde(c.abierta_en, ahora) ?? 0;
    return (servida != null && servida >= MIN_SERVIDA_SIN_COBRAR) || abierta >= MIN_ABIERTA_SIN_COBRAR;
  };
  const olvidadas = cuentas.filter(olvidada);
  const olvidadasUsd = olvidadas.reduce((s, c) => s + saldoDe(c), 0);

  return (
    <div className="space-y-4">
      {/* Encabezado + resumen */}
      <div className="flex flex-wrap items-center justify-between gap-3">
        <h1 className="text-xl font-extrabold">Mesas</h1>
        {num > 0 && (
          <div className="flex flex-wrap gap-2 text-xs font-bold">
            <span className="rounded-full bg-green-500/15 px-3 py-1 text-green-300">{libres} libres</span>
            <span className="rounded-full px-3 py-1" style={{ background: acento + '26', color: acento }}>
              {ocupadas} ocupada{ocupadas === 1 ? '' : 's'}
            </span>
            {porCobrar > 0 && (
              <span className="rounded-full bg-amber-500/15 px-3 py-1 text-amber-300">{porCobrar} por cobrar</span>
            )}
            {olvidadas.length > 0 && (
              <span className="rounded-full bg-red-500/20 px-3 py-1 text-red-300 ring-1 ring-red-400/40">
                {olvidadas.length} sin cobrar hace rato · {formatUsd(olvidadasUsd)}
              </span>
            )}
          </div>
        )}
      </div>

      {/* El letrero que faltaba: si hay mesas con plata sin cobrar y ya paso el
          tiempo, se dice aqui arriba con nombre y monto. Nadie tiene que ir mesa
          por mesa a descubrirlo, ni esperar a que alguien marque "entregado". */}
      {vista === 'activas' && olvidadas.length > 0 && (
        <div className="rounded-2xl bg-red-500/10 p-3 ring-1 ring-red-400/40">
          <p className="text-sm font-extrabold text-red-300">
            Ojo: {olvidadas.length === 1 ? 'hay 1 mesa' : `hay ${olvidadas.length} mesas`} sin cobrar
          </p>
          <div className="mt-1.5 flex flex-wrap gap-1.5">
            {olvidadas.map((c) => (
              <span key={c.id} className="rounded-lg bg-red-500/15 px-2 py-1 text-xs font-bold text-red-200">
                Mesa {c.mesa} · {formatUsd(saldoDe(c))} · hace {haceCuanto(c.abierta_en, ahora)}
              </span>
            ))}
          </div>
          <p className="mt-1.5 text-xs text-niebla">
            Cóbralas, o si la gente ya se fue sin pagar, márcalo como pérdida desde &quot;Cobrar por partes&quot; para que
            quede en el reporte.
          </p>
        </div>
      )}

      {/* Cambio entre mesas activas y el historial de cerradas */}
      <div className="flex gap-1.5">
        {(['activas', 'historial'] as const).map((v) => (
          <button
            key={v}
            onClick={() => setVista(v)}
            className="rounded-full px-3.5 py-1.5 text-sm font-bold transition"
            style={
              vista === v
                ? { background: marca, color: txtMarca }
                : { background: 'rgba(255,255,255,.08)', color: 'rgba(255,255,255,.8)' }
            }
          >
            {v === 'activas' ? 'Activas' : 'Cerradas'}
          </button>
        ))}
      </div>

      {/* Configuración de sala (del dueño; mesero y encargado solo operan) */}
      {vista === 'activas' && !esStaff && !esEncargado && (
      <div className="flex flex-wrap items-center gap-3 rounded-2xl bg-superficie p-3 ring-1 ring-white/10">
        <label className="flex items-center gap-2 text-sm">
          <span className="font-semibold">Número de mesas</span>
          <input
            type="number"
            min={0}
            max={50}
            value={num}
            onChange={(e) => guardarNum(Number(e.target.value) || 0)}
            className="input w-20"
          />
        </label>
        <label className="flex items-center gap-2 text-sm">
          <span className="font-semibold">Min. promedio por pedido</span>
          <input
            type="number"
            min={1}
            max={120}
            value={promedio}
            onChange={(e) => guardarPromedio(Number(e.target.value) || 10)}
            className="input w-20"
          />
        </label>
        <a
          href={`${TIENDA}/sala/${restaurante.slug}`}
          target="_blank"
          rel="noreferrer"
          className="btn"
          style={{ background: marca, color: txtMarca }}
        >
          📺 Abrir pantalla de sala (TV)
        </a>
        <span className="ml-auto text-xs text-niebla">Imprime los QR en Configuración</span>
      </div>
      )}

      {vista === 'activas' && (num === 0 ? (
        <div className="rounded-2xl border border-dashed border-white/15 bg-superficie py-12 text-center">
          <Icono nombre="cubiertos" className="mx-auto h-10 w-10 text-white/30" />
          <p className="mt-2 font-bold text-white">Aún no configuraste mesas</p>
          <p className="mt-1 text-sm text-niebla">Pon cuántas mesas tienes arriba para verlas aquí.</p>
        </div>
      ) : (
        <div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
          {Array.from({ length: num }, (_, i) => String(i + 1)).map((mesa) => {
            const c = porMesa.get(mesa);
            const porPagar = c?.estado === 'por_pagar';
            const totalBs = c && tasa ? bsDesdeUsd(c.total_usd, tasa) : null;
            const saldo = c ? saldoDe(c) : 0;
            const esOlvidada = !!c && olvidada(c);
            const minServida = c?.todo_entregado ? minutosDesde(c.servido_en, ahora) : null;
            // Ahora se ven TODAS las mesas ocupadas (para saber cuales estan
            // libres de verdad), pero un mesero solo OPERA la suya o una sin
            // asignar; el dueno/admin opera cualquiera. El candado real vive
            // en BD (fn_mesa_de_staff); esto solo evita el intento fallido.
            const puedeOperar = !esStaff || !c?.mesero_id || c.mesero_id === perfil?.id;
            const textoCobrar = c
              ? esPagoOnline(c.metodo_pago) && porPagar
                ? 'Verificar pago y liberar'
                : (c.pagado_usd ?? 0) > 0
                  ? 'Cobrar el resto y liberar'
                  : 'Cobrar y liberar'
              : '';
            return (
              <div
                key={mesa}
                className="rounded-2xl border p-4 transition"
                style={
                  porPagar
                    ? { borderColor: acento, background: acento + '14', boxShadow: `0 0 0 1px ${acento}` }
                    : esOlvidada
                      ? // Mesa con plata sin cobrar y demasiado tiempo: que salte a la vista
                        { borderColor: '#F87171', background: 'rgba(248,113,113,0.08)', boxShadow: '0 0 0 1px #F87171' }
                      : { borderColor: 'rgba(255,255,255,0.10)', background: c ? '#141627' : '#1C1F38' }
                }
              >
                <div className="flex items-center justify-between">
                  <p className="text-lg font-extrabold text-white">Mesa {mesa}</p>
                  {c ? (
                    <span className="rounded-full px-2 py-0.5 text-[11px] font-bold" style={{ background: acento + '26', color: acento }}>
                      Ocupada
                    </span>
                  ) : (
                    <span className="rounded-full bg-green-500/15 px-2 py-0.5 text-[11px] font-bold text-green-300">Disponible</span>
                  )}
                </div>

                {c && (
                  <div className="mt-2">
                    <p className="text-xs text-niebla">
                      {c.n_pedidos} pedido{c.n_pedidos === 1 ? '' : 's'} · abierta hace{' '}
                      <span className={esOlvidada ? 'font-bold text-red-300' : ''}>{haceCuanto(c.abierta_en, ahora)}</span>
                    </p>
                    {c.mesero_nombre && (
                      <p className="text-xs font-bold" style={{ color: c.mesero_id === perfil?.id ? acento : 'rgba(255,255,255,.75)' }}>
                        Atiende: {c.mesero_id === perfil?.id ? 'tú' : c.mesero_nombre}
                      </p>
                    )}
                    {c.abierta_por && <p className="truncate text-xs text-niebla">Mesa de: {c.abierta_por}</p>}
                    <p className="text-xl font-extrabold" style={{ color: acento }}>{formatUsd(c.total_usd)}</p>
                    {totalBs && <p className="text-xs text-niebla">{formatBs(totalBs)}</p>}
                    {(c.pagado_usd ?? 0) > 0 && (
                      <p className="mt-0.5 text-xs font-bold text-green-300">
                        Pagado {formatUsd(c.pagado_usd)} · Falta {formatUsd(Math.max(0, c.total_usd - c.pagado_usd))}
                      </p>
                    )}
                    {porPagar && (
                      <div className="mt-2 rounded-lg px-2 py-1.5 text-xs font-bold" style={{ background: acento + '26', color: acento }}>
                        {esPagoOnline(c.metodo_pago) ? '💳 Pago por revisar' : '🔔 Pidió la cuenta'}
                        {c.metodo_pago ? ` · ${METODO[c.metodo_pago] ?? c.metodo_pago}` : ''}
                        {c.referencia && (
                          <span className="mt-0.5 block text-sm font-extrabold text-white">Ref: {c.referencia}</span>
                        )}
                      </div>
                    )}
                    {/* LETRERO DE SIN COBRAR. Antes solo decia "ya se sirvio todo,
                        falta cobrar", sin monto ni tiempo, y solo si TODOS los
                        pedidos estaban marcados entregado (con la cocina de papel
                        casi nadie los marca). Por eso una mesa de 3 horas se veia
                        igual que una de 8 minutos. Ahora dice cuanto falta, desde
                        cuando, y se pone rojo cuando ya paso el tiempo razonable. */}
                    {!porPagar && saldo > 0 && (c.todo_entregado || esOlvidada) && (
                      <div
                        className={`mt-2 rounded-lg px-2 py-1.5 text-xs font-bold ${
                          esOlvidada ? 'bg-red-500/20 text-red-300 ring-1 ring-red-400/40' : 'bg-amber-500/15 text-amber-300'
                        }`}
                      >
                        {c.todo_entregado ? (
                          <>
                            Ya se sirvió todo
                            {minServida != null && ` hace ${haceCuanto(c.servido_en!, ahora)}`}
                            {esOlvidada ? ' y nadie ha cobrado' : ' · Falta cobrar'}
                          </>
                        ) : (
                          <>Lleva {haceCuanto(c.abierta_en, ahora)} abierta y no se ha cobrado</>
                        )}
                        <span className="mt-0.5 block text-sm font-extrabold text-white">
                          Faltan {formatUsd(saldo)} por cobrar
                        </span>
                      </div>
                    )}
                    {(c.perdida_usd ?? 0) > 0 && (
                      <p className="mt-0.5 text-xs font-bold text-red-400">
                        Pérdida marcada {formatUsd(c.perdida_usd!)}
                      </p>
                    )}
                    {c.comprobante_url ? (
                      <button
                        onClick={() => verComprobante(c.comprobante_url!)}
                        className="mt-1 text-xs font-bold underline"
                        style={{ color: acento }}
                      >
                        Ver comprobante
                      </button>
                    ) : porPagar && esPagoOnline(c.metodo_pago) ? (
                      <p className="mt-1 text-[11px] text-niebla">Sin captura, verifica por la referencia</p>
                    ) : null}
                    {puedeOperar ? (
                      <select
                        value=""
                        onChange={(e) => {
                          const accion = e.target.value;
                          if (accion === 'tomar') setTomarMesa(mesa);
                          else if (accion === 'ver') setDetalle(c);
                          else if (accion === 'cobrar') setCobrarTodo(c);
                          else if (accion === 'partes') setPartes(c);
                        }}
                        className="mt-2 w-full rounded-xl border border-white/15 bg-superficie-2 px-3 py-2.5 text-sm font-bold text-white outline-none focus:border-white/35"
                      >
                        <option value="" disabled>
                          Elegir acción...
                        </option>
                        <option value="tomar">Tomar pedido</option>
                        <option value="ver">Ver lo que pidió</option>
                        <option value="cobrar">{textoCobrar}</option>
                        <option value="partes">Cobrar por partes</option>
                      </select>
                    ) : (
                      <button
                        onClick={() => setDetalle(c)}
                        className="mt-2 w-full rounded-xl py-2 text-sm font-bold text-white/90 ring-1 ring-white/15 hover:bg-white/5"
                      >
                        Ver lo que pidió
                      </button>
                    )}
                  </div>
                )}
                {!c && (
                  <button
                    onClick={() => setAbrir(mesa)}
                    className="mt-3 w-full rounded-xl py-2 text-sm font-bold"
                    style={{ background: marca, color: txtMarca }}
                  >
                    Abrir mesa
                  </button>
                )}
              </div>
            );
          })}
        </div>
      ))}

      {vista === 'historial' && (
        <HistorialMesas
          historial={historialQ.data ?? []}
          cargando={historialQ.isLoading}
          dias={dias}
          onDias={setDias}
          acento={acento}
          onDetalle={(h) => setDetalle({ id: h.id, mesa: h.mesa, total_usd: h.total_usd })}
        />
      )}

      {partes && (
        <CobroPartes
          cuenta={partes}
          marca={marca}
          txtMarca={txtMarca}
          acento={acento}
          esStaff={esStaff}
          tasa={tasa}
          restauranteId={restaurante.id}
          onCerrar={() => setPartes(null)}
        />
      )}

      {detalle && <MesaDetalle cuenta={detalle} acento={acento} tasa={tasa} onCerrar={() => setDetalle(null)} />}

      {abrir && (
        <AbrirMesa
          mesa={abrir}
          marca={marca}
          txtMarca={txtMarca}
          acento={acento}
          onCerrar={() => setAbrir(null)}
          onAbierta={(mesaAbierta) => {
            setAbrir(null);
            cuentasQ.refetch();
            setTomarMesa(mesaAbierta);
          }}
        />
      )}

      {tomarMesa && (
        <PuntoDeVenta
          restaurante={restaurante}
          marca={marca}
          txtMarca={txtMarca}
          acento={acento}
          mesaInicial={tomarMesa}
          onClose={() => {
            setTomarMesa(null);
            cuentasQ.refetch();
          }}
        />
      )}

      {cobrarTodo && (
        <CobrarTodo
          cuenta={cobrarTodo}
          marca={marca}
          txtMarca={txtMarca}
          tasa={tasa}
          restauranteId={restaurante.id}
          onCerrar={() => setCobrarTodo(null)}
        />
      )}
    </div>
  );
}

// ── Cobrar todo y cerrar la mesa: pregunta el metodo y REGISTRA el pago ──
// El mesero solo puede efectivo; los pagos electronicos los confirma el dueño.
function CobrarTodo({
  cuenta,
  marca,
  txtMarca,
  tasa,
  restauranteId,
  onCerrar,
}: {
  cuenta: CuentaFila;
  marca: string;
  txtMarca: string;
  tasa: number | null;
  restauranteId: string;
  onCerrar: () => void;
}) {
  const qc = useQueryClient();
  const falta = Math.max(0, cuenta.total_usd - (cuenta.pagado_usd ?? 0));
  const metodos: [string, string][] = METODOS_COBRO;
  const [metodo, setMetodo] = useState(cuenta.metodo_pago && METODO[cuenta.metodo_pago] ? cuenta.metodo_pago : 'efectivo');
  const [referencia, setReferencia] = useState(cuenta.referencia ?? '');
  const [comprobante, setComprobante] = useState<File | null>(null);
  const [propina, setPropina] = useState('');
  const [proc, setProc] = useState(false);
  const [err, setErr] = useState<string | null>(null);
  const [fiscal, setFiscal] = useState<DatosFiscales>(SIN_DATOS_FISCALES);
  const facturaActiva = useFacturaActiva(restauranteId);
  const online = necesitaReferencia(metodo);
  const propinaNum = Math.max(0, parseFloat(propina) || 0);
  const avisoRef = faltaReferencia(metodo, referencia);
  const refFalta = !!avisoRef;

  async function confirmar() {
    setProc(true);
    setErr(null);
    // Los datos del comprador van ANTES de cobrar: al cerrar la cuenta la venta
    // entra sola a la cola de sellado, y a un documento ya sellado no se le
    // cambian los datos (eso seria nota de credito).
    if (facturaActiva && hayDatosFiscales(fiscal)) {
      const problema = await guardarDatosFiscales('cuenta', cuenta.id, fiscal);
      if (problema) {
        setProc(false);
        setErr(problema);
        return;
      }
    }
    // Foto del comprobante (opcional): si falla la subida no se bloquea el
    // cobro, solo se avisa, mismo patron que el POS (PuntoDeVenta.tsx).
    let comprobante_url: string | undefined;
    let avisoFoto = '';
    if (online && comprobante) {
      try {
        comprobante_url = await subirComprobante(comprobante, cuenta.id);
      } catch {
        avisoFoto = ' Ojo: no se pudo subir la foto del comprobante.';
      }
    }
    const { data, error } = await supabase.rpc('cobrar_y_cerrar_mesa', {
      p_cuenta_id: cuenta.id,
      p_metodo: metodo,
      p_referencia: online ? referencia.trim() || undefined : undefined,
      p_propina: propinaNum > 0 ? propinaNum : undefined,
      p_comprobante_url: comprobante_url,
    });
    setProc(false);
    if (error) {
      setErr(error.message);
      return;
    }
    qc.invalidateQueries({ queryKey: ['cuentas-mesa', restauranteId] });
    const cerrada = (data as unknown as { cerrada: boolean }[])?.[0]?.cerrada;
    if (cerrada === false) alert(`Pago registrado. El panel verifica la referencia y libera la mesa.${avisoFoto}`);
    else if (avisoFoto) alert(avisoFoto.trim());
    onCerrar();
  }

  return (
    <div className="fixed inset-0 z-[70] flex items-end justify-center bg-black/70 sm:items-center" onClick={onCerrar}>
      <div
        className="w-full max-w-md rounded-t-2xl bg-superficie p-4 text-white ring-1 ring-white/15 sm:rounded-2xl"
        onClick={(e) => e.stopPropagation()}
      >
        <div className="mb-3 flex items-center justify-between">
          <h3 className="text-lg font-extrabold">Cobrar y cerrar · Mesa {cuenta.mesa}</h3>
          <button onClick={onCerrar} className="grid h-9 w-9 place-items-center rounded-full bg-white/10 text-xl leading-none text-white/80">
            &times;
          </button>
        </div>
        <p className="mb-3 text-sm text-white/75">
          Falta por cobrar: <b className="text-white">{formatUsd(falta)}</b>
          {tasa && tasa > 0 ? ` · ${formatBs(bsDesdeUsd(falta, tasa))}` : ''}
        </p>

        <p className="mb-1.5 text-[13px] font-bold text-white/80">¿Cómo pagó?</p>
        <select
          value={metodo}
          onChange={(e) => setMetodo(e.target.value)}
          className="w-full rounded-xl border border-white/15 bg-superficie-2 px-3 py-2.5 text-sm font-bold text-white outline-none focus:border-white/35"
        >
          {metodos.map(([v, label]) => (
            <option key={v} value={v}>
              {label}
            </option>
          ))}
        </select>
        {online && (
          <p className="mt-2 text-xs text-niebla">
            {metodo === 'punto_venta'
              ? 'El banco ya aprobó el cobro. Copia el lote o la referencia del comprobante para cuadrar la caja.'
              : 'El panel verifica este pago y libera la mesa. Coloca los últimos 4 dígitos de la referencia.'}
          </p>
        )}
        {online && (
          <input
            className={`mt-2 w-full rounded-xl border bg-superficie-2 px-3 py-2.5 text-sm font-medium outline-none placeholder:text-white/45 focus:border-white/35 ${
              avisoRef ? 'border-red-500/60' : 'border-white/15'
            }`}
            placeholder={etiquetaReferencia(metodo)}
            inputMode="numeric"
            value={referencia}
            onChange={(e) => setReferencia(e.target.value)}
          />
        )}
        {avisoRef && <p className="mt-1 text-[12px] font-semibold text-red-400">{avisoRef}</p>}
        {online && (
          <label className="mt-2 flex cursor-pointer items-center justify-between gap-2 rounded-xl border border-dashed border-white/20 bg-superficie-2 px-3 py-2.5 text-sm font-medium text-white/70">
            <span className="truncate">{comprobante ? comprobante.name : 'Foto del comprobante (opcional)'}</span>
            <span className="shrink-0 rounded-lg px-2.5 py-1 text-xs font-bold" style={{ background: marca, color: txtMarca }}>
              {comprobante ? 'Cambiar' : 'Tomar foto'}
            </span>
            <input
              type="file"
              accept="image/*"
              capture="environment"
              className="hidden"
              onChange={(e) => setComprobante(e.target.files?.[0] ?? null)}
            />
          </label>
        )}

        {facturaActiva && <DatosFactura valor={fiscal} onCambio={setFiscal} />}

        <p className="mt-3 mb-1.5 text-[13px] font-bold text-white/80">Propina (opcional)</p>
        <div className="flex items-center gap-2 rounded-xl border border-white/15 bg-superficie-2 px-3 focus-within:border-white/35">
          <span className="text-sm font-bold text-white/55">$</span>
          <input
            className="w-full bg-transparent py-2.5 text-sm font-medium outline-none placeholder:text-white/45"
            inputMode="decimal"
            placeholder="0.00"
            value={propina}
            onChange={(e) => setPropina(e.target.value.replace(/[^0-9.]/g, ''))}
          />
        </div>

        {err && <p className="mt-3 rounded-lg bg-red-500/15 px-3 py-2 text-[13px] font-bold text-red-200 ring-1 ring-red-500/40">{err}</p>}
        <button
          onClick={confirmar}
          disabled={proc || refFalta}
          className="mt-3 w-full rounded-xl py-3 text-base font-extrabold shadow-lg transition active:scale-[.99] disabled:opacity-60"
          style={{ background: marca, color: txtMarca }}
        >
          {proc
            ? 'Registrando…'
            : online
              ? `Registrar pago${propinaNum > 0 ? ` + ${formatUsd(propinaNum)} propina` : ''}`
              : falta > 0
                ? `Cobrar ${formatUsd(falta + propinaNum)} y cerrar`
                : propinaNum > 0
                  ? `Cobrar ${formatUsd(propinaNum)} de propina y cerrar`
                  : 'Cerrar la mesa'}
        </button>
      </div>
    </div>
  );
}

// ── Abrir mesa: el mesero la abre eligiendo personas y modo (junta/separada) ──
function AbrirMesa({
  mesa,
  marca,
  txtMarca,
  acento,
  onCerrar,
  onAbierta,
}: {
  mesa: string;
  marca: string;
  txtMarca: string;
  acento: string;
  onCerrar: () => void;
  onAbierta: (mesa: string) => void;
}) {
  const [comensales, setComensales] = useState(1);
  const [modo, setModo] = useState<'unica' | 'separada'>('unica');
  const [nombres, setNombres] = useState<string[]>(['Persona 1']);
  // Modo "Todos juntos": un solo nombre de referencia (no hace falta uno por
  // persona, ya que pagan todo junto), para saber a nombre de quien quedo la mesa.
  const [nombreUnico, setNombreUnico] = useState('');
  const [proc, setProc] = useState(false);
  const [err, setErr] = useState<string | null>(null);

  function setN(n: number) {
    const v = Math.max(1, Math.min(20, n));
    setComensales(v);
    setNombres((prev) => Array.from({ length: v }, (_, i) => prev[i] ?? `Persona ${i + 1}`));
  }

  async function abrir() {
    setErr(null);
    setProc(true);
    const nom = nombres.map((n) => n.trim()).filter(Boolean);
    const nombreUnicoLimpio = nombreUnico.trim();
    const { error } = await supabase.rpc('abrir_mesa', {
      p_mesa: mesa,
      p_comensales: comensales,
      p_modo: modo,
      p_nombres: modo === 'separada' ? nom : nombreUnicoLimpio ? [nombreUnicoLimpio] : undefined,
    });
    setProc(false);
    if (error) {
      setErr(error.message);
      return;
    }
    onAbierta(mesa);
  }

  return (
    <div className="fixed inset-0 z-[70] flex items-end justify-center bg-black/70 sm:items-center" onClick={onCerrar}>
      <div
        className="flex max-h-[92vh] w-full max-w-md flex-col overflow-hidden rounded-t-2xl bg-superficie text-white ring-1 ring-white/15 sm:rounded-2xl"
        onClick={(e) => e.stopPropagation()}
      >
        <div className="flex items-center justify-between border-b border-white/10 p-4">
          <h3 className="text-lg font-extrabold">Abrir Mesa {mesa}</h3>
          <button onClick={onCerrar} className="grid h-9 w-9 place-items-center rounded-full bg-white/10 text-xl leading-none text-white/80 hover:text-white">
            &times;
          </button>
        </div>

        <div className="min-h-0 flex-1 overflow-y-auto p-4">
          {/* Cuantas personas */}
          <p className="mb-1.5 text-sm font-bold text-white/85">¿Cuántas personas?</p>
          <div className="flex items-center gap-3">
            <button onClick={() => setN(comensales - 1)} disabled={comensales <= 1} className="grid h-11 w-11 place-items-center rounded-full bg-white/10 text-2xl text-white/90 disabled:opacity-40">
              −
            </button>
            <span className="w-8 text-center text-2xl font-black tabular-nums">{comensales}</span>
            <button onClick={() => setN(comensales + 1)} className="grid h-11 w-11 place-items-center rounded-full text-2xl" style={{ background: marca, color: txtMarca }}>
              +
            </button>
          </div>

          {/* Modo de cuenta */}
          <p className="mb-1.5 mt-5 text-sm font-bold text-white/85">La cuenta</p>
          <div className="grid grid-cols-2 gap-2">
            {(['unica', 'separada'] as const).map((m) => (
              <button
                key={m}
                onClick={() => setModo(m)}
                className="rounded-xl py-2.5 text-sm font-bold ring-1 transition"
                style={
                  modo === m
                    ? { background: marca, color: txtMarca, ['--tw-ring-color' as string]: 'transparent' }
                    : { color: 'rgba(255,255,255,.85)', ['--tw-ring-color' as string]: 'rgba(255,255,255,.2)' }
                }
              >
                {m === 'unica' ? 'Todos juntos' : 'Cada quien lo suyo'}
              </button>
            ))}
          </div>

          {/* Nombre de referencia (todos juntos: uno solo) o por persona (cada quien lo suyo) */}
          {modo === 'unica' ? (
            <div className="mt-4">
              <p className="mb-1.5 text-xs font-semibold text-white/60">Nombre de referencia (opcional, para saber a nombre de quién quedó la mesa).</p>
              <input
                value={nombreUnico}
                onChange={(e) => setNombreUnico(e.target.value)}
                className="w-full rounded-xl border border-white/15 bg-superficie-2 px-3 py-2.5 text-sm font-medium outline-none placeholder:text-white/45 focus:border-white/35"
                placeholder="Ej. Familia Rodríguez"
              />
            </div>
          ) : (
            <div className="mt-4 space-y-2">
              <p className="text-xs font-semibold text-white/60">Ponle nombre a cada quien (opcional, para saber qué pidió cada uno).</p>
              {nombres.map((n, i) => (
                <input
                  key={i}
                  value={n}
                  onChange={(e) => setNombres((prev) => prev.map((x, k) => (k === i ? e.target.value : x)))}
                  className="w-full rounded-xl border border-white/15 bg-superficie-2 px-3 py-2.5 text-sm font-medium outline-none placeholder:text-white/45 focus:border-white/35"
                  placeholder={`Persona ${i + 1}`}
                />
              ))}
            </div>
          )}
        </div>

        <div className="border-t border-white/10 p-4">
          {err && <p className="mb-2 rounded-lg bg-red-500/15 px-3 py-2 text-[13px] font-bold text-red-200 ring-1 ring-red-500/40">{err}</p>}
          <button
            onClick={abrir}
            disabled={proc}
            className="w-full rounded-xl py-3.5 text-base font-extrabold shadow-lg transition active:scale-[.99] disabled:opacity-60"
            style={{ background: marca, color: txtMarca }}
          >
            {proc ? 'Abriendo...' : 'Abrir mesa'}
          </button>
          <p className="mt-2 text-center text-xs text-niebla" style={{ color: acento }}>
            La mesa queda a tu nombre.
          </p>
        </div>
      </div>
    </div>
  );
}

// ── Detalle de la mesa: lo que pidió cada persona (los platos), en vivo ──
// Reusa la RPC cuenta_mesa (fase 2): trae los pedidos con sus items y la
// persona (comensal) de cada uno, mas el desglose por persona.
interface DetItem {
  id: string;
  nombre: string;
  cantidad: number;
  precio_usd: number;
  nota: string | null;
  para_llevar?: boolean;
  editable?: boolean;
}
interface DetPedido {
  id: string;
  codigo: string;
  estado: string;
  total_usd: number;
  cliente: string | null;
  pagado: boolean;
  para_llevar: boolean;
  comensal_id: string | null;
  comensal: string | null;
  items: DetItem[];
}
interface DetCuenta {
  mesa: string;
  modo_cuenta: string;
  total_usd: number;
  pedidos: DetPedido[];
}

function MesaDetalle({
  cuenta,
  acento,
  tasa,
  onCerrar,
}: {
  cuenta: { id: string; mesa: string; total_usd: number };
  acento: string;
  tasa: number | null;
  onCerrar: () => void;
}) {
  const qc = useQueryClient();
  const { data, isLoading } = useQuery({
    queryKey: ['mesa-detalle', cuenta.id],
    queryFn: async () => {
      const { data, error } = await supabase.rpc('cuenta_mesa', { p_cuenta_id: cuenta.id });
      if (error) throw error;
      return ((data as unknown as DetCuenta[]) ?? [])[0] ?? null;
    },
    refetchInterval: 6000,
  });

  async function quitar(itemId: string) {
    if (!confirm('¿Quitar este renglón del pedido? Se recalcula el total.')) return;
    const { error } = await supabase.rpc('quitar_item_pedido', { p_item_id: itemId });
    if (error) {
      alert(error.message);
      return;
    }
    qc.invalidateQueries({ queryKey: ['mesa-detalle', cuenta.id] });
    qc.invalidateQueries({ queryKey: ['cuentas-mesa'] });
  }

  const pedidos = (data?.pedidos ?? []).filter((p) => p.estado !== 'cancelado');
  // Agrupa por persona: el comensal registrado, o el nombre con que pidió.
  const grupos = new Map<string, { nombre: string; items: DetItem[]; total: number; pagado: boolean }>();
  for (const p of pedidos) {
    const key = (p.comensal || p.cliente || '').trim() || 'La mesa';
    const g = grupos.get(key) ?? { nombre: key, items: [], total: 0, pagado: true };
    // Solo se puede quitar un renglon de un pedido activo y AUN no cobrado
    // (el candado real vive en quitar_item_pedido; esto solo evita el intento).
    const editable = !p.pagado && p.estado !== 'entregado' && p.estado !== 'cancelado';
    for (const it of p.items ?? []) g.items.push({ ...it, para_llevar: p.para_llevar, editable });
    g.total += Number(p.total_usd);
    if (!p.pagado) g.pagado = false;
    grupos.set(key, g);
  }
  const total = data?.total_usd ?? cuenta.total_usd;
  const separada = data?.modo_cuenta === 'separada';

  return (
    <div className="fixed inset-0 z-[70] flex items-end justify-center bg-black/70 sm:items-center" onClick={onCerrar}>
      <div
        className="flex max-h-[92vh] w-full max-w-lg flex-col overflow-hidden rounded-t-2xl bg-superficie text-white ring-1 ring-white/15 sm:rounded-2xl"
        onClick={(e) => e.stopPropagation()}
      >
        <div className="flex items-center justify-between border-b border-white/10 p-4">
          <div>
            <h3 className="text-lg font-extrabold">Mesa {cuenta.mesa} · lo que pidió</h3>
            <p className="text-[13px] font-medium text-white/65">
              {separada ? 'Cuenta separada por persona' : 'Cuenta de la mesa'} · Total{' '}
              <b className="text-white">{formatUsd(total)}</b>
              {tasa && tasa > 0 ? ` · ${formatBs(bsDesdeUsd(total, tasa))}` : ''}
            </p>
          </div>
          <button
            onClick={onCerrar}
            className="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-white/10 text-xl leading-none text-white/80 hover:text-white"
          >
            &times;
          </button>
        </div>

        <div className="min-h-0 flex-1 overflow-y-auto p-4">
          {isLoading ? (
            <p className="py-6 text-center text-sm text-niebla">Cargando...</p>
          ) : pedidos.length === 0 ? (
            <p className="py-6 text-center text-sm text-niebla">Esta mesa todavía no ha pedido nada.</p>
          ) : (
            <div className="space-y-4">
              {[...grupos.values()].map((g) => (
                <div key={g.nombre} className="rounded-xl bg-superficie-2 p-3 ring-1 ring-white/10">
                  <div className="mb-2 flex items-center justify-between">
                    <span className="text-[15px] font-extrabold">{g.nombre}</span>
                    <span className="flex items-center gap-2 text-sm font-bold tabular-nums" style={{ color: acento }}>
                      {formatUsd(g.total)}
                      {g.pagado && (
                        <span className="rounded-full bg-green-500/15 px-2 py-0.5 text-[11px] font-bold text-green-300">Pagado</span>
                      )}
                    </span>
                  </div>
                  <ul className="space-y-1.5">
                    {g.items.map((it) => (
                      <li key={it.id} className="flex items-start justify-between gap-2 text-sm">
                        <span className="min-w-0">
                          <span className="font-semibold text-white">{it.cantidad}× {it.nombre}</span>
                          {it.para_llevar && (
                            <span className="ml-1.5 rounded-full bg-amber-400/20 px-1.5 py-0.5 align-middle text-[10px] font-black text-amber-300">LLEVAR</span>
                          )}
                          {it.nota && <span className="mt-0.5 block text-xs text-white/60">{it.nota}</span>}
                        </span>
                        <span className="flex shrink-0 items-center gap-2">
                          <span className="font-bold tabular-nums text-white/85">
                            {formatUsd(Number(it.precio_usd) * Number(it.cantidad))}
                          </span>
                          {it.editable && (
                            <button
                              onClick={() => quitar(it.id)}
                              className="text-red-400/70 hover:text-red-400"
                              title="Quitar este renglón"
                              aria-label="Quitar renglón"
                            >
                              <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                                <path d="M3 6h18M8 6V4h8v2M6 6l1 14h10l1-14M10 11v6M14 11v6" />
                              </svg>
                            </button>
                          )}
                        </span>
                      </li>
                    ))}
                  </ul>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ── Historial de mesas cerradas: el registro de lo cobrado y liberado ──
function fechaHora(iso: string): string {
  const d = new Date(iso);
  return d.toLocaleString('es-VE', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit', hour12: true });
}

function HistorialMesas({
  historial,
  cargando,
  dias,
  onDias,
  acento,
  onDetalle,
}: {
  historial: HistorialFila[];
  cargando: boolean;
  dias: number;
  onDias: (d: number) => void;
  acento: string;
  onDetalle: (h: HistorialFila) => void;
}) {
  const totalPeriodo = historial.reduce((s, h) => s + Number(h.total_usd), 0);
  const opts: [number, string][] = [
    [1, 'Hoy'],
    [7, '7 días'],
    [30, '30 días'],
  ];
  return (
    <div className="space-y-3">
      <div className="flex flex-wrap items-center justify-between gap-2">
        <div className="flex gap-1.5">
          {opts.map(([d, label]) => (
            <button
              key={d}
              onClick={() => onDias(d)}
              className="rounded-full px-3 py-1.5 text-xs font-bold ring-1 transition"
              style={
                dias === d
                  ? { background: acento + '26', color: acento, ['--tw-ring-color' as string]: 'transparent' }
                  : { color: 'rgba(255,255,255,.8)', ['--tw-ring-color' as string]: 'rgba(255,255,255,.15)' }
              }
            >
              {label}
            </button>
          ))}
        </div>
        <p className="text-sm font-bold text-white/80">
          {historial.length} mesa{historial.length === 1 ? '' : 's'} ·{' '}
          <span style={{ color: acento }}>{formatUsd(totalPeriodo)}</span>
        </p>
      </div>

      {cargando ? (
        <p className="py-8 text-center text-sm text-niebla">Cargando...</p>
      ) : historial.length === 0 ? (
        <div className="rounded-2xl border border-dashed border-white/15 bg-superficie py-12 text-center">
          <Icono nombre="recibo" className="mx-auto h-10 w-10 text-white/30" />
          <p className="mt-2 font-bold text-white">Sin mesas cerradas</p>
          <p className="mt-1 text-sm text-niebla">Cuando cobres y liberes una mesa, queda aquí su registro.</p>
        </div>
      ) : (
        <div className="space-y-2">
          {historial.map((h) => (
            <div key={h.id} className="flex items-center justify-between gap-3 rounded-2xl bg-superficie p-3 ring-1 ring-white/10">
              <div className="min-w-0">
                <p className="font-extrabold text-white">Mesa {h.mesa}</p>
                <p className="truncate text-xs text-niebla">
                  {fechaHora(h.cerrada_en)} · {h.n_pedidos} pedido{h.n_pedidos === 1 ? '' : 's'}
                  {h.mesero_nombre ? ` · ${h.mesero_nombre}` : ''}
                  {h.metodo_pago ? ` · ${METODO[h.metodo_pago] ?? h.metodo_pago}` : ''}
                </p>
              </div>
              <div className="shrink-0 text-right">
                <p className="font-extrabold" style={{ color: acento }}>{formatUsd(h.total_usd)}</p>
                <button onClick={() => onDetalle(h)} className="text-xs font-bold text-white/70 underline">
                  Ver detalle
                </button>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ── Cobro por partes: la cuenta de la mesa dividida por persona ──
// Cada cobro queda como fila en pagos_mesa (quien cobro, metodo, referencia)
// y los pedidos cobrados no se pueden volver a cobrar (candado en BD).
function CobroPartes({
  cuenta,
  marca,
  txtMarca,
  acento,
  esStaff,
  tasa,
  restauranteId,
  onCerrar,
}: {
  cuenta: CuentaFila;
  marca: string;
  txtMarca: string;
  acento: string;
  esStaff: boolean;
  tasa: number | null;
  restauranteId: string;
  onCerrar: () => void;
}) {
  const qc = useQueryClient();
  const [sel, setSel] = useState<string[]>([]);
  const [metodo, setMetodo] = useState('efectivo');
  const [referencia, setReferencia] = useState('');
  const [comprobante, setComprobante] = useState<File | null>(null);
  const [proc, setProc] = useState(false);
  const [err, setErr] = useState<string | null>(null);
  const [msg, setMsg] = useState<string | null>(null);
  const [fiscal, setFiscal] = useState<DatosFiscales>(SIN_DATOS_FISCALES);
  const facturaActiva = useFacturaActiva(restauranteId);
  // Marcar perdida (dueño/encargado): condona lo seleccionado, pide motivo obligatorio.
  const [perdidaAbierta, setPerdidaAbierta] = useState(false);
  const [motivoPerdida, setMotivoPerdida] = useState('');
  const [procPerdida, setProcPerdida] = useState(false);

  const pedidosQ = useQuery({
    queryKey: ['partes-pedidos', cuenta.id],
    queryFn: async () => {
      const { data, error } = await supabase
        .from('pedidos')
        .select('id,codigo,cliente_nombre,total_usd,estado,pago_mesa_id')
        .eq('cuenta_id', cuenta.id)
        .order('recibido_en');
      if (error) throw error;
      return (data as unknown as PedidoParte[]) ?? [];
    },
  });
  const pagosQ = useQuery({
    queryKey: ['partes-pagos', cuenta.id],
    queryFn: async () => {
      const { data, error } = await supabase
        .from('pagos_mesa')
        .select('id,monto_usd,metodo,referencia,nota,mesero_nombre,creado_en,verificado,comprobante_url,comensal_id')
        .eq('cuenta_id', cuenta.id)
        .order('creado_en');
      if (error) throw error;
      return (data as unknown as PagoParte[]) ?? [];
    },
  });
  const comensalesQ = useQuery({
    queryKey: ['partes-comensales', cuenta.id],
    queryFn: async () => {
      const { data } = await supabase.from('comensales_mesa').select('id,nombre').eq('cuenta_id', cuenta.id);
      return (data as { id: string; nombre: string }[]) ?? [];
    },
  });
  const comensalNombre = (id: string | null) => (comensalesQ.data ?? []).find((c) => c.id === id)?.nombre ?? null;

  const pedidos = (pedidosQ.data ?? []).filter((p) => p.estado !== 'cancelado');
  const pagos = pagosQ.data ?? [];
  const total = pedidos.reduce((s, p) => s + Number(p.total_usd), 0);
  // "Pagado" = solo cobros verificados. Los pagos en linea del cliente entran
  // "por revisar" hasta que el mesero los confirma.
  const pagado = pagos.filter((p) => p.verificado).reduce((s, p) => s + Number(p.monto_usd), 0);
  const porRevisar = pagos.filter((p) => !p.verificado).reduce((s, p) => s + Number(p.monto_usd), 0);
  const falta = Math.max(0, total - pagado);
  const pagoById = new Map(pagos.map((p) => [p.id, p]));

  // Desglose por persona (el nombre que dio al pedir por QR o el que puso el mesero)
  const grupos = new Map<string, PedidoParte[]>();
  for (const p of pedidos) {
    const k = (p.cliente_nombre || '').trim() || 'Sin nombre';
    grupos.set(k, [...(grupos.get(k) ?? []), p]);
  }

  const seleccion = pedidos.filter((p) => sel.includes(p.id));
  const montoSel = seleccion.reduce((s, p) => s + Number(p.total_usd), 0);
  const online = necesitaReferencia(metodo);
  const avisoRef = faltaReferencia(metodo, referencia);
  const refFalta = !!avisoRef;

  function toggle(id: string) {
    setMsg(null);
    setErr(null);
    setSel((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]));
  }
  function toggleGrupo(items: PedidoParte[]) {
    setMsg(null);
    setErr(null);
    const cobrables = items.filter((p) => !p.pago_mesa_id).map((p) => p.id);
    const todos = cobrables.length > 0 && cobrables.every((id) => sel.includes(id));
    setSel((prev) => (todos ? prev.filter((id) => !cobrables.includes(id)) : [...new Set([...prev, ...cobrables])]));
  }

  async function cobrarSel() {
    if (!sel.length || refFalta) return;
    setErr(null);
    setMsg(null);
    setProc(true);
    // Igual que en el cobro completo: los datos del comprador van antes, porque
    // el ultimo pago cierra la cuenta y la manda a sellar.
    if (facturaActiva && hayDatosFiscales(fiscal)) {
      const problema = await guardarDatosFiscales('cuenta', cuenta.id, fiscal);
      if (problema) {
        setProc(false);
        setErr(problema);
        return;
      }
    }
    // Foto del comprobante (opcional): si falla la subida no se bloquea el
    // cobro, solo se avisa, mismo patron que el POS (PuntoDeVenta.tsx).
    let comprobante_url: string | undefined;
    let avisoFoto = '';
    if (online && comprobante) {
      try {
        comprobante_url = await subirComprobante(comprobante, cuenta.id);
      } catch {
        avisoFoto = ' Ojo: no se pudo subir la foto del comprobante.';
      }
    }
    const nombres = [...new Set(seleccion.map((p) => (p.cliente_nombre || '').trim() || 'Sin nombre'))].join(', ');
    const { data, error } = await supabase.rpc('cobrar_parte_mesa', {
      p_cuenta_id: cuenta.id,
      p_pedido_ids: sel,
      p_metodo: metodo,
      p_referencia: online ? referencia.trim() || undefined : undefined,
      p_nota: nombres || undefined,
      p_comprobante_url: comprobante_url,
    });
    setProc(false);
    if (error) {
      setErr(error.message);
      pedidosQ.refetch();
      return;
    }
    const fila = (data as unknown as { monto_usd: number; saldo_usd: number }[])?.[0];
    setMsg(`Cobrado ${formatUsd(Number(fila?.monto_usd ?? montoSel))}. ${Number(fila?.saldo_usd) > 0 ? `Falta ${formatUsd(Number(fila.saldo_usd))}.` : 'Cuenta completa: puedes liberar la mesa.'}${avisoFoto}`);
    setSel([]);
    setReferencia('');
    setComprobante(null);
    pedidosQ.refetch();
    pagosQ.refetch();
    qc.invalidateQueries({ queryKey: ['cuentas-mesa', restauranteId] });
  }

  async function confirmarPerdida() {
    if (!sel.length || !motivoPerdida.trim()) return;
    setErr(null);
    setMsg(null);
    setProcPerdida(true);
    const { data, error } = await supabase.rpc('marcar_perdida_mesa', {
      p_cuenta_id: cuenta.id,
      p_pedido_ids: sel,
      p_motivo: motivoPerdida.trim(),
    });
    setProcPerdida(false);
    if (error) {
      setErr(error.message);
      pedidosQ.refetch();
      return;
    }
    const fila = (data as unknown as { monto_usd: number; saldo_usd: number }[])?.[0];
    setMsg(`Marcado como pérdida ${formatUsd(Number(fila?.monto_usd ?? montoSel))}. ${Number(fila?.saldo_usd) > 0 ? `Falta ${formatUsd(Number(fila.saldo_usd))}.` : 'Cuenta completa: puedes liberar la mesa.'}`);
    setSel([]);
    setMotivoPerdida('');
    setPerdidaAbierta(false);
    pedidosQ.refetch();
    pagosQ.refetch();
    qc.invalidateQueries({ queryKey: ['cuentas-mesa', restauranteId] });
  }

  async function anular(p: PagoParte) {
    if (!confirm(`Anular el cobro de ${formatUsd(Number(p.monto_usd))}${p.nota ? ` (${p.nota})` : ''}? Los pedidos vuelven a quedar por cobrar.`)) return;
    setErr(null);
    const { error } = await supabase.from('pagos_mesa').delete().eq('id', p.id);
    if (error) {
      setErr(error.message);
      return;
    }
    pedidosQ.refetch();
    pagosQ.refetch();
    qc.invalidateQueries({ queryKey: ['cuentas-mesa', restauranteId] });
  }

  async function verificarPago(p: PagoParte, ok: boolean) {
    if (
      !ok &&
      !confirm(`Rechazar el pago en línea de ${formatUsd(Number(p.monto_usd))}? Los pedidos vuelven a quedar por cobrar.`)
    )
      return;
    setErr(null);
    const { error } = await supabase.rpc('verificar_pago_cliente', { p_pago_id: p.id, p_ok: ok });
    if (error) {
      setErr(error.message);
      return;
    }
    setMsg(ok ? 'Pago confirmado.' : 'Pago rechazado.');
    pedidosQ.refetch();
    pagosQ.refetch();
    qc.invalidateQueries({ queryKey: ['cuentas-mesa', restauranteId] });
  }

  async function verComprobante(path: string) {
    const { data, error } = await supabase.storage.from('comprobantes').createSignedUrl(path, 120);
    if (error) alert(error.message);
    else window.open(data.signedUrl, '_blank');
  }

  return (
    <div className="fixed inset-0 z-[70] flex items-end justify-center bg-black/70 sm:items-center" onClick={onCerrar}>
      <div
        className="flex max-h-[92vh] w-full max-w-lg flex-col overflow-hidden rounded-t-2xl bg-superficie text-white ring-1 ring-white/15 sm:rounded-2xl"
        onClick={(e) => e.stopPropagation()}
      >
        <div className="flex items-center justify-between border-b border-white/10 p-4">
          <div>
            <h3 className="text-lg font-extrabold">Cobrar por partes · Mesa {cuenta.mesa}</h3>
            <p className="text-[13px] font-medium text-white/65">
              Total {formatUsd(total)} · Pagado {formatUsd(pagado)} · <b className="text-white">Falta {formatUsd(falta)}</b>
              {porRevisar > 0 && <span className="text-amber-300"> · Por revisar {formatUsd(porRevisar)}</span>}
            </p>
          </div>
          <button onClick={onCerrar} className="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-white/10 text-xl leading-none text-white/80 hover:text-white">&times;</button>
        </div>

        <div className="min-h-0 flex-1 overflow-y-auto p-4">
          {pedidos.length === 0 ? (
            <p className="py-6 text-center text-sm text-niebla">Esta cuenta no tiene pedidos.</p>
          ) : (
            <div className="space-y-4">
              {[...grupos.entries()].map(([nombre, items]) => {
                const subtotal = items.reduce((s, p) => s + Number(p.total_usd), 0);
                const pendientes = items.filter((p) => !p.pago_mesa_id);
                const enRevisionGrupo = items.some((p) => p.pago_mesa_id && !pagoById.get(p.pago_mesa_id)?.verificado);
                return (
                  <div key={nombre} className="rounded-xl bg-superficie-2 p-3 ring-1 ring-white/10">
                    <button className="flex w-full items-center justify-between" onClick={() => toggleGrupo(items)}>
                      <span className="text-[15px] font-extrabold">{nombre}</span>
                      <span className="text-sm font-bold tabular-nums" style={{ color: acento }}>
                        {formatUsd(subtotal)}
                        {pendientes.length === 0 &&
                          (enRevisionGrupo ? (
                            <span className="ml-2 rounded-full bg-amber-500/15 px-2 py-0.5 text-[11px] font-bold text-amber-300">Por revisar</span>
                          ) : (
                            <span className="ml-2 rounded-full bg-green-500/15 px-2 py-0.5 text-[11px] font-bold text-green-300">Pagado</span>
                          ))}
                      </span>
                    </button>
                    <div className="mt-2 space-y-1.5">
                      {items.map((p) => {
                        const cobrado = !!p.pago_mesa_id;
                        const enRevision = !!(p.pago_mesa_id && !pagoById.get(p.pago_mesa_id)?.verificado);
                        const on = sel.includes(p.id);
                        return (
                          <button
                            key={p.id}
                            disabled={cobrado}
                            onClick={() => toggle(p.id)}
                            className="flex w-full items-center justify-between rounded-lg px-2.5 py-2 text-left text-sm ring-1 ring-white/10 disabled:opacity-50"
                            style={on ? { boxShadow: `inset 0 0 0 2px ${acento}` } : undefined}
                          >
                            <span className="flex items-center gap-2">
                              <span
                                className="grid h-5 w-5 place-items-center rounded-md border-2 text-xs font-black"
                                style={on ? { background: marca, borderColor: marca, color: txtMarca } : { borderColor: 'rgba(255,255,255,.3)', color: 'transparent' }}
                              >
                                ✓
                              </span>
                              <span className="font-semibold">{p.codigo}</span>
                              {cobrado &&
                                (enRevision ? (
                                  <span className="rounded-full bg-amber-500/15 px-2 py-0.5 text-[11px] font-bold text-amber-300">Por revisar</span>
                                ) : (
                                  <span className="rounded-full bg-green-500/15 px-2 py-0.5 text-[11px] font-bold text-green-300">Pagado</span>
                                ))}
                            </span>
                            <span className="font-bold tabular-nums">{formatUsd(Number(p.total_usd))}</span>
                          </button>
                        );
                      })}
                    </div>
                  </div>
                );
              })}

              {pagos.some((p) => !p.verificado) && (
                <div>
                  <p className="mb-1.5 text-xs font-bold uppercase tracking-wide text-amber-300">Pagos en línea por revisar</p>
                  <div className="space-y-1.5">
                    {pagos
                      .filter((p) => !p.verificado)
                      .map((p) => (
                        <div key={p.id} className="rounded-lg bg-amber-500/10 p-2.5 text-sm ring-1 ring-amber-500/30">
                          <p className="text-white/90">
                            <b>{formatUsd(Number(p.monto_usd))}</b> · {METODO[p.metodo] ?? p.metodo}
                            {comensalNombre(p.comensal_id) ? ` · ${comensalNombre(p.comensal_id)}` : ''}
                            {p.referencia ? ` · Ref ${p.referencia}` : ''}
                          </p>
                          <div className="mt-1.5 flex flex-wrap items-center gap-2">
                            {p.comprobante_url && (
                              <button onClick={() => verComprobante(p.comprobante_url!)} className="text-xs font-bold underline" style={{ color: acento }}>
                                Ver comprobante
                              </button>
                            )}
                            {esStaff ? (
                              <span className="ml-auto text-xs font-semibold text-white/55">El panel lo confirma</span>
                            ) : (
                              <>
                                <button onClick={() => verificarPago(p, true)} className="ml-auto rounded-lg bg-green-600 px-3 py-1.5 text-xs font-bold text-white">
                                  Confirmar
                                </button>
                                <button onClick={() => verificarPago(p, false)} className="rounded-lg border border-white/20 px-3 py-1.5 text-xs font-bold text-white/85">
                                  Rechazar
                                </button>
                              </>
                            )}
                          </div>
                        </div>
                      ))}
                  </div>
                </div>
              )}

              {pagos.some((p) => p.verificado) && (
                <div>
                  <p className="mb-1.5 text-xs font-bold uppercase tracking-wide text-niebla">Cobros registrados</p>
                  <div className="space-y-1.5">
                    {pagos
                      .filter((p) => p.verificado)
                      .map((p) => {
                        const esPerdida = p.metodo === 'perdida';
                        return (
                          <div
                            key={p.id}
                            className={`flex items-center justify-between rounded-lg px-2.5 py-2 text-sm ring-1 ${
                              esPerdida ? 'bg-red-500/10 ring-red-500/30' : 'bg-white/5 ring-white/10'
                            }`}
                          >
                            <span className="min-w-0 truncate text-white/85">
                              <b className={esPerdida ? 'text-red-400' : undefined}>{formatUsd(Number(p.monto_usd))}</b> ·{' '}
                              <span className={esPerdida ? 'font-bold text-red-400' : undefined}>{METODO[p.metodo] ?? p.metodo}</span>
                              {p.nota ? ` · ${p.nota}` : comensalNombre(p.comensal_id) ? ` · ${comensalNombre(p.comensal_id)}` : ''}
                              {p.mesero_nombre ? ` · ${esPerdida ? 'marcó' : 'cobró'} ${p.mesero_nombre}` : ''}
                              {p.referencia ? ` · Ref ${p.referencia}` : ''}
                            </span>
                            {!esStaff && (
                              <button onClick={() => anular(p)} className="ml-2 shrink-0 text-xs font-bold text-red-400 underline">
                                Anular
                              </button>
                            )}
                          </div>
                        );
                      })}
                  </div>
                </div>
              )}
            </div>
          )}
        </div>

        <div className="border-t border-white/10 p-4">
          {err && <p className="mb-2 rounded-lg bg-red-500/15 px-3 py-2 text-[13px] font-bold text-red-200 ring-1 ring-red-500/40">{err}</p>}
          {msg && <p className="mb-2 rounded-lg bg-green-500/15 px-3 py-2 text-[13px] font-bold text-green-200 ring-1 ring-green-500/40">{msg}</p>}
          <select
            value={metodo}
            onChange={(e) => setMetodo(e.target.value)}
            className="mb-2 w-full rounded-xl border border-white/15 bg-superficie-2 px-3 py-2.5 text-sm font-bold text-white outline-none focus:border-white/35"
          >
            {METODOS_COBRO.map(([v, label]) => (
              <option key={v} value={v}>
                {label}
              </option>
            ))}
          </select>
          {online && (
            <input
              className={`mb-2 w-full rounded-xl border bg-superficie-2 px-3 py-2.5 text-sm font-medium outline-none placeholder:text-white/45 focus:border-white/35 ${
                avisoRef ? 'border-red-500/60' : 'border-white/15'
              }`}
              placeholder={etiquetaReferencia(metodo)}
              inputMode="numeric"
              value={referencia}
              onChange={(e) => setReferencia(e.target.value)}
            />
          )}
          {avisoRef && <p className="mb-2 text-[12px] font-semibold text-red-400">{avisoRef}</p>}
          {online && (
            <label className="mb-2 flex cursor-pointer items-center justify-between gap-2 rounded-xl border border-dashed border-white/20 bg-superficie-2 px-3 py-2.5 text-sm font-medium text-white/70">
              <span className="truncate">{comprobante ? comprobante.name : 'Foto del comprobante (opcional)'}</span>
              <span className="shrink-0 rounded-lg px-2.5 py-1 text-xs font-bold" style={{ background: marca, color: txtMarca }}>
                {comprobante ? 'Cambiar' : 'Tomar foto'}
              </span>
              <input
                type="file"
                accept="image/*"
                capture="environment"
                className="hidden"
                onChange={(e) => setComprobante(e.target.files?.[0] ?? null)}
              />
            </label>
          )}
          {facturaActiva && (
            <div className="mb-2">
              <DatosFactura valor={fiscal} onCambio={setFiscal} />
            </div>
          )}
          <button
            onClick={cobrarSel}
            disabled={proc || !sel.length || refFalta}
            className="flex w-full items-center justify-between rounded-xl px-5 py-3 text-base font-extrabold shadow-lg transition active:scale-[.99] disabled:opacity-50"
            style={{ background: marca, color: txtMarca }}
          >
            <span>{proc ? 'Cobrando…' : `Cobrar ${sel.length} pedido${sel.length === 1 ? '' : 's'}`}</span>
            <span className="tabular-nums">
              {formatUsd(montoSel)}
              {tasa && tasa > 0 && montoSel > 0 ? ` · ${formatBs(bsDesdeUsd(montoSel, tasa))}` : ''}
            </span>
          </button>
          {!esStaff && (
            <button
              onClick={() => setPerdidaAbierta(true)}
              disabled={!sel.length}
              className="mt-2 w-full rounded-xl border border-red-500/30 py-2.5 text-sm font-bold text-red-400 transition disabled:opacity-40"
            >
              Marcar {sel.length ? `${sel.length} pedido${sel.length === 1 ? '' : 's'} (${formatUsd(montoSel)})` : ''} como pérdida
            </button>
          )}
        </div>
      </div>

      {perdidaAbierta && (
        <div className="fixed inset-0 z-[80] flex items-center justify-center bg-black/70 p-4" onClick={() => !procPerdida && setPerdidaAbierta(false)}>
          <div className="w-full max-w-sm rounded-2xl bg-superficie p-5 ring-1 ring-red-500/30" onClick={(e) => e.stopPropagation()}>
            <h3 className="text-lg font-extrabold text-red-400">Marcar como pérdida</h3>
            <p className="mt-1.5 text-sm text-white/70">
              Vas a dar por perdidos <b className="text-white">{formatUsd(montoSel)}</b> de {sel.length} pedido
              {sel.length === 1 ? '' : 's'}. Esto NO es un cobro: queda registrado como pérdida, no como venta.
            </p>
            <label className="mt-3 block text-xs font-bold text-white/60">Motivo (obligatorio)</label>
            <textarea
              value={motivoPerdida}
              onChange={(e) => setMotivoPerdida(e.target.value)}
              placeholder="Ej: el cliente se fue sin pagar su parte"
              rows={3}
              className="mt-1 w-full rounded-xl border border-white/15 bg-superficie-2 px-3 py-2.5 text-sm text-white outline-none placeholder:text-white/40 focus:border-red-400/50"
            />
            <div className="mt-4 flex gap-2">
              <button
                onClick={() => setPerdidaAbierta(false)}
                disabled={procPerdida}
                className="flex-1 rounded-xl border border-white/15 py-2.5 text-sm font-bold text-white/80"
              >
                Cancelar
              </button>
              <button
                onClick={confirmarPerdida}
                disabled={procPerdida || !motivoPerdida.trim()}
                className="flex-1 rounded-xl bg-red-500 py-2.5 text-sm font-bold text-white disabled:opacity-50"
              >
                {procPerdida ? 'Marcando…' : 'Confirmar pérdida'}
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
