import { CallbackOptions, loadStytch, LoginOrSignupView, StyleConfig, StytchClient } from '@stytch/stytch-js';
import React, { useEffect, useState } from 'react';

import { useAsyncState } from 'utils/async';
import { invariant } from 'utils/invariant';

import { useIsMounted__INTERNAL, useStytchRaw__INTERNAL } from './StytchContext';

export interface StytchProps {
  publicToken?: string;
  loginOrSignupView?: LoginOrSignupView;
  style?: StyleConfig;
  callbacks?: CallbackOptions;
}

/**
 * Returns a unique element ID.
 * Client-side only - will return null for server-side
 * resolves: https://stytch.slack.com/archives/C015UDB4X33/p1641450131000800
 * based on: https://github.com/vercel/next.js/issues/7322#issuecomment-968858477
 */
const useUniqueElementId = (): string | null => {
  const [elementId, setElementId] = useState<string | null>(null);
  useEffect(() => {
    const randId = Math.floor(Math.random() * 1e6);
    setElementId(`stytch-magic-link-${randId}`);
  }, []);
  return elementId;
};

/**
 * Stytch JS React Component
 *
 * [Documentation](https://stytch.com/docs/javascript-sdk)
 */
export const Stytch = ({ publicToken, style, callbacks, loginOrSignupView }: StytchProps) => {
  const stytchClientFromContext = useStytchRaw__INTERNAL();
  const stytchMounted = useIsMounted__INTERNAL();
  const [stytchClient, setStytchClient] = useAsyncState<StytchClient | null>(() => {
    // If StytchProvider has been mounted, use context value
    if (stytchMounted) {
      return stytchClientFromContext;
    }
    // If StytchProvider isn't used, publicToken must be provided
    invariant(
      publicToken,
      'The Stytch component must either be inside a <StytchProvider> or provided the publicToken prop.',
    );
    // If Stytch has already been loaded, use global value
    if (typeof window !== undefined && window.Stytch) {
      return window.Stytch(publicToken);
    }
    // Otherwise, we will load Stytch
    return null;
  });
  const elementId = useUniqueElementId();

  useEffect(() => {
    if (!stytchClient) {
      if (!stytchMounted) {
        loadStytch().then((globalStytch) => globalStytch && setStytchClient(globalStytch(publicToken)));
      } else if (stytchClientFromContext) {
        setStytchClient(stytchClientFromContext);
      }
    }
  }, [stytchClient, stytchClientFromContext, stytchMounted, publicToken, setStytchClient]);

  useEffect(() => {
    if (!stytchClient || !elementId) {
      return;
    }

    stytchClient.mount({
      callbacks,
      elementId: `#${elementId}`,
      loginOrSignupView,
      style,
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [elementId, stytchClient]);

  return elementId ? <div id={elementId}></div> : null;
};
