/**
 * Telemetry module — React context + hook for event tracking in React Native apps.
 * Maps queued events to platform-service TelemetryEventSchema for POST /telemetry/events.
 */

import React, { createContext, useContext, useCallback, useRef, useEffect } from 'react';
import { Platform } from 'react-native';
import type { PlatformSDK } from '../core.js';

export interface TelemetryEvent {
  name: string;
  properties?: Record<string, unknown>;
  timestamp?: string;
}

export interface TelemetryConfig {
  /** Flush interval in ms (default: 30000) */
  flushInterval?: number;
  /** Max batch size before auto-flush (default: 20) */
  maxBatchSize?: number;
  appVersion?: string;
  buildNumber?: string;
  /** Default: beta */
  releaseChannel?: 'dev' | 'beta' | 'prod';
  /** Stable anonymous id (e.g. from MMKV). If omitted, generated per app session. */
  getInstallId?: () => string;
}

function randomUuid(): 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);
  });
}

interface TelemetryContextType {
  track: (name: string, properties?: Record<string, unknown>) => void;
  flush: () => Promise<void>;
}

const TelemetryContext = createContext<TelemetryContextType | null>(null);

export function useTelemetry(): TelemetryContextType {
  const ctx = useContext(TelemetryContext);
  if (!ctx) throw new Error('useTelemetry must be used within a TelemetryProvider');
  return ctx;
}

interface TelemetryProviderProps {
  sdk: PlatformSDK;
  config?: TelemetryConfig;
  children: React.ReactNode;
}

export function TelemetryProvider({
  sdk,
  config,
  children,
}: TelemetryProviderProps): React.JSX.Element {
  const queue = useRef<TelemetryEvent[]>([]);
  const sessionIdRef = useRef(randomUuid());
  const installIdRef = useRef<string | null>(null);
  const flushInterval = config?.flushInterval ?? 30_000;
  const maxBatchSize = config?.maxBatchSize ?? 20;

  const resolveInstallId = useCallback((): string => {
    if (!installIdRef.current) {
      installIdRef.current = config?.getInstallId?.() ?? randomUuid();
    }
    return installIdRef.current;
  }, [config?.getInstallId]);

  const flush = useCallback(async () => {
    if (queue.current.length === 0) return;
    const batch = queue.current.splice(0);
    const os = Platform.OS;
    const platform = os === 'ios' ? 'ios' : os === 'android' ? 'android' : 'web';
    const osFamily = platform === 'ios' ? 'ios' : platform === 'android' ? 'android' : 'other';
    const events = batch.map(e => ({
      id: randomUuid(),
      productId: sdk.config.productId,
      anonymousInstallId: resolveInstallId(),
      sessionId: sessionIdRef.current,
      platform,
      channel: 'mobile_app' as const,
      osFamily,
      osVersion: String(Platform.Version ?? ''),
      appVersion: config?.appVersion ?? '0.0.0',
      buildNumber: config?.buildNumber ?? '0',
      releaseChannel: config?.releaseChannel ?? ('beta' as const),
      eventType: 'info' as const,
      module: 'app',
      eventName: e.name,
      occurredAt: e.timestamp ?? new Date().toISOString(),
      context: e.properties,
    }));
    try {
      await sdk.fetch('/telemetry/events', {
        method: 'POST',
        body: JSON.stringify({ productId: sdk.config.productId, events }),
      });
    } catch {
      queue.current.unshift(...batch);
    }
  }, [sdk, config?.appVersion, config?.buildNumber, config?.releaseChannel, resolveInstallId]);

  const track = useCallback(
    (name: string, properties?: Record<string, unknown>) => {
      queue.current.push({
        name,
        properties,
        timestamp: new Date().toISOString(),
      });
      if (queue.current.length >= maxBatchSize) {
        flush();
      }
    },
    [maxBatchSize, flush]
  );

  useEffect(() => {
    const id = setInterval(flush, flushInterval);
    return () => {
      clearInterval(id);
      flush();
    };
  }, [flush, flushInterval]);

  const value: TelemetryContextType = { track, flush };
  return React.createElement(TelemetryContext.Provider, { value }, children);
}
