/**
 * Silent user behavior tracking
 *
 * Uses navigator.sendBeacon for non-blocking delivery.
 * Batches events (max 10 per 5s flush).
 */

import { hasAnalyticsConsent } from '@/lib/cookie-consent';
import { getPaidProductMetadata } from '@/lib/offer';

const API_URL = process.env.NEXT_PUBLIC_API_URL || 'https://rainmakersports.app/api';
const FLUSH_INTERVAL = 5000;
const MAX_BATCH = 10;
const TIKTOK_COOKIE_TTL_SECONDS = 90 * 24 * 60 * 60;
const X_SIGNUP_EVENT_ID = process.env.NEXT_PUBLIC_X_SIGNUP_EVENT_ID || 'tw-rbtif-rbtjd';

type BufferedEvent = {
  event_type: string;
  event_data: any;
  session_id: string;
  user_id: string | null;
  timestamp: string;
};

type TrackEventOptions = {
  immediate?: boolean;
};

export type LeadTrackingPayload = {
  consent: boolean;
  eventId: string;
  method?: string | null;
  placement?: string | null;
  referrer?: string | null;
  ttclid?: string | null;
  ttp?: string | null;
  url?: string | null;
};

let eventBuffer: BufferedEvent[] = [];
let flushTimer: ReturnType<typeof setTimeout> | null = null;

function getSessionId(): string {
  if (typeof window === 'undefined') return '';
  try {
    let sid = sessionStorage.getItem('rm_session_id');
    if (!sid) {
      sid = `s_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
      sessionStorage.setItem('rm_session_id', sid);
    }
    return sid;
  } catch {
    return `s_${Date.now()}_anon`;
  }
}

function sendBehaviorBatch(batch: BufferedEvent[]) {
  if (typeof window === 'undefined' || batch.length === 0) return;
  const payload = JSON.stringify({ events: batch });

  try {
    if (navigator.sendBeacon) {
      navigator.sendBeacon(`${API_URL}/behavior/track`, new Blob([payload], { type: 'application/json' }));
    } else {
      fetch(`${API_URL}/behavior/track`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: payload,
        keepalive: true,
      }).catch((err) => {
        console.error('Behavior tracking fallback failed:', err);
      });
    }
  } catch {
    console.error('Behavior tracking failed to serialize or dispatch');
  }
}

function flush() {
  if (typeof window === 'undefined' || eventBuffer.length === 0) return;

  const batch = eventBuffer.splice(0, MAX_BATCH);
  sendBehaviorBatch(batch);
}

function scheduleFlush() {
  if (flushTimer) return;
  flushTimer = setTimeout(() => {
    flush();
    flushTimer = null;
    if (eventBuffer.length > 0) scheduleFlush();
  }, FLUSH_INTERVAL);
}

/**
 * Track a user behavior event.
 * Non-blocking, batched, fire-and-forget.
 */
export function trackEvent(eventType: string, eventData?: Record<string, any>, options?: TrackEventOptions) {
  if (typeof window === 'undefined') return;

  const attribution = getAttribution();

  // Also fire to GA4
  try {
    const gtag = (window as any).gtag;
    if (typeof gtag === 'function') {
      gtag('event', eventType, {
        ...(eventData || {}),
        utm_source: attribution.utm_source || '',
        utm_campaign: attribution.utm_campaign || '',
      });
    }
  } catch { /* silent */ }

  const event: BufferedEvent = {
    event_type: eventType,
    event_data: {
      ...(eventData || {}),
      attribution,
    },
    session_id: getSessionId(),
    user_id: getUserId(),
    timestamp: new Date().toISOString(),
  };

  if (options?.immediate) {
    sendBehaviorBatch([event]);
    return;
  }

  eventBuffer.push(event);

  if (eventBuffer.length >= MAX_BATCH) {
    flush();
  } else {
    scheduleFlush();
  }
}

// Flush on page unload
if (typeof window !== 'undefined') {
  window.addEventListener('beforeunload', flush);
  window.addEventListener('pagehide', flush);
}

/* ── Pageview + Conversion Tracking ── */

function getUserId(): string | null {
  return null;
}

export function getVisitorId(): string {
  if (typeof window === 'undefined') return '';
  try {
    let vid = localStorage.getItem('rm_visitor_id');
    if (!vid) {
      vid = `v_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
      localStorage.setItem('rm_visitor_id', vid);
    }
    return vid;
  } catch {
    return `v_${Date.now()}_anon`;
  }
}

function beacon(url: string, body: Record<string, any>) {
  try {
    const payload = JSON.stringify(body);
    if (navigator.sendBeacon) {
      navigator.sendBeacon(url, new Blob([payload], { type: 'application/json' }));
    } else {
      fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: payload, keepalive: true }).catch((err) => {
        console.error('Analytics beacon fallback failed:', err);
      });
    }
  } catch {
    console.error('Analytics beacon failed to serialize or dispatch');
  }
}

function hasSecureContext(): boolean {
  return typeof window !== 'undefined' && window.location.protocol === 'https:';
}

function getCookieValue(name: string): string | null {
  if (typeof document === 'undefined') return null;
  const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
  if (!match) return null;
  try {
    return decodeURIComponent(match[1]);
  } catch {
    return match[1];
  }
}

function setCookieValue(name: string, value: string, maxAgeSeconds = TIKTOK_COOKIE_TTL_SECONDS) {
  if (typeof document === 'undefined') return;
  const secure = hasSecureContext() ? ' Secure;' : '';
  document.cookie = `${name}=${encodeURIComponent(value)}; Path=/; Max-Age=${maxAgeSeconds}; SameSite=Lax;${secure}`;
}

function mergeAttribution(base: Record<string, any>, incoming: Record<string, any>) {
  return Object.fromEntries(
    Object.entries({
      ...base,
      ...Object.fromEntries(Object.entries(incoming).filter(([, value]) => value !== undefined && value !== null && value !== '')),
    }).filter(([, value]) => value !== undefined)
  );
}

export function getAttribution(): Record<string, any> {
  if (typeof window === 'undefined') return {};

  const params = new URLSearchParams(window.location.search);
  const current = {
    src: params.get('src') || params.get('source') || undefined,
    campaign: params.get('campaign') || undefined,
    creative: params.get('creative') || undefined,
    variant: params.get('variant') || undefined,
    click_id: params.get('click_id') || undefined,
    slug: params.get('slug') || undefined,
    exp: params.get('exp') || undefined,
    ad_type: params.get('ad_type') || undefined,
    ttclid: params.get('ttclid') || undefined,
    utm_source: params.get('utm_source') || undefined,
    utm_medium: params.get('utm_medium') || undefined,
    utm_campaign: params.get('utm_campaign') || undefined,
    utm_content: params.get('utm_content') || undefined,
    utm_term: params.get('utm_term') || undefined,
    landing_path: window.location.pathname,
    full_path: `${window.location.pathname}${window.location.search}`,
  };

  try {
    const storedRaw = localStorage.getItem('rm_attribution');
    const stored = storedRaw ? JSON.parse(storedRaw) : null;
    const cookieAttribution = {
      click_id: getCookieValue('rm_click_id') || undefined,
      src: getCookieValue('rm_click_src') || undefined,
      campaign: getCookieValue('rm_click_campaign') || undefined,
      creative: getCookieValue('rm_click_creative') || undefined,
      variant: getCookieValue('rm_click_variant') || undefined,
      slug: getCookieValue('rm_click_slug') || undefined,
      exp: getCookieValue('rm_click_exp') || undefined,
      ad_type: getCookieValue('rm_click_ad_type') || undefined,
      ttclid: getCookieValue('rm_ttclid') || undefined,
    };
    const fallback = mergeAttribution(cookieAttribution, stored || {});

    const hasAttribution = current.src
      || current.campaign
      || current.creative
      || current.variant
      || current.click_id
      || current.slug
      || current.exp
      || current.ad_type
      || current.ttclid
      || current.utm_source
      || current.utm_medium
      || current.utm_campaign
      || current.utm_content
      || current.utm_term;

    if (hasAttribution) {
      const merged = mergeAttribution(fallback, current);
      if (typeof merged.ttclid === 'string' && merged.ttclid) {
        setCookieValue('rm_ttclid', merged.ttclid);
      }
      localStorage.setItem('rm_attribution', JSON.stringify({
        ...merged,
        first_seen_at: stored?.first_seen_at || new Date().toISOString(),
        referrer: stored?.referrer || document.referrer || '',
      }));
      return merged;
    }

    if (stored) return stored;
    return Object.values(cookieAttribution).some(Boolean) ? cookieAttribution : current;
  } catch {
    return current;
  }
}

function getTikTokCookieId(): string | null {
  return getCookieValue('_ttp');
}

function generateEventId(prefix: string): string {
  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
    return `${prefix}_${crypto.randomUUID()}`;
  }
  return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
}

export function buildLeadTrackingPayload(context?: {
  eventId?: string;
  method?: string | null;
  placement?: string | null;
}): LeadTrackingPayload {
  if (typeof window === 'undefined') {
    return {
      consent: false,
      eventId: context?.eventId || generateEventId('rm_signup'),
      method: context?.method || null,
      placement: context?.placement || null,
      referrer: null,
      ttclid: null,
      ttp: null,
      url: null,
    };
  }

  const attribution = getAttribution();

  return {
    consent: hasAnalyticsConsent(),
    eventId: context?.eventId || generateEventId('rm_signup'),
    method: context?.method || null,
    placement: context?.placement || null,
    referrer: document.referrer || null,
    ttclid: typeof attribution.ttclid === 'string' ? attribution.ttclid : null,
    ttp: getTikTokCookieId(),
    url: window.location.href,
  };
}

export function trackPageview(path: string) {
  if (typeof window === 'undefined') return;
  beacon(`${API_URL}/pageviews/track`, {
    path,
    referrer: document.referrer || '',
    visitor_id: getVisitorId(),
    session_id: getSessionId(),
    user_id: getUserId(),
    screen_w: window.innerWidth,
    attribution: getAttribution(),
  });

  if (hasAnalyticsConsent()) {
    const ttq = (window as any).ttq;
    if (ttq && typeof ttq.page === 'function') {
      ttq.page();
    }
  }
}

export function trackConversion(event: string, data?: Record<string, any>) {
  if (typeof window === 'undefined') return;
  beacon(`${API_URL}/pageviews/conversion`, {
    event,
    visitor_id: getVisitorId(),
    session_id: getSessionId(),
    user_id: getUserId(),
    data: {
      ...(data || {}),
      attribution: getAttribution(),
    },
  });

  // Fire GA4 event for key conversions
  fireGA4Event(event, data);
  fireTikTokEvent(event, data);
  fireXEvent(event, data);
}

/**
 * Fire events to GA4 via gtag for funnel visibility in Google Analytics.
 * Maps our internal events to GA4 recommended event names where applicable.
 */
function fireGA4Event(event: string, data?: Record<string, any>) {
  if (typeof window === 'undefined') return;
  const gtag = (window as any).gtag;
  if (typeof gtag !== 'function') return;

  const attribution = getAttribution();

  // Map internal events to GA4 events
  switch (event) {
    case 'signup':
      gtag('event', 'sign_up', {
        method: 'email',
        purchase_intent: data?.purchaseIntent || null,
        utm_source: attribution.utm_source || '',
        utm_campaign: attribution.utm_campaign || '',
        utm_content: attribution.utm_content || '',
      });
      break;

    case 'email_verified':
      gtag('event', 'email_verified', {
        utm_source: attribution.utm_source || '',
        utm_campaign: attribution.utm_campaign || '',
      });
      break;

    case 'forecast_unlock':
      gtag('event', 'forecast_unlock', {
        league: data?.league || '',
        event_id: data?.eventId || '',
        method: data?.method || 'free',
      });
      break;

    case 'checkout_start': {
      const productDefaults = resolvePaidProductTrackingDefaults(data, 'forecast_pass', 'Forecast pass');
      gtag('event', 'add_to_cart', {
        currency: data?.currency ?? 'USD',
        value: typeof data?.value === 'number'
          ? data.value
          : typeof data?.amount === 'number'
            ? data.amount
            : 0,
        items: [{
          item_id: productDefaults.contentId,
          item_name: productDefaults.contentName,
          quantity: typeof data?.quantity === 'number' ? data.quantity : 1,
        }],
      });
      break;
    }

    case 'purchase_complete':
      gtag('event', 'purchase', {
        transaction_id: data?.transactionId || '',
        value: typeof data?.amount === 'number'
          ? data.amount
          : typeof data?.value === 'number'
            ? data.value
            : 0,
        currency: data?.currency ?? 'USD',
        items: [{
          item_name: resolvePaidProductTrackingDefaults(data, 'forecast_pass', 'Forecast purchase').contentName,
        }],
      });
      break;

    case 'checkout_token_created':
      gtag('event', 'begin_checkout', {
        plan: resolvePaidProductTrackingDefaults(data, 'forecast_pass', 'Forecast pass').contentName,
        utm_source: attribution.utm_source || '',
      });
      break;

    case 'landing_primary_cta_click':
      gtag('event', 'cta_click', {
        cta_type: 'primary',
        page: data?.page || window.location.pathname,
        utm_source: attribution.utm_source || '',
        utm_content: attribution.utm_content || '',
      });
      break;

    case 'landing_secondary_cta_click':
      gtag('event', 'cta_click', {
        cta_type: 'secondary',
        page: data?.page || window.location.pathname,
      });
      break;

    case 'signup_intake_complete':
      gtag('event', 'tutorial_complete', {
        favorite_sports: data?.favoriteSports?.join(',') || '',
      });
      break;

    case 'signup_intake_skipped':
      gtag('event', 'tutorial_skip', {});
      break;

    default:
      // Fire generic event for anything else
      gtag('event', event, {
        ...Object.fromEntries(
          Object.entries(data || {}).slice(0, 10).map(([k, v]) =>
            [k, typeof v === 'object' ? JSON.stringify(v) : v]
          )
        ),
      });
      break;
  }
}

function normalizeEmailAddress(value: unknown): string | null {
  if (typeof value !== 'string') return null;
  const normalized = value.trim().toLowerCase();
  return normalized.includes('@') ? normalized : null;
}

function normalizePhoneNumber(value: unknown): string | null {
  if (typeof value !== 'string') return null;
  const normalized = value.replace(/[^\d+]/g, '').trim();
  return normalized.length >= 7 ? normalized : null;
}

function normalizeExternalId(value: unknown): string | null {
  if (typeof value !== 'string') return null;
  const normalized = value.trim();
  return normalized || null;
}

function buildDerivedTikTokEventId(baseId: string, suffix: string): string {
  return `${baseId}_${suffix}`;
}

async function sha256Hex(value: string): Promise<string | null> {
  try {
    if (typeof crypto === 'undefined' || !crypto.subtle) return null;
    const data = new TextEncoder().encode(value);
    const digest = await crypto.subtle.digest('SHA-256', data);
    return Array.from(new Uint8Array(digest))
      .map((byte) => byte.toString(16).padStart(2, '0'))
      .join('');
  } catch {
    return null;
  }
}

async function buildTikTokIdentifyPayload(data?: Record<string, any>) {
  const emailAddress = normalizeEmailAddress(data?.email);
  const phoneNumber = normalizePhoneNumber(data?.phone_number ?? data?.phoneNumber);
  const externalId = normalizeExternalId(data?.external_id ?? data?.externalId ?? data?.userId) || getVisitorId();

  const [hashedEmail, hashedPhoneNumber, hashedExternalId] = await Promise.all([
    emailAddress ? sha256Hex(emailAddress) : Promise.resolve(null),
    phoneNumber ? sha256Hex(phoneNumber) : Promise.resolve(null),
    externalId ? sha256Hex(externalId) : Promise.resolve(null),
  ]);

  return Object.fromEntries(
    Object.entries({
      email: hashedEmail,
      phone_number: hashedPhoneNumber,
      external_id: hashedExternalId,
    }).filter(([, value]) => typeof value === 'string' && value.length > 0)
  );
}

function buildTikTokContents(defaults: {
  contentId: string;
  contentName: string;
  contentType?: string | null;
}, data?: Record<string, any>) {
  return [
    {
      content_id: data?.content_id ?? data?.contentId ?? data?.product ?? data?.plan ?? defaults.contentId,
      content_type: data?.content_type ?? data?.contentType ?? defaults.contentType ?? 'product',
      content_name: data?.content_name ?? data?.contentName ?? defaults.contentName,
    },
  ];
}

function buildTikTokCommonProperties(defaults: {
  contentId: string;
  contentName: string;
  contentType?: string | null;
}, data?: Record<string, any>) {
  const value = typeof data?.value === 'number'
    ? data.value
    : typeof data?.amount === 'number'
      ? data.amount
      : typeof data?.content_value === 'number'
        ? data.content_value
        : null;

  return Object.fromEntries(
    Object.entries({
      contents: buildTikTokContents(defaults, data),
      value,
      currency: data?.currency ?? data?.content_currency ?? 'USD',
      search_string: data?.search_string ?? data?.searchString ?? null,
      status: data?.status ?? null,
    }).filter(([, entry]) => entry !== null && entry !== undefined && entry !== '')
  );
}

async function identifyTikTokUser(ttq: any, data?: Record<string, any>) {
  if (typeof ttq?.identify !== 'function') return;
  const payload = await buildTikTokIdentifyPayload(data);
  if (Object.keys(payload).length === 0) return;
  ttq.identify(payload);
}

function buildXSignupPayload(data?: Record<string, any>) {
  const emailAddress = normalizeEmailAddress(data?.email);
  if (!emailAddress) return null;

  return {
    value: data?.value ?? null,
    contents: [
      {
        content_type: data?.content_type ?? null,
        content_id: data?.content_id ?? null,
        content_name: data?.content_name ?? null,
        content_price: data?.content_price ?? null,
        num_items: data?.num_items ?? null,
        content_group_id: data?.content_group_id ?? null,
      },
    ],
    status: data?.status ?? 'completed',
    conversion_id: data?.conversionId ?? `rm_signup_${getSessionId()}_${Date.now()}`,
    email_address: emailAddress,
    phone_number: data?.phone_number ?? null,
  };
}

function resolvePaidProductTrackingDefaults(
  data: Record<string, any> | undefined,
  fallbackId: string,
  fallbackName: string,
) {
  const productKey = typeof data?.product === 'string'
    ? data.product
    : typeof data?.plan === 'string'
      ? data.plan
      : typeof data?.productType === 'string'
        ? data.productType
        : null;
  const productMeta = getPaidProductMetadata(productKey);

  return {
    contentId: data?.content_id ?? data?.contentId ?? productMeta?.contentId ?? productKey ?? fallbackId,
    contentName: data?.content_name ?? data?.contentName ?? productMeta?.contentName ?? productKey ?? fallbackName,
    contentType: data?.content_type ?? data?.contentType ?? 'product',
  };
}

async function dispatchTikTokEvent(event: string, data?: Record<string, any>) {
  if (typeof window === 'undefined' || !hasAnalyticsConsent()) return;

  const ttq = (window as any).ttq;
  if (!ttq || typeof ttq.track !== 'function') return;

  const eventId = typeof data?.eventId === 'string'
    ? data.eventId
    : typeof data?.conversionId === 'string'
      ? data.conversionId
      : generateEventId('rm_tiktok');

  switch (event) {
    case 'signup':
    case 'quick_signup': {
      await identifyTikTokUser(ttq, data);
      ttq.track('SubmitForm', {
        ...buildTikTokCommonProperties({
          contentId: data?.content_id ?? 'rainmaker_signup_form',
          contentName: 'Rainmaker Sports signup form',
          contentType: 'lead',
        }, data),
      }, {
        event_id: buildDerivedTikTokEventId(eventId, 'submit_form'),
      });
      ttq.track('CompleteRegistration', {
        ...buildTikTokCommonProperties({
          contentId: data?.content_id ?? 'rainmaker_account',
          contentName: 'Rainmaker Sports account',
          contentType: 'product',
        }, data),
      }, {
        event_id: buildDerivedTikTokEventId(eventId, 'complete_registration'),
      });
      break;
    }
    case 'forecast_unlock': {
      ttq.track('ViewContent', {
        ...buildTikTokCommonProperties({
          contentId: data?.eventId ?? 'forecast_unlock',
          contentName: data?.content_name ?? `Rainmaker forecast${data?.league ? ` ${String(data.league).toUpperCase()}` : ''}`,
          contentType: 'product',
        }, data),
      }, {
        event_id: eventId,
      });
      break;
    }
    case 'checkout_start': {
      await identifyTikTokUser(ttq, data);
      ttq.track('AddToCart', {
        ...buildTikTokCommonProperties(
          resolvePaidProductTrackingDefaults(data, 'forecast_pass', 'Forecast pass'),
          data,
        ),
      }, {
        event_id: eventId,
      });
      break;
    }
    case 'checkout_token_created': {
      await identifyTikTokUser(ttq, data);
      ttq.track('InitiateCheckout', {
        ...buildTikTokCommonProperties(
          resolvePaidProductTrackingDefaults(data, 'forecast_pass', 'Forecast pass'),
          data,
        ),
      }, {
        event_id: eventId,
      });
      break;
    }
    case 'purchase_complete': {
      await identifyTikTokUser(ttq, data);
      const purchaseDefaults = resolvePaidProductTrackingDefaults(data, 'forecast_pass', 'Forecast purchase');
      ttq.track('Purchase', {
        ...buildTikTokCommonProperties(
          {
            ...purchaseDefaults,
            contentId: data?.content_id ?? data?.contentId ?? data?.invoiceNumber ?? data?.transactionId ?? purchaseDefaults.contentId,
          },
          data,
        ),
      }, {
        event_id: eventId,
      });
      break;
    }
    default:
      break;
  }
}

function fireTikTokEvent(event: string, data?: Record<string, any>) {
  void dispatchTikTokEvent(event, data);
}

function fireXEvent(event: string, data?: Record<string, any>) {
  if (typeof window === 'undefined' || !hasAnalyticsConsent()) return;

  const twq = (window as any).twq;
  if (typeof twq !== 'function') return;

  switch (event) {
    case 'signup':
    case 'quick_signup': {
      const payload = buildXSignupPayload(data);
      if (!payload) return;
      twq('event', X_SIGNUP_EVENT_ID, payload);
      break;
    }
    default:
      break;
  }
}
