import { createHmac, timingSafeEqual } from "node:crypto";
import { createAdminClient } from "@/lib/supabase-server";
import { buildPublicCatalogUrl, normalizeCustomDomain } from "@/lib/public-catalog-url";
import { documentoLimpo, type DocumentoDoBanner } from "./documento";

/*
 * O link público de uma arte (como o "qualquer pessoa com o link" do Canva):
 * /arte/<código> abre a arte para ver, tocar e baixar (PNG, JPG, PDF, MP4),
 * sem login. Editar, só dentro da Ktalogu, no editor.
 *
 * O código são 38 letras: o id da arte (22) e a assinatura dele (16), feita
 * com uma chave derivada da chave do servidor. Sem tabela nova: o link vale
 * enquanto a arte existir (apagou a arte, o link morre) e mostra sempre o
 * jeito atual dela.
 */

const TAMANHO_DO_ID = 22;
const TAMANHO_DA_ASSINATURA = 16;

function chaveDosLinks() {
  const base = process.env.SUPABASE_SERVICE_ROLE_KEY || "";
  if (!base) throw new Error("SUPABASE_SERVICE_ROLE_KEY ausente: sem chave para assinar o link da arte.");
  return createHmac("sha256", base).update("ktalogu:link-publico-da-arte:v1").digest();
}

const assinatura = (id: string) => createHmac("sha256", chaveDosLinks()).update(id.toLowerCase()).digest("base64url").slice(0, TAMANHO_DA_ASSINATURA);

/** O código do link de uma arte (id em uuid). */
export function codigoDoLink(id: string): string {
  const bytes = Buffer.from(id.replace(/-/g, ""), "hex");
  if (bytes.length !== 16) throw new Error("Id de arte inválido.");
  return `${bytes.toString("base64url")}${assinatura(id)}`;
}

/** O id da arte do código, ou null se o código não confere. */
export function idDoCodigo(codigo: string): string | null {
  if (typeof codigo !== "string" || codigo.length !== TAMANHO_DO_ID + TAMANHO_DA_ASSINATURA || !/^[A-Za-z0-9_-]+$/.test(codigo)) return null;
  const hex = Buffer.from(codigo.slice(0, TAMANHO_DO_ID), "base64url").toString("hex");
  if (hex.length !== 32) return null;
  const id = `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
  const veio = Buffer.from(codigo.slice(TAMANHO_DO_ID));
  const certa = Buffer.from(assinatura(id));
  return veio.length === certa.length && timingSafeEqual(veio, certa) ? id : null;
}

export const linkPublicoDaArte = (origem: string, id: string) => `${origem.replace(/\/$/, "")}/arte/${codigoDoLink(id)}`;

export type ArtePublica = {
  id: string;
  nome: string;
  largura: number;
  altura: number;
  documento: DocumentoDoBanner;
  atualizada: string;
  empresa: { nome: string | null; logo: string | null; cor: string | null };
  miniatura: string | null;
};

/** A arte pelo código do link (pela chave do servidor: quem abre não tem conta). */
export async function arteDoLink(codigo: string): Promise<ArtePublica | null> {
  const id = idDoCodigo(codigo);
  if (!id) return null;
  const admin = createAdminClient();
  const { data, error } = await admin.from("artes_do_editor").select("id,user_id,nome,largura,altura,documento,updated_at").eq("id", id).maybeSingle();
  if (error || !data) return null;
  const linha = data as { id: string; user_id: string; nome: string; largura: number; altura: number; documento: unknown; updated_at: string };
  const { data: perfil } = await admin.from("profiles").select("catalog_name,store_name,catalog_logo_url,logo_url,primary_color").eq("id", linha.user_id).maybeSingle();
  const p = (perfil ?? {}) as { catalog_name?: string | null; store_name?: string | null; catalog_logo_url?: string | null; logo_url?: string | null; primary_color?: string | null };
  /* A miniatura que a galeria tirou desta versão (se houver): vira a foto do link no WhatsApp. */
  const deposito = admin.storage.from("products");
  const nomeDaFoto = `${Date.parse(linha.updated_at) || 0}.webp`;
  const { data: fotos } = await deposito.list(`editor-artes/${id}`, { limit: 20 });
  const miniatura = (fotos || []).some((f) => f.name === nomeDaFoto) ? deposito.getPublicUrl(`editor-artes/${id}/${nomeDaFoto}`).data.publicUrl : null;
  return {
    id: linha.id,
    nome: linha.nome,
    largura: linha.largura,
    altura: linha.altura,
    documento: documentoLimpo(linha.documento, linha.largura, linha.altura),
    atualizada: linha.updated_at,
    empresa: { nome: p.catalog_name || p.store_name || null, logo: p.catalog_logo_url || p.logo_url || null, cor: p.primary_color || null },
    miniatura,
  };
}

/* --------------------------------------------------- no domínio da loja */

type LojaDoLink = { slug?: string | null; custom_domain?: string | null; custom_domain_verified?: boolean | null };
/* O endereço da loja de cada empresa, lembrado por 1 minuto (a lista de artes pede um link por arte). */
const lojasLembradas = ((globalThis as { __lojasDosLinksDeArte?: Map<string, { ate: number; loja: LojaDoLink | null }> }).__lojasDosLinksDeArte ??= new Map());

async function lojaDoDono(dono: string): Promise<LojaDoLink | null> {
  const lembrada = lojasLembradas.get(dono);
  if (lembrada && lembrada.ate > Date.now()) return lembrada.loja;
  /* O domínio próprio mora em profile_custom_domains (como no link do orçamento). */
  const admin = createAdminClient();
  const [{ data: perfil }, { data: dominio }] = await Promise.all([
    admin.from("profiles").select("slug").eq("id", dono).maybeSingle(),
    admin.from("profile_custom_domains").select("domain,verified").eq("profile_id", dono).maybeSingle(),
  ]);
  const d = dominio as { domain?: string | null; verified?: boolean | null } | null;
  const loja: LojaDoLink | null = perfil ? { slug: (perfil as { slug?: string | null }).slug ?? null, custom_domain: d?.domain ?? null, custom_domain_verified: Boolean(d?.verified) } : null;
  lojasLembradas.set(dono, { ate: Date.now() + 60_000, loja });
  if (lojasLembradas.size > 500) lojasLembradas.delete(lojasLembradas.keys().next().value as string);
  return loja;
}

/**
 * O link público no endereço da LOJA da empresa (como o pedido e o
 * orçamento): o domínio próprio verificado (meudominio.com.br/arte/<código>)
 * ou o grátis (ktalogu.com.br/<loja>/arte/<código>). A página da loja mostra
 * a arte daqui (apps/public/src/app/[slug]/arte). Sem loja, fica no painel.
 */
export async function linkPublicoNaLoja(origemDoPainel: string, dono: string, id: string): Promise<string> {
  const loja = await lojaDoDono(dono).catch(() => null);
  const slug = String(loja?.slug || "").trim();
  const dominio = normalizeCustomDomain(loja?.custom_domain);
  if (!slug && !(dominio && loja?.custom_domain_verified)) return linkPublicoDaArte(origemDoPainel, id);
  /* Na máquina de quem desenvolve, a loja roda em LOJA_LOCAL (ver loja-local-na-porta-3005). */
  const base = process.env.NODE_ENV !== "production" && process.env.LOJA_LOCAL ? process.env.LOJA_LOCAL : origemDoPainel;
  const codigo = codigoDoLink(id);
  return buildPublicCatalogUrl(base, loja, { slugPath: `/${slug}/arte/${codigo}`, customPath: `/arte/${codigo}` });
}
