// Pushes custom events to the GTM dataLayer.
// GTM consent mode (Silktide analytics_storage) governs whether GA4 receives each event.
import { pageTypeOf } from '@sarimarcus/content-sites-core/url';

declare global {
  interface Window {
    dataLayer: Record<string, unknown>[];
    silktideConsentManager?: { hasConsent(id: string): boolean };
    track?: (eventName: string, params?: Record<string, unknown>) => void;
  }
}

function hasAnalyticsConsent(): boolean {
  const mgr = window.silktideConsentManager;
  if (!mgr) return true; // not yet loaded — GTM consent mode enforces at send time
  return typeof mgr.hasConsent === 'function' ? mgr.hasConsent('analytics') : true;
}

export function track(eventName: string, params: Record<string, unknown> = {}): void {
  if (!hasAnalyticsConsent()) return;
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({ event: eventName, page_path: window.location.pathname, ...params });
}

// Param key derived from the data-track-* attribute name where the
// event-specific prefix (e.g. 'cta_', 'affiliate_', 'link_') has been stripped.
const HREF_DESTINATION_KEYS: Record<string, string> = {
  outbound_click: 'link_destination',
  cta_click: 'cta_destination',
  internal_link_click: 'link_destination',
};

// A same-origin link inside a [data-track-zone] ancestor, or null.
export function zoneLinkOf(target: Element, origin: string): { link: HTMLAnchorElement; zone: string } | null {
  const link = target.closest<HTMLAnchorElement>('a[href]');
  if (!link || link.origin !== origin) return null;
  const zone = link.closest<HTMLElement>('[data-track-zone]')?.dataset.trackZone;
  return zone ? { link, zone } : null;
}

// Reads data-track-* attributes from the clicked element and fires track().
// Handles affiliate_click, cta_click, outbound_click, internal_link_click.
// An untagged same-origin link inside [data-track-zone] fires internal_link_click with the zone as link_block.
export function setupDelegatedTracking(): void {
  // MPA — pathname is stable for the page's lifetime; compute once.
  const pageType = pageTypeOf(window.location.pathname);

  document.addEventListener('click', (e: MouseEvent) => {
    const target = e.target;
    if (!(target instanceof Element)) return;
    const el = target.closest<HTMLElement>('[data-track-event]');
    if (!el) {
      const zoned = zoneLinkOf(target, window.location.origin);
      if (zoned) track('internal_link_click', { page_type: pageType, link_block: zoned.zone, link_destination: zoned.link.href });
      return;
    }
    const eventName = el.dataset.trackEvent;
    if (!eventName) return;

    const params: Record<string, unknown> = { page_type: pageType };

    for (const key of Object.keys(el.dataset)) {
      if (key === 'trackEvent' || !key.startsWith('track')) continue;
      const param = key
        .slice('track'.length)
        .replace(/([A-Z])/g, '_$1')
        .toLowerCase()
        .replace(/^_/, '');
      params[param] = el.dataset[key];
    }

    // Fall back to href for the destination param when the markup didn't set it
    // — saves repeating the URL on every CTA/link.
    const destKey = HREF_DESTINATION_KEYS[eventName];
    if (destKey && !params[destKey] && el instanceof HTMLAnchorElement) {
      params[destKey] = el.href;
    }

    track(eventName, params);
  });
}

// Fires faq_expand on <details data-faq-track> open. Uses capture because toggle doesn't bubble.
export function setupFaqTracking(): void {
  document.addEventListener(
    'toggle',
    (e: Event) => {
      const details = e.target as HTMLDetailsElement;
      if (!details.hasAttribute('data-faq-track') || !details.open) return;
      const question = details.querySelector('summary h3')?.textContent?.trim() ?? '';
      track('faq_expand', { question });
    },
    true,
  );
}

// Fires scroll_depth at 25/50/75/100% — deduped per page view, removed after 100%.
// Reads layout-triggering geometry (scrollHeight) inside a rAF-throttled tick
// so rapid scroll events coalesce to one measurement per frame.
export function setupScrollDepthTracking(): void {
  const thresholds = [25, 50, 75, 100];
  const fired = new Set<number>();
  let frameQueued = false;

  function measure() {
    frameQueued = false;
    const total = document.documentElement.scrollHeight;
    if (total <= window.innerHeight) return;
    const pct = Math.round(((window.scrollY + window.innerHeight) / total) * 100);
    for (const d of thresholds) {
      if (pct >= d && !fired.has(d)) {
        fired.add(d);
        track('scroll_depth', { percent: d });
      }
    }
    if (fired.size === thresholds.length) {
      window.removeEventListener('scroll', onScroll);
    }
  }

  function onScroll() {
    if (frameQueued) return;
    frameQueued = true;
    requestAnimationFrame(measure);
  }

  window.addEventListener('scroll', onScroll, { passive: true });
}

export interface AnalyticsOptions {
  /** Pages that get scroll-depth tracking, tested against `location.pathname`. Omitted or false: none; true: every page. */
  scrollDepth?: boolean | RegExp | ((pathname: string) => boolean);
  /** Track `<details data-faq-track>` opens. */
  faq?: boolean;
}

let installed = false;

/** Sets `window.track` and binds delegated click, FAQ and scroll-depth tracking. A second call is a no-op. */
export function setupAnalytics(options: AnalyticsOptions = {}): void {
  if (installed) return;
  installed = true;
  window.track = track;
  setupDelegatedTracking();
  if (options.faq) setupFaqTracking();
  const { scrollDepth } = options;
  const path = window.location.pathname;
  const wantsScroll =
    scrollDepth instanceof RegExp ? scrollDepth.test(path) : typeof scrollDepth === 'function' ? scrollDepth(path) : scrollDepth === true;
  if (wantsScroll) setupScrollDepthTracking();
}
