/**
 * Browser/React Native-safe telemetry client for platform-service.
 *
 * Replaces hand-rolled telemetry clients in ChronoMind web, NomGap, and LysnrAI user-dashboard.
 * No Node.js dependencies — uses globalThis.fetch and configurable storage.
 *
 * @example
 * ```ts
 * import { createTelemetryClient } from '@bytelyst/telemetry-client';
 *
 * const telemetry = createTelemetryClient({
 *   productId: 'chronomind',
 *   baseUrl: 'http://localhost:4003/api',
 *   platform: 'web',
 *   channel: 'pwa',
 *   transport: 'beacon',
 * });
 *
 * telemetry.init();
 * telemetry.trackEvent('info', 'timer', 'timer_created');
 * ```
 */

import type {
  TelemetryClient,
  TelemetryClientConfig,
  TelemetryEvent,
  TelemetryStorage,
} from './types.js';

// ── UUID helper (browser + RN safe) ──────────────────────────────

function uuid(): string {
  if (typeof globalThis.crypto?.randomUUID === 'function') {
    return globalThis.crypto.randomUUID();
  }
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
    const r = (Math.random() * 16) | 0;
    return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
  });
}

// ── Noop storage ─────────────────────────────────────────────────

const noopStorage: TelemetryStorage = {
  getItem: () => null,
  setItem: () => {},
};

function getDefaultStorage(): TelemetryStorage {
  if (
    typeof globalThis.localStorage !== 'undefined' &&
    typeof globalThis.localStorage?.getItem === 'function'
  ) {
    return globalThis.localStorage;
  }
  return noopStorage;
}

// ── Factory ──────────────────────────────────────────────────────

export function createTelemetryClient(config: TelemetryClientConfig): TelemetryClient {
  const {
    productId,
    baseUrl,
    endpoint = '/telemetry/events',
    platform,
    channel,
    transport = 'fetch',
    maxQueue = 50,
    flushIntervalMs = 30_000,
    appVersion = '0.0.0',
    buildNumber = '0',
    releaseChannel = 'dev',
    osFamily = 'other',
    osVersion = '',
  } = config;

  const storage = config.storage ?? getDefaultStorage();
  const INSTALL_KEY = `${productId}_telemetry_install_id`;

  let queue: TelemetryEvent[] = [];
  let sessionId = '';
  let installId = '';
  let flushTimer: ReturnType<typeof setInterval> | null = null;

  function getInstallId(): string {
    if (installId) return installId;
    const stored = storage.getItem(INSTALL_KEY);
    if (stored) {
      installId = stored;
      return installId;
    }
    installId = uuid();
    storage.setItem(INSTALL_KEY, installId);
    return installId;
  }

  function getSessionId(): string {
    return sessionId;
  }

  function flushViaBeacon(): void {
    if (queue.length === 0) return;
    const events = [...queue];
    queue = [];

    const body = JSON.stringify({ productId, events });
    const url = `${baseUrl}${endpoint}`;

    try {
      const sent = typeof navigator?.sendBeacon === 'function' && navigator.sendBeacon(url, body);
      if (!sent) {
        // Fallback to fetch
        globalThis
          .fetch(url, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
              'x-product-id': productId,
              'x-request-id': uuid(),
            },
            body,
            keepalive: true,
          })
          .catch(() => {});
      }
    } catch {
      // Silently ignore telemetry failures
    }
  }

  function flushViaFetch(): void {
    if (queue.length === 0) return;
    const events = [...queue];
    queue = [];

    const body = JSON.stringify({ productId, events });
    const url = `${baseUrl}${endpoint}`;

    globalThis
      .fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'x-product-id': productId,
          'x-request-id': uuid(),
        },
        body,
      })
      .catch(() => {});
  }

  function flush(): void {
    if (transport === 'beacon') {
      flushViaBeacon();
    } else {
      flushViaFetch();
    }
  }

  function trackEvent(
    eventType: string,
    module: string,
    eventName: string,
    extra?: {
      feature?: string;
      message?: string;
      tags?: Record<string, string>;
      metrics?: Record<string, number>;
      userId?: string;
    }
  ): void {
    const event: TelemetryEvent = {
      id: uuid(),
      productId,
      anonymousInstallId: getInstallId(),
      sessionId,
      platform,
      channel,
      osFamily,
      osVersion,
      appVersion,
      buildNumber,
      releaseChannel,
      eventType,
      module,
      eventName,
      ...extra,
      occurredAt: new Date().toISOString(),
    };

    queue.push(event);

    if (queue.length >= maxQueue) {
      flush();
    }
  }

  function init(): void {
    sessionId = uuid();
    getInstallId();

    // Auto-flush on visibility change (web only)
    if (typeof document !== 'undefined') {
      document.addEventListener('visibilitychange', () => {
        if (document.visibilityState === 'hidden') {
          flush();
        }
      });
    }

    // Periodic flush
    if (flushTimer) clearInterval(flushTimer);
    flushTimer = setInterval(flush, flushIntervalMs);

    trackEvent('info', 'app_lifecycle', 'session_started');
  }

  function shutdown(): void {
    flush();
    if (flushTimer) {
      clearInterval(flushTimer);
      flushTimer = null;
    }
  }

  return {
    init,
    trackEvent,
    flush,
    shutdown,
    getInstallId,
    getSessionId,
  };
}
