import { createHash, randomBytes, randomInt, timingSafeEqual } from "node:crypto";

import type { SupabaseClient } from "@supabase/supabase-js";

/*
 * A entrada na área de membros ("Minhas compras") por código no e-mail.
 *
 * Só recebe código quem tem cadastro NESTA loja com este e-mail e comprou
 * algum produto digital nela. O código tem 6 números, vale 10 minutos e
 * aguenta 5 tentativas erradas. Acertou: nasce uma sessão do portal do
 * cliente, igual à do login com senha (o mesmo cookie, os mesmos 15 dias).
 *
 * O e-mail é o do CADASTRO: quem comprou com outro e-mail recebe tudo pelo
 * link do e-mail da entrega, mas não entra no portal de ninguém.
 */

export const VALIDADE_DO_CODIGO_MS = 10 * 60 * 1000;
export const TENTATIVAS_POR_CODIGO = 5;
export const CODIGOS_POR_CLIENTE_POR_HORA = 5;
export const CODIGOS_POR_IP_POR_HORA = 12;
const DIAS_DA_SESSAO = 15;

export function emailValido(email: string): boolean {
  return /^[^\s@]{1,64}@[^\s@]{1,190}\.[a-z]{2,24}$/i.test(email);
}

export function novoCodigo(): string {
  return String(randomInt(0, 1_000_000)).padStart(6, "0");
}

/* O resumo leva o cliente junto: o mesmo código para duas pessoas não se confunde. */
export function resumoDoCodigo(clienteId: string, codigo: string): string {
  return createHash("sha256").update(`${clienteId}:${codigo}`, "utf8").digest("hex");
}

export function resumosIguais(a: string, b: string): boolean {
  const x = Buffer.from(a, "utf8");
  const y = Buffer.from(b, "utf8");
  return x.length === y.length && timingSafeEqual(x, y);
}

/** O cliente desta loja com este e-mail que tem compras digitais (ou null). */
export async function clienteDosMembros(admin: SupabaseClient, lojaId: string, email: string): Promise<{ id: string; name: string | null } | null> {
  const { data } = await admin
    .from("clients")
    .select("id,name,portal_enabled,portal_status,active,is_deleted")
    .eq("user_id", lojaId)
    /* `_` e `%` seriam curingas na comparação: escapados, o e-mail bate só com ele. */
    .ilike("email", email.replace(/[\\%_]/g, "\\$&"))
    .limit(10);
  const candidatos = ((data ?? []) as Array<{
    id: string;
    name: string | null;
    portal_enabled: boolean | null;
    portal_status: string | null;
    active: boolean | null;
    is_deleted: boolean | null;
  }>).filter((c) => c.is_deleted !== true && c.active !== false && c.portal_enabled !== false && c.portal_status !== "rejected");
  if (!candidatos.length) return null;

  const { data: acessos } = await admin
    .from("digital_entitlements")
    .select("client_id,criado_em")
    .eq("user_id", lojaId)
    .in("client_id", candidatos.map((c) => c.id))
    .order("criado_em", { ascending: false })
    .limit(1);
  const comCompra = ((acessos ?? []) as Array<{ client_id: string }>)[0]?.client_id;
  const escolhido = candidatos.find((c) => c.id === comCompra);
  return escolhido ? { id: escolhido.id, name: escolhido.name } : null;
}

/** Cria a sessão do portal do cliente (o mesmo formato do login com senha). */
export async function criarSessaoDoPortal(
  admin: SupabaseClient,
  params: { lojaId: string; clienteId: string; ip: string | null; navegador: string | null }
): Promise<{ token: string; expiraEm: string }> {
  const token = randomBytes(32).toString("hex");
  const expiraEm = new Date(Date.now() + DIAS_DA_SESSAO * 24 * 60 * 60 * 1000).toISOString();
  const { error } = await admin.from("client_portal_sessions").insert({
    user_id: params.lojaId,
    client_id: params.clienteId,
    token_hash: createHash("sha256").update(token, "utf8").digest("hex"),
    ip_address: params.ip?.slice(0, 64) || null,
    user_agent: params.navegador?.slice(0, 500) || null,
    expires_at: expiraEm,
  });
  if (error) throw new Error("Não foi possível entrar agora.");
  await admin.from("clients").update({ portal_last_login_at: new Date().toISOString() }).eq("id", params.clienteId);
  return { token, expiraEm };
}

export const DURACAO_DA_SESSAO_S = DIAS_DA_SESSAO * 24 * 60 * 60;
