import { createContext, useContext, useEffect, useMemo } from 'react';

import type {
  SongwhipEvent,
  SongwhipEventContext,
} from '@theorchard/songwhip-events';
import type { FC, ReactNode } from 'react';

import { setClientCookie } from '~/lib/utils/cookie';
import useOneTrust from '~/src/components/OneTrust/useOneTrust';
import useFetchSessionUser, {
  pickFirstUserAccount,
} from '~/src/hooks/useFetchSessionUser';
import { useSelector, useStore } from '~/src/store/redux';
import {
  selectSessionUtmParams,
  selectUser,
  selectUserCountry,
  selectUserLanguage,
} from '~/src/store/session/selectors';
import { trackClientEventLazy } from '.';
import { CLIENT_ID_COOKIE_NAME, getClientId } from './cookies';

export type TrackEvent = (
  event: SongwhipEvent,
  context?: Partial<SongwhipEventContext>
) => Promise<void>;

interface TrackingContextValue {
  trackEvent: TrackEvent;
  baseContext?: Partial<SongwhipEventContext> | null;
}

export const TrackerContext = createContext<TrackingContextValue>({
  trackEvent: async () => {
    // eslint-disable-next-line no-console
    console.error('no <TrackingProvider> in scope');
  },
  baseContext: {},
});

export interface TrackerProviderParams {
  children: ReactNode;
  baseContext?: Partial<SongwhipEventContext>;
}

export const TrackerProvider: FC<TrackerProviderParams> = ({
  baseContext,
  children,
}) => {
  const { baseContext: inheritedBaseContext } = useContext(TrackerContext);
  const { initialResolvedUser } = useFetchSessionUser();
  const utmParams = useSelector(selectSessionUtmParams);
  const getOneTrustConfig = useOneTrust();
  const { getState } = useStore();

  useEffect(() => {
    const isAnalyticsEnabled = getOneTrustConfig().analyticsEnabled;
    const clientId = getClientId().clientId;

    // change clientId cookie ttl to session if analytics is disabled
    // we still can track user events but must not identify user across sessions
    setClientCookie(
      CLIENT_ID_COOKIE_NAME,
      clientId,
      isAnalyticsEnabled ? undefined : 0
    );
  }, [getOneTrustConfig]);

  return (
    <TrackerContext.Provider
      value={useMemo(
        (): TrackingContextValue => ({
          baseContext,

          trackEvent: async (
            event: SongwhipEvent,
            context?: SongwhipEventContext
          ) => {
            // Get the current User from the store JUST before tracking the event
            // as the store may just have been updated before calling trackEvent().
            // Using a hook outside this closure can result in stale objects.
            const user = selectUser(getState());
            const language = selectUserLanguage(getState());
            const country = selectUserCountry(getState());
            const account = pickFirstUserAccount(user?.accounts);

            // Don't trigger any events until initial logged in User has been fetched otherwise
            // the first page-view event might be missing user params (race-condition).
            const initialUser = await initialResolvedUser;

            const params = {
              // Always use User from the redux store when defined as
              // this is the source-of-truth. But this may not be defined
              // if trackEvent() was called before the initial user request completes.
              user: user || initialUser,

              event,

              context: {
                language,

                accountId: account?.id,
                accountName: account?.name ?? undefined,

                userCountry: country,

                ...utmParams,

                ...inheritedBaseContext,
                ...baseContext,
                ...context,
              },
            };

            return trackClientEventLazy(params);
          },
        }),
        [baseContext]
      )}
    >
      {children}
    </TrackerContext.Provider>
  );
};

export const useTracker = () => {
  return useContext(TrackerContext);
};
