import { useEffect, useLayoutEffect } from 'react';

import type ApiError from '~/lib/errors/ApiError';
import type { AppPage, AppPageContext } from '~/src/components/NextApp/types';
import type { State } from '~/src/store/types';

import { toDecodedItemPath } from '~/lib/router2/utils';
import { ItemTypes } from '~/lib/types';
import { toDraftItemPagePath } from '~/src/components/ItemPage/utils';
import { useAppLoading } from '~/src/components/NextApp/lib/CoreUi';
import PageError from '~/src/components/PageError';
import PagePlaceholder from '~/src/components/PagePlaceholder';
import { useI18n, withI18n } from '~/src/lib/i18n';
import { useAppRouter } from '~/src/lib/router2';
import { reportError } from '~/src/lib/sentry';
import addServerTimingHeader from '~/src/lib/utils/addServerTimingHeader';
import { fetchItemByPathAction } from '~/src/store/paths/actions';
import {
  selectItemByPathGlobal,
  selectPathError,
  selectPathIsLoading,
  selectPathValue,
} from '~/src/store/paths/selectors';
import { useDispatch, useSelector } from '~/src/store/redux';
import AlbumPage from '~/src/views/AlbumPage';
import CustomPage from '~/src/views/CustomPage';
import { PrereleasePage } from '~/src/views/PrereleasePage';
import TrackPage from '~/src/views/TrackPage';

interface ItemPageNextPageProps {
  itemPath: string;
}

const ItemPageNextPage: AppPage<ItemPageNextPageProps> = ({
  itemPath,
}: ItemPageNextPageProps) => {
  const { t } = useI18n('app');
  const dispatch = useDispatch();
  const appRouter = useAppRouter();

  // show the app global loading bar when item in 'loading' state
  const setIsAppLoading = useAppLoading();

  const item = useSelector((state: State) =>
    selectItemByPathGlobal(state, itemPath)
  );

  // redirect to draft page asap
  // REVIEW: using client-side redirect here leads to a flash of error state when page
  // accesses via direct live page url (rare case), as we do not aware if user is logged in or not during SSR.
  // We cannot add token to server side request easily right now because of edge caching.
  useLayoutEffect(() => {
    if (!item || !('isDraft' in item)) return;

    if (item.isDraft) {
      appRouter.replace(toDraftItemPagePath(item.pagePath));
    }
  }, [item]);

  const pathIsLoading = useSelector((state: State) =>
    selectPathIsLoading(state, itemPath)
  );

  const pathValue = useSelector((state: State) =>
    selectPathValue(state, itemPath)
  );

  const pathError = useSelector((state: State) =>
    selectPathError(state, itemPath)
  );

  useEffect(() => {
    setIsAppLoading(pathIsLoading);
  }, [pathIsLoading, setIsAppLoading]);

  useEffect(() => {
    dispatch(
      fetchItemByPathAction({
        path: itemPath,
        silent: true,
      })
    );
  }, [dispatch, itemPath]);

  if (!pathValue || !item) {
    return (
      <PagePlaceholder
        isLoading={pathIsLoading}
        error={pathError}
        toErrorText={({ status }) => {
          if (status === 404) {
            return t('errors.item');
          }
        }}
      />
    );
  }

  switch (item.type) {
    case ItemTypes.ALBUM:
      return <AlbumPage albumId={pathValue.id} />;
    case ItemTypes.TRACK:
      return <TrackPage trackId={pathValue.id} />;
    case ItemTypes.CUSTOM_PAGE:
      return <CustomPage customPageId={pathValue.id} />;
    case ItemTypes.PRERELEASE:
      return <PrereleasePage prerelease={item} />;
    default:
      return <PageError error={{ message: 'Unsupported page type' }} />;
  }
};

ItemPageNextPage.getInitialProps = async (ctx) => {
  const {
    res,
    query,
    reduxStore: { dispatch, getState },
    asPath,
    pathname,
  } = ctx;

  // the fully DECODED path as stored in the songwhip-api database
  const itemPath = toDecodedItemPath(query);

  // is server
  if (typeof window === 'undefined') {
    const timingStart = Date.now();

    const { serverRedirectIfNeeded } = await import(
      '~/src/lib/router2/redirectIfNeeded/serverRedirectIfNeeded'
    );

    if (res) {
      try {
        // SSR: block render until data fetched and report errors
        const result = await dispatch(
          fetchItemByPathAction({
            path: itemPath,
            // Throw errors on server side to trigger try-catch below.
            silent: false,
          })
        );

        if (result) {
          res.setHeader(
            'x-songwhip-resolve-path-from-api-cache',
            result.fromApiCache ? 'true' : 'false'
          );
        }
      } catch (error) {
        await onServerFetchError({ error, ctx });

        return {
          itemPath,
        };
      }

      const state = getState();
      const item = selectItemByPathGlobal(state, itemPath)!;

      addServerTimingHeader(
        res,
        'songwhip-web-ssr-resolve-path',
        Date.now() - timingStart
      );

      serverRedirectIfNeeded({
        res,
        state,
        expectedPagePath: item.pagePath,
        currentAsPath: asPath!,
        currentRoutePathname: pathname,
        pageOwnedByAccountIds: item.ownedByAccountIds,
      });
    }
  }

  return {
    itemPath,
  };
};

const onServerFetchError = async ({
  error,
  ctx,
}: {
  error: ApiError;
  ctx: AppPageContext;
}) => {
  const { res } = ctx;

  // add the correct status-code, this is super important as returning
  // a non-error code means that the response will be cached upstream
  if (res) {
    res.statusCode = error.status || 500;
  }

  // don't report 404s (too noisy)
  if (error.status === 404) {
    return;
  }

  await reportError({
    error,
    nextCtx: ctx,

    extras: {
      caughtAt: 'pages/[param1]/[param2]:onServerFetchError',
    },
  });
};

const ItemNextPageWithI18n = withI18n(ItemPageNextPage, [
  'item',
  'prerelease',
  'form',
]);

export default ItemNextPageWithI18n;
