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

/*
  A passagem da prévia dos temas.

  Em Catálogo > Personalização, cada cartão de tema mostra a PRÓPRIA loja do
  lojista naquele tema, num quadro em miniatura: /<slug>?previa=<passagem>&tema=<tema>.
  Fora de produção o ?tema= sempre valeu (comparar lado a lado); em produção
  ele só vale com esta passagem, assinada pelo painel
  (apps/admin/src/lib/loja/passagem-da-previa.ts) com a chave de serviço do
  Supabase. 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 que pediu (só registro; a prévia não conversa com ninguém);
  - e: quando vence.

  O prefixo é outro que o do editor ("editor-da-loja:v1:") e o do aviso de
  revalidar: a assinatura de uma nunca confere como a de outra, então a
  passagem do editor não abre a prévia e a da prévia não abre o editor.

  A prévia não é o editor: nada de rascunho nem de moldura, a loja lida da
  mesma lembrança de dois minutos de sempre (o tema muda só o desenho), e a
  visita não conta (sem page_view, sem cliques, sem chat) e não vai para
  busca. Sem passagem, vencida, adulterada ou de outra loja, ?tema= em
  produção não muda nada.
*/

export const PREFIXO_DA_PREVIA = "previa-do-tema:v1:";
export const VALIDADE_DA_PREVIA_S = 2 * 60 * 60;
export const TEMAS_DA_PREVIA = ["default", "vitrine", "vendor", "boutique"] as const;
export type TemaDaPrevia = (typeof TEMAS_DA_PREVIA)[number];

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

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

/** O tema pedido na prévia, se for um dos que a loja sabe desenhar. */
export function temaDaPrevia(valor: unknown): TemaDaPrevia | null {
  const texto = String(valor ?? "").trim();
  return (TEMAS_DA_PREVIA as readonly string[]).includes(texto) ? (texto as TemaDaPrevia) : null;
}

/**
 * O tema que a home desenha. O ?tema= só troca o desenho fora de produção
 * (comparar lado a lado) ou, em produção, na prévia com a passagem conferida;
 * em qualquer outro caso vale o tema salvo no perfil.
 */
export function temaDaPagina<T>(dados: { pedido: unknown; naPrevia: boolean; producao: boolean; salvo: T }): TemaDaPrevia | T {
  const pedido = temaDaPrevia(dados.pedido);
  return pedido && (dados.naPrevia || !dados.producao) ? pedido : dados.salvo;
}

/** Monta a passagem (o painel tem a mesma conta; aqui ela serve ao teste). */
export function criarPassagemDaPrevia(
  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_PREVIA_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 de prévia íntegra, dentro do prazo, desta loja e deste dono.
 */
export function lerPassagemDaPrevia(
  bruto: unknown,
  esperado: { dono: string; slug: string },
  chave: string,
  agoraS = Math.floor(Date.now() / 1000)
): PassagemDaPrevia | null {
  const texto = String(bruto ?? "").trim();
  if (!chave || !esperado.dono || !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_PREVIA_S + 60) return null;
  if (String(dados.u || "") !== esperado.dono) return null;
  if (String(dados.s || "").toLowerCase() !== esperado.slug.toLowerCase()) return null;
  return { dono: esperado.dono, slug: esperado.slug.toLowerCase(), vence };
}
