import { useAuth } from '@/profile/auth/context';
import * as Crypto from 'expo-crypto';
import * as SecureStore from 'expo-secure-store';
import { ReactNode, RefObject, createContext, useEffect, useMemo, useRef, useState } from 'react';
import { initTelemetry } from './index';
import { MonitorEvent, StatsigProvider, useStatsigClient } from './statsig';

initTelemetry();

interface MonitorContextType {
  logEvent: (event: MonitorEvent) => void;
}

const noopContext: MonitorContextType = { logEvent: () => {} };
const MonitorContext = createContext<MonitorContextType>(noopContext);
const ANON_ID_KEY = 'palace_anonymous_id';

function MonitorInner({
  children,
  eventQueueRef,
}: {
  children: ReactNode;
  eventQueueRef: RefObject<MonitorEvent[]>;
}) {
  const { client } = useStatsigClient();
  const flushed = useRef(false);

  useEffect(() => {
    const queue = eventQueueRef.current;
    if (!flushed.current && queue && queue.length > 0) {
      flushed.current = true;
      for (const event of queue) {
        client.logEvent(event);
      }
      queue.splice(0, queue.length);
    }
  }, [client, eventQueueRef]);

  const value = useMemo<MonitorContextType>(
    () => ({
      logEvent: (event: MonitorEvent) => client.logEvent(event),
    }),
    [client]
  );

  return <MonitorContext.Provider value={value}>{children}</MonitorContext.Provider>;
}

export function MonitorProvider({ children }: { children: ReactNode }) {
  const { user } = useAuth();
  const [anonId, setAnonId] = useState<string | null>(null);
  const eventQueue = useRef<MonitorEvent[]>([]);

  useEffect(() => {
    if (user) return;
    let mounted = true;
    (async () => {
      let id = await SecureStore.getItemAsync(ANON_ID_KEY);
      if (!id) {
        id = Crypto.randomUUID();
        await SecureStore.setItemAsync(ANON_ID_KEY, id);
      }
      if (mounted) setAnonId(id);
    })();
    return () => {
      mounted = false;
    };
  }, [user]);

  const monitorUser = useMemo(() => {
    if (user) return { id: user.id, isAnonymous: false };
    if (anonId) return { id: anonId, isAnonymous: true };
    return null;
  }, [user, anonId]);

  const bufferingContext = useMemo<MonitorContextType>(
    () => ({
      logEvent: (event: MonitorEvent) => eventQueue.current.push(event),
    }),
    []
  );

  if (!monitorUser) {
    return <MonitorContext.Provider value={bufferingContext}>{children}</MonitorContext.Provider>;
  }

  return (
    <StatsigProvider user={monitorUser}>
      <MonitorInner eventQueueRef={eventQueue}>{children}</MonitorInner>
    </StatsigProvider>
  );
}
