import { getDb } from '@pintrush/db';
import { declaredBodyLength, isBot, MAX_BEACON_BYTES, parseBeacon, prefersNoTracking, readBodyCapped, trackedPath } from '@/lib/analytics';
import { clientIp, hashIp, SlidingWindowLimiter, utcDay } from '@/lib/rate-limit';
import { analyticsSalt } from '@/lib/server-env';
import { siteUrl } from '@/lib/site';

/**
 * Cookie-less page-view beacon. Stores {kind: PAGE_VIEW, path, referrer host}; never cookies, never
 * IPs, nothing at all for bots or visitors sending DNT / Sec-GPC. Always answers 204 for valid
 * requests so the response reveals nothing about what was recorded.
 *
 * Order matters: everything that can be decided from headers (size, rate limits) happens before a
 * single body byte is read, the body is read with a hard byte cap, and only views of real site
 * routes count against the global ceiling and reach the database.
 */

// Keyed by a salted hash held only in memory; caps how fast one client can inflate counts.
const limiter = new SlidingWindowLimiter(60, 10 * 60 * 1000);
// Process-wide ceiling on stored views, whatever addresses they claim to come from. Far above real
// traffic for a pilot-stage marketing site; stops a flood from growing the table without bound.
const globalLimiter = new SlidingWindowLimiter(20_000, 60 * 60 * 1000);

function empty(status: number): Response {
  return new Response(null, { status, headers: { 'Cache-Control': 'no-store' } });
}

export async function POST(request: Request): Promise<Response> {
  const headers = request.headers;
  if (prefersNoTracking(headers) || isBot(headers.get('user-agent'))) return empty(204);

  // Browsers mark cross-site beacons; only count views sent from our own pages.
  const fetchSite = headers.get('sec-fetch-site');
  if (fetchSite && fetchSite !== 'same-origin') return empty(204);

  // sendBeacon and fetch with a string body always declare their length; chunked uploads are refused.
  const length = declaredBodyLength(headers.get('content-length'));
  if (!length.ok) return empty(length.status);

  const now = Date.now();
  if (!limiter.hit(hashIp(clientIp(headers), analyticsSalt(), utcDay(now)), now).allowed) return empty(204);

  const body = await readBodyCapped(request.body, MAX_BEACON_BYTES);
  if (body === null) return empty(413);
  const view = parseBeacon(body, siteUrl.hostname);
  if (!view) return empty(400);
  // A well-formed beacon for a path that isn't a site route: say nothing, store nothing.
  const path = trackedPath(view.path);
  if (!path) return empty(204);
  if (!globalLimiter.hit('all', now).allowed) return empty(204);

  try {
    await getDb().analyticsEvent.create({ data: { kind: 'PAGE_VIEW', path, referrer: view.referrer } });
  } catch (error) {
    // Analytics must never surface errors to visitors.
    console.error('[analytics] Could not record page view:', error instanceof Error ? error.message : error);
  }
  return empty(204);
}
