import { createHmac, timingSafeEqual } from "node:crypto";

/*
  A passagem do editor da página inicial.

  O painel abre a loja dentro dele com /<slug>?editar=<passagem>. Só com a
  passagem a página vira editor: lê o banco sem a lembrança de dois minutos,
  aceita o rascunho que o painel manda e desenha os botões de cada seção. A
  passagem é assinada pelo painel com a chave de serviço do Supabase (a mesma
  que assina o aviso de /api/loja/revalidar, com outro prefixo, então uma não
  serve no lugar da outra), vale duas horas e diz:

  - u: o perfil dono da loja (tem de ser o dono do slug aberto);
  - s: o slug;
  - o: a origem do painel, a única que pode conversar com o editor.

  Sem passagem, com passagem vencida, de outra loja ou adulterada, a página
  é a loja de sempre. Nada aqui grava: o editor só mostra; quem grava é o
  painel, com o login de quem está nele.
*/

export const PREFIXO_DA_PASSAGEM = "editor-da-loja:v1:";
export const VALIDADE_DA_PASSAGEM_S = 2 * 60 * 60;

export type PassagemDoEditor = { dono: string; slug: string; origemDoPainel: string; vence: number };

function assinar(carga: string, chave: string) {
  return createHmac("sha256", chave).update(`${PREFIXO_DA_PASSAGEM}${carga}`).digest("hex");
}

function origemValida(valor: unknown): string | null {
  const texto = String(valor ?? "").trim();
  if (!texto || texto.length > 200) return null;
  try {
    const url = new URL(texto);
    if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) return null;
    const local = /^(localhost|127\.0\.0\.1)$/i.test(url.hostname);
    if (url.protocol !== "https:" && !(local && url.protocol === "http:")) return null;
    return url.origin;
  } catch {
    return null;
  }
}

/** Monta a passagem (o painel tem a mesma conta; aqui ela serve ao teste). */
export function criarPassagemDoEditor(
  dados: { dono: string; slug: string; origemDoPainel: string; agoraS?: number },
  chave: string
): string {
  const agora = dados.agoraS ?? Math.floor(Date.now() / 1000);
  const carga = Buffer.from(
    JSON.stringify({ u: dados.dono, s: dados.slug.toLowerCase(), o: dados.origemDoPainel, e: agora + VALIDADE_DA_PASSAGEM_S }),
    "utf8"
  ).toString("base64url");
  return `${carga}.${assinar(carga, chave)}`;
}

/**
 * Lê e confere a passagem. Devolve null para qualquer coisa que não seja uma
 * passagem íntegra, dentro do prazo, desta loja e deste dono.
 */
export function lerPassagemDoEditor(
  bruto: unknown,
  esperado: { dono: string; slug: string },
  chave: string,
  agoraS = Math.floor(Date.now() / 1000)
): PassagemDoEditor | null {
  const texto = String(bruto ?? "").trim();
  if (!chave || !texto || texto.length > 1200) return null;
  const ponto = texto.lastIndexOf(".");
  if (ponto <= 0) return null;
  const carga = texto.slice(0, ponto);
  const assinatura = texto.slice(ponto + 1);
  const certa = assinar(carga, chave);
  if (assinatura.length !== certa.length) return null;
  if (!timingSafeEqual(Buffer.from(assinatura, "utf8"), Buffer.from(certa, "utf8"))) return null;

  let dados: Record<string, unknown>;
  try {
    dados = JSON.parse(Buffer.from(carga, "base64url").toString("utf8"));
  } catch {
    return null;
  }
  if (!dados || typeof dados !== "object") return null;
  const vence = Number(dados.e);
  if (!Number.isFinite(vence) || vence < agoraS || vence > agoraS + VALIDADE_DA_PASSAGEM_S + 60) return null;
  if (String(dados.u || "") !== esperado.dono) return null;
  if (String(dados.s || "").toLowerCase() !== esperado.slug.toLowerCase()) return null;
  const origemDoPainel = origemValida(dados.o);
  if (!origemDoPainel) return null;
  return { dono: esperado.dono, slug: esperado.slug, origemDoPainel, vence };
}
