import { memo, useRef } from 'react';
import dynamic from 'next/dynamic';
import Debug from 'debug';

import type { OrchardBrands } from '~/lib/songwhipApi/types';
import type { FC, ReactNode } from 'react';
import type { ItemPageLayoutProps } from './layouts/types';
import type { ItemContext } from './types';

import useFetchSessionUser from '~/src/hooks/useFetchSessionUser';
import { useI18n } from '~/src/lib/i18n';
import { useRedirectIfNeeded } from '~/src/lib/router2/redirectIfNeeded/useRedirectIfNeeded';
import { TrackerProvider } from '~/src/lib/tracker/useTracker';
import PageError from '../PageError';
import PageLoading from '../PageLoading';
import { useItemPageDialog } from './components/ItemPageDialog';
import ItemPageGate from './components/ItemPageGate';
import { PageThemeProvider } from './hooks/theme';
import useCopyShareUrl from './hooks/useCopyShareUrl';
import useCustomTracking from './hooks/useCustomTracking';
import { withEmitter } from './hooks/useEmitter';
import useOnCopyGesture from './hooks/useOnCopyGesture';
import { ItemPageContext } from './ItemPageContext';
import ItemPageLayout from './layouts';
import { toShareUrl } from './utils';

const debug = Debug('songwhip/components/ItemPage');

const OneTrustLazy = dynamic(() => import('../OneTrust'), { ssr: false });

const ItemPageDialogLazy = dynamic(
  () => import('./components/ItemPageDialog'),
  {
    ssr: false,
  }
);

export interface ItemPageProps
  extends Omit<ItemPageLayoutProps, 'settings' | 'contentRootRef'> {
  /**
   * Indicate whether there is enough content to render.
   *
   * If `false` and `isLoading: false` a loading spinner will
   * be shown. If `true` and `isLoading: true` then we can show
   * content and load in the background (eg. in refresh/localize case).
   *
   * This is also used internally infer decide whether to trigger
   * the 'page-view' tracking event. We don't want to trigger the
   * `page-view` if we haven't got data that needs to be sent with
   * the event.
   */
  hasContent: boolean;
  isLoading: boolean;
  isOwned: boolean | undefined;
  isDraft?: boolean;
  pageBrand?: OrchardBrands;
  pageOwnedByAccountIds: number[] | undefined;
  error?: Error;
  pagePath: string;
  itemContext: ItemContext;
  testId?: string;
  withRedirectIfNeeded?: boolean;
  children?: ReactNode;
}

const ItemPage: FC<ItemPageProps> = ({
  withRedirectIfNeeded,
  pageOwnedByAccountIds,
  pagePath,
  isOwned,
  isDraft,
  pageBrand,
  itemContext,
  children,
  isLoading,
  hasContent,
  error,
  ...itemPageLayoutProps
}) => {
  debug('render');

  const { isDialogOpened } = useItemPageDialog();
  const contentRootRef = useRef<HTMLDivElement>(null);
  const shareUrl = toShareUrl({ isOwned, isDraft, pageBrand });
  const { isLoggedIn } = useFetchSessionUser();
  const { t } = useI18n('app');

  const copyShareUrl = useCopyShareUrl(shareUrl);

  useCustomTracking(itemContext);

  useOnCopyGesture({
    nodeRef: contentRootRef,
    callback: copyShareUrl,
  });

  if (withRedirectIfNeeded) {
    // eslint-disable-next-line react-hooks/rules-of-hooks
    useRedirectIfNeeded({
      pagePath,
      pageOwnedByAccountIds,
    });
  }

  // only show loading spinner if the don't have any content to show
  // WARN: if we show the loading spinner when there *is* content on the page
  // we can end up wiping out the UI whenever redux regards the Item as being in
  // a "loading" state. This can end up closing dialog boxes etc. An example of
  // this is the EditDetailsDialog on the AlbumPage, we don't want to wipe out
  // the dialog box when the user is saving changes to the album.
  if (isLoading && !hasContent) {
    return <PageLoading />;
  }

  // only show the error if we don't have any useful content to show
  if (error && !hasContent) {
    return (
      <PageError
        error={error}
        toText={({ status }) => {
          if (status === 404) {
            return t('errors.item');
          }
        }}
      />
    );
  }

  return (
    <>
      <ItemPageGate>
        <ItemPageLayout
          contentRootRef={contentRootRef}
          settings={itemContext.layout.settings}
          {...itemPageLayoutProps}
        />
        {children}
      </ItemPageGate>
      {isDialogOpened && <ItemPageDialogLazy />}
      {!isLoggedIn && <OneTrustLazy sendConsentEvent={isOwned} />}
    </>
  );
};

const ItemPageMemo = memo(
  withEmitter((props) => {
    const { itemContext, pageType, trackerContext } = props;

    return (
      <TrackerProvider baseContext={{ ...trackerContext, pageType }}>
        <ItemPageContext.Provider value={itemContext}>
          <PageThemeProvider itemContext={itemContext}>
            <ItemPage {...props} />
          </PageThemeProvider>
        </ItemPageContext.Provider>
      </TrackerProvider>
    );
  })
) as FC<ItemPageProps>;

export default ItemPageMemo;
