import { NextResponse } from "next/server";

import type { CompanyPermission } from "@/lib/company-context";
import { ErroDaSpedy } from "@/lib/spedy/cliente";

import { abrirContextoFiscal, type ContextoFiscal } from "./base";
import { ErroFiscal } from "./notas";

/**
 * O invólucro das rotas fiscais: abre o contexto (sessão, empresa, permissão),
 * roda e traduz qualquer erro para uma resposta que a tela sabe mostrar. Erro
 * inesperado vira mensagem genérica: detalhe técnico fica só no log.
 */
export async function rotaFiscal(
  permissao: CompanyPermission | CompanyPermission[] | null,
  executar: (contexto: ContextoFiscal) => Promise<unknown>
): Promise<Response> {
  const contexto = await abrirContextoFiscal(permissao);
  if (contexto instanceof NextResponse) return contexto;
  try {
    const resultado = await executar(contexto);
    if (resultado instanceof Response) return resultado;
    return NextResponse.json(resultado ?? { ok: true });
  } catch (erro) {
    return respostaDeErro(erro);
  }
}

export function respostaDeErro(erro: unknown): Response {
  if (erro instanceof ErroFiscal) {
    return NextResponse.json({ error: erro.message, ...(erro.extra ?? {}) }, { status: erro.status });
  }
  if (erro instanceof ErroDaSpedy) {
    const status = erro.transitorio ? 503 : erro.status === 403 ? 502 : 422;
    return NextResponse.json({ error: erro.message }, { status });
  }
  console.error("[fiscal] erro inesperado", erro);
  return NextResponse.json({ error: "Algo deu errado do nosso lado. Tente de novo em instantes." }, { status: 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>) : {};
}

export const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

export function exigirUuid(valor: string | undefined | null, nome = "Registro") {
  if (!valor || !UUID.test(valor)) throw new ErroFiscal(`${nome} inválido.`, 400);
  return valor;
}
