import { NextResponse } from "next/server";
import { createAdminClient } from "@/lib/supabase-admin";
import { carregarLoja } from "@/lib/loja/dados-da-loja";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

/*
  Um clique num link ou botão de uma seção da página da loja
  (components/home/ContadorDeCliques). Soma um no dia da seção
  (contar_clique_da_secao, migration 199): nada do visitante é guardado.

  O freio fica aqui, na memória do processo: cada endereço de internet conta
  no máximo um clique a cada 2 segundos por seção, 40 por hora por seção e
  240 por hora no total. Passou disso, responde 204 do mesmo jeito (quem
  insiste não aprende o limite) e não conta. A tabela da memória tem teto.
*/

type Marca = { ultimo: number; janela: number; quantos: number };

declare global {
  var __freioDosCliques: Map<string, Marca> | undefined;
}

const HORA_MS = 60 * 60 * 1000;
const TETO_DA_MEMORIA = 50_000;

function freio() {
  globalThis.__freioDosCliques ??= new Map();
  return globalThis.__freioDosCliques;
}

function passa(chave: string, intervaloMs: number, porHora: number, agora: number) {
  const mapa = freio();
  const marca = mapa.get(chave);
  if (!marca || agora - marca.janela >= HORA_MS) {
    if (mapa.size >= TETO_DA_MEMORIA) {
      /* Esquece as marcas velhas; se ainda estiver cheia, recomeça. */
      for (const [k, m] of mapa) if (agora - m.janela >= HORA_MS) mapa.delete(k);
      if (mapa.size >= TETO_DA_MEMORIA) mapa.clear();
    }
    mapa.set(chave, { ultimo: agora, janela: agora, quantos: 1 });
    return true;
  }
  if (agora - marca.ultimo < intervaloMs || marca.quantos >= porHora) return false;
  marca.ultimo = agora;
  marca.quantos += 1;
  return true;
}

function ipDoPedido(request: Request) {
  return (
    String(request.headers.get("cf-connecting-ip") || "").trim() ||
    String(request.headers.get("x-forwarded-for") || "").split(",")[0].trim() ||
    String(request.headers.get("x-real-ip") || "").trim() ||
    "sem-ip"
  );
}

const nada = () => new NextResponse(null, { status: 204 });

export async function POST(request: Request) {
  const texto = await request.text().catch(() => "");
  if (!texto || texto.length > 500) return nada();
  let corpo: { slug?: unknown; secao?: unknown } | null = null;
  try {
    corpo = JSON.parse(texto);
  } catch {
    return nada();
  }
  const slug = String(corpo?.slug ?? "").trim().toLowerCase();
  const secao = String(corpo?.secao ?? "").trim().toLowerCase();
  if (!/^[a-z0-9-]{1,80}$/.test(slug) || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(secao)) return nada();

  const ip = ipDoPedido(request);
  const agora = Date.now();
  if (!passa(`ip:${ip}`, 0, 240, agora) || !passa(`secao:${ip}:${secao}`, 2000, 40, agora)) return nada();

  /* O dono sai da loja lembrada (sem ida ao banco na maioria das vezes). A
     função confere que a seção é dessa loja e está ligada. */
  const loja = await carregarLoja(slug).catch(() => null);
  const dono = String(loja?.profile?.id || "");
  if (!dono) return nada();

  const { error } = await createAdminClient().rpc("contar_clique_da_secao", { p_dono: dono, p_secao: secao });
  if (error && process.env.NODE_ENV !== "production") console.warn("[cliques] não contou", error.message);
  return nada();
}
