import { NextResponse } from "next/server";

import { createAdminClient } from "@/lib/supabase-admin";

import { ErroDoSfu } from "../sfu.ts";
import { comAvisos } from "../sinal.ts";
import { ErroDaLive, exigirParticipante, exigirSala, type Admin, type Participante, type SalaDoBanco } from "./sala.ts";

const SEM_CACHE = { "cache-control": "no-store" };

export function respostaDaLive(corpo: unknown, status = 200) {
  return NextResponse.json(corpo, { status, headers: SEM_CACHE });
}

function traduzir(erro: unknown): Response {
  if (erro instanceof ErroDaLive) return respostaDaLive({ error: erro.message, ...(erro.extra ?? {}) }, erro.status);
  if (erro instanceof ErroDoSfu) {
    console.error("[live] sfu", erro.message);
    /* Sessão vencida ou caída na Cloudflare: a tela abre uma conexão nova. */
    /* 406: a negociação anterior ficou pela metade; também só se resolve com conexão nova. */
    /* 504: a Cloudflare não respondeu a tempo; a negociação pode ter ficado pela metade. */
    if (erro.status === 410 || erro.status === 404 || erro.status === 406 || erro.status === 504) return respostaDaLive({ error: "Reconectando o vídeo…", motivo: "reconectar" }, 409);
    return respostaDaLive({ error: "A transmissão engasgou. Tente de novo em instantes.", motivo: "sfu" }, 502);
  }
  console.error("[live] erro inesperado", erro);
  return respostaDaLive({ error: "Algo deu errado do nosso lado. Tente de novo em instantes." }, 500);
}

export async function lerCorpo(req: Request): Promise<Record<string, unknown>> {
  const corpo = await req.json().catch(() => null);
  return corpo && typeof corpo === "object" && !Array.isArray(corpo) ? (corpo as Record<string, unknown>) : {};
}

/* A rota que só precisa da sala (entrar). */
export async function rotaDaSala(salaId: string, executar: (admin: Admin, sala: SalaDoBanco) => Promise<unknown>): Promise<Response> {
  try {
    const admin = createAdminClient();
    const sala = await exigirSala(admin, salaId);
    const resultado = await executar(admin, sala);
    return resultado instanceof Response ? resultado : respostaDaLive(resultado ?? { ok: true });
  } catch (erro) {
    return traduzir(erro);
  }
}

/* A rota de quem já está na sala: confere a credencial antes de tudo. */
export async function rotaDeQuemEsta(
  req: Request,
  salaId: string,
  executar: (contexto: { admin: Admin; sala: SalaDoBanco; eu: Participante }) => Promise<unknown>,
): Promise<Response> {
  try {
    const admin = createAdminClient();
    /* A sala e quem pede saem do banco ao mesmo tempo (o banco fica longe). */
    const [sala, eu] = await Promise.all([exigirSala(admin, salaId), exigirParticipante(admin, { id: salaId }, req)]);
    const { resultado, avisos } = await comAvisos(() => executar({ admin, sala, eu }));
    if (resultado instanceof Response) return resultado;
    const corpo = (resultado ?? { ok: true }) as Record<string, unknown>;
    /* Os avisos assinados vão junto: o navegador repassa pelo canal na hora. */
    return respostaDaLive(avisos.length && corpo && typeof corpo === "object" && !Array.isArray(corpo) ? { ...corpo, avisos } : corpo);
  } catch (erro) {
    return traduzir(erro);
  }
}
