/**
 * Analytics Engine click writer for referral link tracking.
 *
 * AE schema (logical):
 *   index1  = link_id
 *   blob1   = link_id (redundant for query convenience)
 *   blob2   = ip_hash
 *   blob3   = ua_family  ('mobile'|'desktop'|'bot'|'other')
 *   blob4   = country    (CF-IPCountry, 2-letter ISO)
 *   blob5   = referer_host
 *   blob6   = utm_joined (utm_source|utm_medium|utm_campaign pipe-joined)
 *   blob7   = suspicion_codes (JSON array, '[]' if clean)
 *   blob8   = visitor_id (device fingerprint, '' if absent)
 *   double1 = is_suspicious (0 or 1)
 *
 * Privacy: ip_hash uses a daily-rotating salt — raw IPs never persisted.
 * AE retention: 90 days.
 * Fire-and-forget: do NOT await writeDataPoint on the request path.
 */

import { tryParseRefererUrl } from './security/referer-parse.js';

export type AnalyticsEngineDataset = {
  writeDataPoint(data: { indexes?: string[]; blobs?: string[]; doubles?: number[] }): void;
};

export type UaFamily = 'mobile' | 'desktop' | 'bot' | 'other';

export type ClickPoint = {
  linkId: string;
  ipHash: string;
  uaFamily: UaFamily;
  country: string;
  refererHost: string;
  utmJoined: string;
  isSuspicious: boolean;
  suspicionCodes: string[];
  /** Device/browser fingerprint from client-side FP library. Empty string if absent. */
  visitorId?: string;
};

// Bot patterns: common crawlers and headless browsers.
const BOT_PATTERN =
  /bot|crawler|spider|scraper|googlebot|bingbot|slurp|duckduck|baidu|yandex|sogou|exabot|facebot|ia_archiver|headless/i;

// Mobile patterns: phones and tablets.
const MOBILE_PATTERN =
  /mobile|android|iphone|ipad|ipod|blackberry|windows phone|opera mini|opera mobi/i;

/**
 * Classify a User-Agent string into a device family bucket.
 */
export function extractUaFamily(ua: string): UaFamily {
  if (!ua) return 'other';
  if (BOT_PATTERN.test(ua)) return 'bot';
  if (MOBILE_PATTERN.test(ua)) return 'mobile';
  // If it includes common desktop cues — treat as desktop.
  if (ua.includes('Macintosh') || ua.includes('Windows NT') || ua.includes('X11')) return 'desktop';
  return 'other';
}

/**
 * Extract the eTLD+1 hostname from a referer URL, stripping www.
 * Returns empty string for null/invalid/missing referers.
 */
export function extractRefererHost(referer: string | null): string {
  if (!referer) return '';
  const url = tryParseRefererUrl(referer);
  if (!url) return '';
  return url.hostname.replace(/^www\./, '');
}

/**
 * Write a referral click data point to the Analytics Engine binding.
 *
 * Fire-and-forget — no return value, no await needed on the call site.
 * The AE binding buffers writes internally.
 */
export function writeClickToAE(binding: AnalyticsEngineDataset, point: ClickPoint): void {
  binding.writeDataPoint({
    indexes: [point.linkId],
    blobs: [
      point.linkId,
      point.ipHash,
      point.uaFamily,
      point.country,
      point.refererHost,
      point.utmJoined,
      JSON.stringify(point.suspicionCodes),
      point.visitorId ?? '', // blob8: visitor_id (device fingerprint)
    ],
    doubles: [point.isSuspicious ? 1 : 0],
  });
}

/**
 * Write a social share click data point to the Analytics Engine binding.
 *
 * Discriminated from referral clicks by blob8='share'.
 * index1 uses 'share:' prefix so AE index queries can optionally filter by type.
 *
 * Fire-and-forget — do NOT await on the request path.
 */
export function writeShareClickToAE(binding: AnalyticsEngineDataset, point: ClickPoint): void {
  binding.writeDataPoint({
    indexes: [`share:${point.linkId}`],
    blobs: [
      point.linkId,
      point.ipHash,
      point.uaFamily,
      point.country,
      point.refererHost,
      point.utmJoined,
      JSON.stringify(point.suspicionCodes),
      'share', // blob8 type discriminator — referral clicks have '' here
    ],
    doubles: [point.isSuspicious ? 1 : 0],
  });
}
