import { NextResponse } from "next/server";

import { lerCorpoComLimite } from "@/lib/assistentes/corpo";
import { enderecosDoConector } from "@/lib/assistentes/oauth/enderecos";
import { enderecoDeVolta } from "@/lib/assistentes/oauth/pedido";
import { avaliarPedido, lerParametros } from "@/lib/assistentes/oauth/permitir";
import { criarCodigo } from "@/lib/assistentes/oauth/servidor";
import { createAdminClient } from "@/lib/supabase-server";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

const SEM_CACHE = { "Cache-Control": "no-store" };

const recusa = (status: number, erro: string) => NextResponse.json({ erro }, { status, headers: SEM_CACHE });

/**
 * O clique em "Permitir" (ou em "Cancelar") da tela de permissão.
 *
 * Esta é a única porta do login por botão que usa a sessão da pessoa, e por
 * isso é a única que só atende a própria tela: o pedido tem de vir do endereço
 * do painel. Tudo o que a tela conferiu ao abrir é conferido de novo aqui,
 * antes de nascer o código que volta para o programa.
 */
export async function POST(request: Request) {
  const enderecos = enderecosDoConector(request);

  /* Só a própria tela chama. Um site de fora não consegue mandar este pedido
     com a sessão da pessoa (o navegador não envia), e se conseguisse pararia
     aqui: a origem tem de ser a do painel. */
  const origem = request.headers.get("origin");
  const aceitas = new Set([new URL(enderecos.painel).origin, new URL(enderecos.pedida).origin]);
  if (!origem || !aceitas.has(origem)) return recusa(403, "Pedido recusado.");
  const deOnde = request.headers.get("sec-fetch-site");
  if (deOnde && deOnde !== "same-origin") return recusa(403, "Pedido recusado.");

  const bruto = await lerCorpoComLimite(request, 16 * 1024);
  let corpo: Record<string, unknown> | null = null;
  try {
    const lido = bruto === null ? null : (JSON.parse(bruto) as unknown);
    if (lido && typeof lido === "object" && !Array.isArray(lido)) corpo = lido as Record<string, unknown>;
  } catch {
    corpo = null;
  }
  if (!corpo) return recusa(400, "Pedido inválido.");

  const decisao = corpo.decisao;
  const acesso = corpo.acesso;
  if (decisao !== "permitir" && decisao !== "cancelar") return recusa(400, "Pedido inválido.");

  const parametros = lerParametros((corpo.parametros ?? {}) as Record<string, unknown>);
  const avaliacao = await avaliarPedido(request, parametros);

  if (avaliacao.tipo === "erro") return recusa(400, avaliacao.mensagem);
  if (avaliacao.tipo === "voltar") return NextResponse.json({ destino: avaliacao.destino, resultado: "erro" }, { headers: SEM_CACHE });
  if (avaliacao.tipo === "entrar") return recusa(401, "A sua sessão terminou. Entre de novo para continuar.");
  if (avaliacao.tipo === "sem_acesso") {
    if (decisao === "cancelar") return NextResponse.json({ destino: avaliacao.voltar, resultado: "cancelado" }, { headers: SEM_CACHE });
    return recusa(403, avaliacao.mensagem);
  }

  const { pedido } = avaliacao;
  if (decisao === "cancelar") {
    return NextResponse.json(
      {
        destino: enderecoDeVolta(pedido.redirectUri, {
          error: "access_denied",
          error_description: "A pessoa nao permitiu a conexao.",
          state: pedido.estado,
          iss: avaliacao.enderecos.emissor,
        }),
        resultado: "cancelado",
      },
      { headers: SEM_CACHE }
    );
  }

  /* A pessoa escolhe na tela o que o assistente pode fazer. Sem escolha, não há permissão. */
  if (acesso !== "leitura" && acesso !== "escrita") return recusa(400, "Escolha o que o assistente pode fazer.");

  const codigo = await criarCodigo(createAdminClient(), {
    programaId: avaliacao.registro.id,
    usuario: { id: avaliacao.user.id, email: avaliacao.user.email ?? null },
    companyId: avaliacao.contexto.companyId,
    donoId: avaliacao.contexto.ownerId,
    escopos: acesso === "escrita" ? ["leitura", "escrita"] : ["leitura"],
    redirectUri: pedido.redirectUri,
    desafio: pedido.desafio,
    recurso: pedido.recurso,
  });
  if (!codigo) return recusa(503, "Não foi possível concluir a conexão agora. Tente de novo.");

  return NextResponse.json(
    { destino: enderecoDeVolta(pedido.redirectUri, { code: codigo, state: pedido.estado, iss: avaliacao.enderecos.emissor }), resultado: "permitido" },
    { headers: SEM_CACHE }
  );
}
