type FbqFn = (method: string, ...args: unknown[]) => void;

export type PixelEvent = 'PageView' | 'ViewContent' | 'InitiateCheckout' | 'Lead';

export interface PixelEventParams {
  contentName?: string;
  contentCategory?: string;
}

/** Raw (unhashed) user identifiers for advanced matching. */
export interface PixelUserData {
  email?: string | null;
  phone?: string | null;
}

// Hashed user data stored in sessionStorage for the duration of the browsing session.
// Keyed by a short namespace to avoid collisions.
const STORAGE_KEY = 'ks_pud';

async function sha256Hex(str: string): Promise<string> {
  const buffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(str));
  return Array.from(new Uint8Array(buffer))
    .map((b) => b.toString(16).padStart(2, '0'))
    .join('');
}

function getFbq(): FbqFn | undefined {
  if (typeof window === 'undefined') return undefined;
  return (window as Window & { fbq?: FbqFn }).fbq;
}

/** Read the pixel IDs registered by MetaPixel via the inline init script. */
function getRegisteredPixelIds(): string[] {
  return (window as Window & { __ks_pixel_ids?: string[] }).__ks_pixel_ids ?? [];
}

/** Apply stored hashed user data to every configured Meta Pixel. */
function applyStoredUserData(fbq: FbqFn): void {
  try {
    const raw = sessionStorage.getItem(STORAGE_KEY);
    if (!raw) return;
    const hashed = JSON.parse(raw) as Record<string, string>;
    if (Object.keys(hashed).length === 0) return;
    getRegisteredPixelIds().forEach((id) => fbq('init', id, hashed));
  } catch {
    // sessionStorage unavailable or JSON malformed — safe to ignore
  }
}

/**
 * Hash and store user identifiers so they are automatically included in all
 * subsequent pixel events for this browser session. Call this as soon as
 * identity is known (e.g. contact form submission, portal login step 1).
 */
export async function setPixelUserData(userData: PixelUserData): Promise<void> {
  const hashed: Record<string, string> = {};

  if (userData.email) {
    hashed.em = await sha256Hex(userData.email.trim().toLowerCase());
  }
  if (userData.phone) {
    const digits = userData.phone.replace(/\D/g, '');
    if (digits) hashed.ph = await sha256Hex(digits);
  }

  if (Object.keys(hashed).length === 0) return;

  try {
    sessionStorage.setItem(STORAGE_KEY, JSON.stringify(hashed));
  } catch {
    // sessionStorage unavailable — still apply to fbq for this page load
  }

  const fbq = getFbq();
  if (fbq) {
    getRegisteredPixelIds().forEach((id) => fbq('init', id, hashed));
  }
}

/**
 * Single entry point for all client-side Meta Pixel event fires.
 * Automatically applies any stored user identity before firing so that Meta
 * can match events to known users across the entire session.
 * Silently no-ops if fbq is not loaded (pixel not configured for this site).
 */
export function firePixelEvent(event: PixelEvent, params?: PixelEventParams): void {
  const fbq = getFbq();
  if (!fbq) {
    console.debug('[MetaPixel] skipped — fbq not loaded', { event });
    return;
  }

  // Re-apply stored identity before every event so user data is included
  // even on events that fire after a client-side navigation (PageView, ViewContent, etc.)
  applyStoredUserData(fbq);

  const normalized: Record<string, string> = {};
  if (params?.contentName) normalized.content_name = params.contentName;
  if (params?.contentCategory) normalized.content_category = params.contentCategory;

  console.debug('[MetaPixel]', event, normalized);
  fbq('track', event, Object.keys(normalized).length > 0 ? normalized : undefined);
}
