/**
 * A API pública da Cakto vista pelo servidor da Ktalogu. Usa CAKTO_CLIENT_ID e
 * CAKTO_CLIENT_SECRET; sem eles, `caktoConfigurada()` é falso e quem chama
 * segue sem a API (o aviso por webhook continua funcionando).
 *
 * O token vale 10 horas e é reaproveitado: pedir um a cada chamada gasta o
 * limite de emissão da Cakto.
 */

const BASE = "https://api.cakto.com.br";

type Token = { valor: string; ate: number };

declare global {
  var __tokenDaCakto: Token | null | undefined;
}

export function caktoConfigurada() {
  return Boolean(String(process.env.CAKTO_CLIENT_ID || "").trim() && String(process.env.CAKTO_CLIENT_SECRET || "").trim());
}

async function token(): Promise<string> {
  const guardado = globalThis.__tokenDaCakto;
  if (guardado && guardado.ate > Date.now()) return guardado.valor;
  const id = String(process.env.CAKTO_CLIENT_ID || "").trim();
  const segredo = String(process.env.CAKTO_CLIENT_SECRET || "").trim();
  if (!id || !segredo) throw new Error("Chave da API da Cakto não configurada.");
  const resposta = await fetch(`${BASE}/public_api/token/`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({ client_id: id, client_secret: segredo }),
  });
  if (!resposta.ok) throw new Error(`Cakto recusou a chave da API (HTTP ${resposta.status}).`);
  const json = (await resposta.json()) as { access_token?: string; expires_in?: number };
  if (!json.access_token) throw new Error("Cakto não devolveu o token.");
  globalThis.__tokenDaCakto = { valor: json.access_token, ate: Date.now() + Math.max(60, (json.expires_in ?? 3600) - 600) * 1000 };
  return json.access_token;
}

async function chamar(metodo: "GET" | "POST", caminho: string) {
  const resposta = await fetch(`${BASE}${caminho}`, {
    method: metodo,
    headers: { Authorization: `Bearer ${await token()}`, Accept: "application/json" },
  });
  if (resposta.status === 401) globalThis.__tokenDaCakto = null;
  const corpo = await resposta.json().catch(() => null);
  return { status: resposta.status, corpo };
}

type Pagina<T> = { results?: T[]; next?: string | null };

export async function lerPagina<T = Record<string, unknown>>(caminho: string): Promise<Pagina<T>> {
  const { status, corpo } = await chamar("GET", caminho);
  if (status < 200 || status >= 300) throw new Error(`Cakto respondeu HTTP ${status} em ${caminho.split("?")[0]}.`);
  return (corpo ?? {}) as Pagina<T>;
}

export type AssinaturaNaCakto = {
  id: string;
  status: string;
  product: string;
  offer: string;
  paymentMethod?: string;
  next_payment_date?: string | null;
  createdAt?: string;
  customer?: { email?: string } | string;
};

/** As assinaturas de um e-mail (a busca da Cakto procura no cliente). */
export async function assinaturasDoEmail(email: string): Promise<AssinaturaNaCakto[]> {
  const pagina = await lerPagina<AssinaturaNaCakto>(`/public_api/subscriptions/?search=${encodeURIComponent(email)}&limit=50`);
  const alvo = email.trim().toLowerCase();
  return (pagina.results ?? []).filter((item) => {
    const doCliente = typeof item.customer === "object" ? String(item.customer?.email ?? "").trim().toLowerCase() : "";
    return !doCliente || doCliente === alvo;
  });
}

/**
 * Cancela a renovação de uma assinatura. "Já cancelada" e "não existe" contam
 * como feito: é o que queríamos. Devolve o erro em texto para ficar registrado e o relógio
 * tentar de novo.
 */
export async function cancelarNaCakto(assinaturaId: string): Promise<{ ok: true } | { ok: false; erro: string }> {
  try {
    const { status, corpo } = await chamar("POST", `/public_api/subscriptions/${encodeURIComponent(assinaturaId)}/cancel/`);
    if (status >= 200 && status < 300) return { ok: true };
    const detalhe = String((corpo as { detail?: unknown } | null)?.detail ?? "");
    if (status === 400 && /cancel/i.test(detalhe)) return { ok: true };
    /* Não existe na Cakto: não há o que cancelar, e tentar de novo não muda isso. */
    if (status === 404) return { ok: true };
    return { ok: false, erro: `HTTP ${status}${detalhe ? `: ${detalhe}` : ""}` };
  } catch (e) {
    return { ok: false, erro: e instanceof Error ? e.message : String(e) };
  }
}
