import { NextResponse } from "next/server";

import { cabecalhosDeCorsDoComputador, freios, lerJson, SEM_CACHE } from "@/lib/assistentes/oauth/http";
import { semAcentos } from "@/lib/assistentes/oauth/pedido";
import { lerRegistro } from "@/lib/assistentes/oauth/registro";
import { registrarPrograma } from "@/lib/assistentes/oauth/servidor";
import { enderecoDeQuemChama, faixaDoEndereco } from "@/lib/assistentes/tentativas";
import { createAdminClient } from "@/lib/supabase-server";

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

/**
 * O registro de um programa.
 *
 * É o próprio programa (o ChatGPT, o Cursor, o Claude Code) que chama esta
 * porta, sozinho, antes de mandar a pessoa para a tela de permitir. Por isso
 * ela é aberta. O que a protege: só é registrado quem volta para um endereço
 * da lista fechada; o pedido tem de ser JSON (um site qualquer não consegue
 * mandar JSON para cá do navegador de quem o visita sem que o navegador
 * pergunte antes, e a resposta é não); há teto por endereço e por hora; e o
 * registro que não liga ninguém em seis horas é apagado.
 */

function responder(request: Request, corpo: unknown, status: number, cabecalhos: Record<string, string> = {}) {
  return NextResponse.json(corpo, { status, headers: { ...SEM_CACHE, ...cabecalhosDeCorsDoComputador(request), ...cabecalhos } });
}

function recusar(request: Request, status: number, erro: string, descricao: string, cabecalhos: Record<string, string> = {}) {
  return responder(request, { error: erro, error_description: semAcentos(descricao) }, status, cabecalhos);
}

export async function POST(request: Request) {
  const tipo = (request.headers.get("content-type") || "").toLowerCase();
  if (!tipo.includes("application/json")) {
    return recusar(request, 400, "invalid_client_metadata", "Envie o registro como JSON (Content-Type: application/json).");
  }

  const faixa = faixaDoEndereco(enderecoDeQuemChama(request.headers));
  const { registro, registroGeral } = freios();
  if (registro.bloqueado(faixa) || registroGeral.bloqueado("todos")) {
    return recusar(request, 429, "temporarily_unavailable", "Muitos registros em pouco tempo. Tente de novo mais tarde.", { "Retry-After": "600" });
  }

  const corpo = await lerJson(request);
  if (corpo === undefined) return recusar(request, 400, "invalid_client_metadata", "O registro precisa ser um JSON de até 32 KB.");

  const lido = lerRegistro(corpo);
  if (!lido.ok) return recusar(request, 400, lido.erro, lido.descricao);

  registro.anotar(faixa);
  registroGeral.anotar("todos");

  const feito = await registrarPrograma(createAdminClient(), lido.programa, faixa);
  if (!feito) return recusar(request, 500, "server_error", "Não foi possível registrar o programa agora.");

  /* Só entram os campos que têm valor: há programa que tropeça em campo nulo. */
  return responder(
    request,
    {
      client_id: feito.programa.id,
      client_id_issued_at: Math.floor(new Date(feito.programa.criadoEm).getTime() / 1000),
      client_name: feito.programa.nome,
      redirect_uris: feito.programa.redirectUris,
      token_endpoint_auth_method: feito.programa.metodo,
      grant_types: ["authorization_code", "refresh_token"],
      response_types: ["code"],
      ...(feito.segredo ? { client_secret: feito.segredo, client_secret_expires_at: 0 } : {}),
    },
    201
  );
}

export function OPTIONS(request: Request) {
  return new NextResponse(null, { status: 204, headers: { ...SEM_CACHE, ...cabecalhosDeCorsDoComputador(request) } });
}
