import { getSupabaseClient } from "@/lib/supabase";

/*
  A ponte entre o balcão e a tela virada para o cliente.

  A tela do cliente é outra janela — no segundo monitor, ou num tablet em cima
  do balcão. Ela não decide nada: só mostra o que o PDV manda. Duas estradas
  levam a mensagem até lá:

  - `BroadcastChannel`, quando as duas janelas estão no mesmo navegador: chega
    na hora, sem rede;
  - o realtime do Supabase, para o tablet ou qualquer outro aparelho.

  As duas mandam a mesma mensagem, e a tela fica com a que chegar primeiro: o
  número de sequência descarta a repetida.

  Pode haver mais de um balcão falando no mesmo canal — outra aba esquecida,
  outra máquina. A tela segue o balcão com **mais coisa acontecendo** (um
  pagamento em andamento vence uma conta aberta, que vence um balcão parado)
  e, entre iguais, o **usado por último** (`ativoEm`, a hora da última ação
  do operador). Só troca se outro passa na frente por essa régua ou se o
  adotado ficou calado por meio minuto. Um PDV aberto e intocado nunca rouba
  a tela de quem está cobrando.
*/

export type FaseDaTela =
  | "ocioso"
  | "conta"
  | "preparando"
  | "gerando"
  | "pagar"
  | "aprovado"
  | "cancelado"
  | "expirado";

export type ItemDaTela = {
  id: string;
  titulo: string;
  quantidade: number;
  medida?: string | null;
  detalhe?: string | null;
  total: number;
};

export type EstadoDaTela = {
  seq: number;
  em: string;
  /* Quem manda e quando foi tocado pela última vez (ms desde a época; 0 = nunca). */
  origem: string;
  ativoEm: number;
  fase: FaseDaTela;
  itens: ItemDaTela[];
  subtotal: number;
  desconto: number;
  total: number;
  cliente?: string | null;
  pix?: {
    valor: number;
    qrCode: string | null;
    qrCodeBase64: string | null;
    expiraEm: string | null;
  } | null;
  venda?: {
    numero: number | null;
    troco: number;
    pago: number;
    /* Produto digital: o QR code do link da entrega, para o cliente apontar o celular. */
    entrega?: { qr: string } | null;
  } | null;
};

export type MensagemDaPonte =
  | { evento: "estado"; estado: EstadoDaTela }
  | { evento: "ola"; origem: string };

/* Quanto tempo sem notícia do balcão adotado até a tela aceitar outro. */
const SILENCIO_MS = 30000;

/* O que a tela não pode deixar de mostrar: um pagamento em andamento vence
   uma conta aberta, que vence um balcão parado — não importa quem foi tocado
   por último. */
function prioridadeDaFase(fase: FaseDaTela | undefined) {
  if (fase === "preparando" || fase === "gerando" || fase === "pagar" || fase === "aprovado") return 2;
  if (fase === "conta") return 1;
  return 0;
}

export const canalDaTela = (tenantId: string) => `pdv-tela-${tenantId}`;

export function abrirPonte(tenantId: string, aoReceber: (mensagem: MensagemDaPonte) => void) {
  const nome = canalDaTela(tenantId);
  let adotado: { origem: string; seq: number; ativoEm: number; prioridade: number; em: number } | null = null;

  const entregar = (bruto: unknown) => {
    const mensagem = bruto as MensagemDaPonte | null;
    if (!mensagem || typeof mensagem !== "object" || !("evento" in mensagem)) return;
    if (mensagem.evento === "estado") {
      const estado = mensagem.estado;
      const seq = Number(estado?.seq);
      const origem = String(estado?.origem || "");
      const ativoEm = Number(estado?.ativoEm) || 0;
      if (!Number.isFinite(seq) || !origem) return;
      const agora = Date.now();
      const prioridade = prioridadeDaFase(estado?.fase);
      if (adotado && adotado.origem === origem) {
        if (seq <= adotado.seq) return;
      } else if (adotado) {
        const calado = agora - adotado.em > SILENCIO_MS;
        const maisImportante = prioridade > adotado.prioridade;
        const maisRecente = prioridade === adotado.prioridade && ativoEm > adotado.ativoEm;
        if (!calado && !maisImportante && !maisRecente) return;
      }
      adotado = { origem, seq, ativoEm, prioridade, em: agora };
    }
    aoReceber(mensagem);
  };

  /* Mesmo navegador: a estrada curta. */
  let local: BroadcastChannel | null = null;
  try {
    if (typeof BroadcastChannel !== "undefined") {
      local = new BroadcastChannel(nome);
      local.onmessage = (evento) => entregar(evento.data);
    }
  } catch {
    local = null;
  }

  /* Outro aparelho: a estrada longa. */
  const supabase = getSupabaseClient();
  const remoto = supabase.channel(nome, { config: { broadcast: { self: false } } });
  remoto.on("broadcast", { event: "pdv" }, (pacote) => entregar(pacote.payload));
  remoto.subscribe();

  return {
    enviar(mensagem: MensagemDaPonte) {
      try {
        local?.postMessage(mensagem);
      } catch {
        /* sem a estrada curta, vai pela longa */
      }
      void remoto.send({ type: "broadcast", event: "pdv", payload: mensagem });
    },
    fechar() {
      try {
        local?.close();
      } catch {
        /* já fechado */
      }
      void supabase.removeChannel(remoto);
    },
  };
}
