import type { TipoDeTrilha } from "@/lib/live/evento";
import { TIPOS_DE_TRILHA } from "@/lib/live/evento";
import { ehDaEquipe } from "@/lib/live/regras";
import { lerCorpo, rotaDeQuemEsta } from "@/lib/live/servidor/rota";
import { ErroDaLive, sessaoDoSfu, pessoasOnline, uuidValido } from "@/lib/live/servidor/sala";
import { configuracaoDoSfu, novaSessao, novasTrilhas } from "@/lib/live/sfu";

export const runtime = "nodejs";

/*
 * Receber o vídeo e o áudio de quem está no palco. A tela diz de quem e o
 * quê; o servidor acha a trilha no SFU (a tela nunca escolhe a sessão de
 * ninguém). Antes de a live começar, quem assiste não recebe quem apresenta.
 * Tudo isso pela conexão de receber, separada da de mandar: uma trilha que
 * demora a chegar nunca trava quem quer ligar a própria câmera.
 */
export async function POST(req: Request, { params }: { params: Promise<{ salaId: string }> }) {
  const { salaId } = await params;
  return rotaDeQuemEsta(req, salaId, async ({ admin, sala, eu }) => {
    const cfg = configuracaoDoSfu();
    if (!cfg) throw new ErroDaLive("A transmissão ainda está sendo ligada pela Ktalogu.", 503, { motivo: "sem_sfu" });
    const corpo = await lerCorpo(req);
    const pedidas = (Array.isArray(corpo.quero) ? corpo.quero : [])
      .map((t) => t as { participante?: unknown; tipo?: unknown })
      .filter((t) => uuidValido(t.participante) && TIPOS_DE_TRILHA.includes(t.tipo as TipoDeTrilha))
      .slice(0, 24) as Array<{ participante: string; tipo: TipoDeTrilha }>;
    if (!pedidas.length) throw new ErroDaLive("Nada para receber.", 400);

    const equipe = ehDaEquipe(eu.papel);
    /* Reunião que ainda não começou: ninguém vê ninguém até quem organiza chegar. */
    if (!equipe && sala.categoria === "reuniao" && sala.status !== "ao_vivo") return { oferta: null, renegociar: false, trilhas: [] };
    const pessoas = new Map((await pessoasOnline(admin, sala.id)).map((p) => [p.id, p]));
    const achadas: Array<{ participante: string; tipo: TipoDeTrilha; sessao: string; nome: string }> = [];
    for (const pedido of pedidas) {
      if (pedido.participante === eu.id) continue;
      const dono = pessoas.get(pedido.participante);
      const trilha = dono?.trilhas?.[pedido.tipo];
      if (!dono || !dono.sfu_sessao || !trilha?.nome) continue;
      if (!equipe && ehDaEquipe(dono.papel) && sala.status !== "ao_vivo") continue;
      achadas.push({ participante: dono.id, tipo: pedido.tipo, sessao: dono.sfu_sessao, nome: trilha.nome });
    }
    if (!achadas.length) return { oferta: null, renegociar: false, trilhas: [] };

    const sessao = await sessaoDoSfu(admin, eu, () => novaSessao(cfg), "recebe");
    const resposta = await novasTrilhas(cfg, sessao, {
      tracks: achadas.map((a) => ({ location: "remote" as const, sessionId: a.sessao, trackName: a.nome })),
    }, 10000);
    const porNome = new Map(achadas.map((a) => [`${a.sessao}|${a.nome}`, a]));
    const trilhas = (resposta.tracks ?? [])
      .map((t) => {
        const achada = porNome.get(`${t.sessionId}|${t.trackName}`);
        /* A Cloudflare manda o erro de cada trilha no próprio item (ex.: quem manda ainda não está mandando). */
        if (!achada || !t.mid || t.error || t.errorCode) return null;
        return { mid: t.mid, participante: achada.participante, tipo: achada.tipo, nome: achada.nome };
      })
      .filter(Boolean);
    /*
     * A oferta volta SEMPRE que a Cloudflare mandar uma, mesmo se nenhuma
     * trilha deu certo: ela fica esperando a resposta, e sem a resposta todo
     * pedido seguinte desta conexão falha ("expecting a remote answer").
     */
    return {
      oferta: resposta.sessionDescription ?? null,
      renegociar: Boolean(resposta.requiresImmediateRenegotiation),
      trilhas,
    };
  });
}
