import { useCallback, useMemo, useState } from 'react';
import dynamic from 'next/dynamic';

import type { ServiceTypes } from '~/lib/types';
import type { ClickableOnClick } from '~/src/components/Clickable';
import type { PageSectionComponent } from '../types';
import type { PresaveButtonsProps, SendMarketingConsent } from './types';

import { toDate } from '~/lib/utils/date';
import Box from '~/src/components/Box';
import { useOpenDialog } from '~/src/components/DialogBoxWithStages';
import useHash from '~/src/hooks/useHash';
import {
  getMarketingConsentPayload,
  getMarketingConsentType,
  isCountryRequireFunctionalEmailControl,
  isCountryWithLockedAuth,
  isMarketingConsentRequired,
} from '~/src/lib/privacy';
import { reportErrorClient } from '~/src/lib/sentry/client';
import { sendMarketingConsentApi } from '~/src/lib/songwhipApi/orchard';
import { getClientId } from '~/src/lib/tracker/cookies';
import { useSelector } from '~/src/store/redux';
import { selectUserCountry } from '~/src/store/session/selectors';
import { getItemPageDialogUrl } from '../../components/ItemPageDialog';
import { LegalFootnote } from '../../components/LegalFootnote';
import LegalGate from '../../components/LegalGate';
import { useEmitter } from '../../hooks/useEmitter';
import { resolveLink } from '../lib';
import SectionTitle from '../lib/SectionTitle';
import ServiceButton from '../lib/ServiceButton';
import { SERVICES_WITH_PRESAVE } from './constants';
import {
  PresaveDialogNotification,
  PresaveDialogStage,
} from './PresaveDialog/types';
import { toNotificationType } from './PresaveDialog/utils';
import { PresaveNextStep } from './types';
import { useDefaultItems } from './useDefaultItems';
import { useServicePresave } from './useServicePresave';
import { getIsEmailFirstPresave, sortPresaveReleaseItems } from './utils';

const PresaveDialogLazy = dynamic({
  loader: () => import('./PresaveDialog'),
  ssr: false,
});

const PresaveButtons2: PageSectionComponent<PresaveButtonsProps> = ({
  title: defaultTitle,
  items: initialItems,
  withColoredIcons,
  withAlphabeticalSort,
  withEmailAndOptInsBeforePresave = false,
  sectionPath,
  layoutData,
  territoryOverrides,
}) => {
  const { item } = layoutData;

  if (item.type !== 'prerelease' && item.type !== 'album') {
    throw new Error(
      `PresaveButtons2 component expects item type to be 'prerelease' or 'album', got '${item.type}'`
    );
  }

  const presaveDialogId = `presaveDialog:${sectionPath}`;

  const [isPresaveDialogOpened, openPresaveDialog] =
    useOpenDialog(presaveDialogId);

  const defaultItems = useDefaultItems(initialItems);
  const userCountry = useSelector(selectUserCountry);
  const consentType = getMarketingConsentType(userCountry);
  const { backToBeforeFirstHash } = useHash();
  const emitter = useEmitter();

  const [presaveGate, setPresaveGate] = useState({
    isLocked: isCountryWithLockedAuth(userCountry),
    hasError: false,
  });

  const title = territoryOverrides?.[userCountry]?.title || defaultTitle;
  const items = territoryOverrides?.[userCountry]?.items || defaultItems;

  const sortedItems = useMemo(
    () =>
      withAlphabeticalSort
        ? [...items].sort(sortPresaveReleaseItems(layoutData))
        : items,
    [items, withAlphabeticalSort]
  );

  const isEmailFirstPresave = (serviceType?: ServiceTypes): boolean =>
    getIsEmailFirstPresave({
      serviceType,
      userCountry,
      isEmailCollectionBefore: withEmailAndOptInsBeforePresave,
    });

  // in some cases we send this request in optimistic way
  // so we do not want to add any additional requests, like
  // songwhip event, in this function as it can be lost
  const sendMarketingConsent = useCallback<SendMarketingConsent>(
    async ({
      correlationId,
      formValues,
      userMarketingConsent = {},
      isOptimistic = false,
    }) => {
      const marketingConsent = getMarketingConsentPayload(
        userCountry,
        userMarketingConsent
      );

      try {
        await sendMarketingConsentApi({
          keepalive: true,

          correlationId,
          email: formValues.email,
          dob: toDate(formValues.dateOfBirth),
          guardianEmail: formValues.guardianEmail,
          country: userCountry,
          clientId: getClientId().clientId,
          pagePath: item.pagePath,
          artistIds: item.artistIds,
          artistName: item.artistName,
          userSelection: userMarketingConsent,

          doubleOptInEmailMarketing: consentType === 'doubleOptIn',
          doubleOptInRedirectUrl: getItemPageDialogUrl(
            'marketingConsentSubmitted'
          ),

          controlFunctionalEmails:
            isCountryRequireFunctionalEmailControl(userCountry),

          ...marketingConsent,
        });
      } catch (error) {
        if (isOptimistic) {
          reportErrorClient({ error });
          return;
        }

        throw error;
      }
    },
    [item, userCountry, consentType]
  );

  const withPresaveNotificationEmail =
    // NOTE:
    // - When we ask for email before presave, we always record email notification first
    // - If country requires functional email control, we record email notification
    //   along with marketing consent to be able to control approval
    !isEmailFirstPresave() &&
    !isCountryRequireFunctionalEmailControl(userCountry);

  const startNativePresave = useServicePresave({
    item,
    withNotificationEmail: withPresaveNotificationEmail,

    onError: backToBeforeFirstHash,
    onResult() {
      openPresaveDialog(PresaveDialogStage.Loading);
    },
    async onSuccess({ serviceType, ...params }) {
      // emit event to notify other components about successful auth
      emitter.emit('serviceAuthSuccess', params);

      // email is optional here only because Apple Music does not return it
      // and not having email here means an issue with presave stages logic
      // but we still handle it gracefully and re-ask to collect email again
      if (!params.email) {
        openPresaveDialog(PresaveDialogStage.Email, {
          // we show loading stage once we trigger presave request
          // and we do not want to hold loading in history, so we replace this stage
          replace: true,
          params: {
            serviceType,
            withEmailPresave: String(!withPresaveNotificationEmail),
            nextStep: PresaveNextStep.NativePresaveDone,
          },
        });

        return;
      }

      if (isEmailFirstPresave(serviceType)) {
        openPresaveDialog(PresaveDialogStage.Notification, {
          replace: true,
          params: {
            serviceType,
            notificationType: toNotificationType({
              country: params.country ?? userCountry,
              dob: params.dob,
              fallbackNotificationType: PresaveDialogNotification.NativePresave,
            }),
          },
        });
      } else if (isMarketingConsentRequired(userCountry)) {
        openPresaveDialog(PresaveDialogStage.Email, {
          replace: true,
          params: {
            email: params.email,
            serviceType,
            withEmailPresave: String(!withPresaveNotificationEmail),
            nextStep: PresaveNextStep.NativePresaveDone,
          },
        });
      } else {
        // send default marketing consent for countries that does not require opt-ins at all
        // it happens in background and fails silently as we do not show any UI
        sendMarketingConsent({
          formValues: { email: params.email },
          isOptimistic: true,
        });

        openPresaveDialog(PresaveDialogStage.Notification, {
          replace: true,
          params: {
            serviceType,
            notificationType: toNotificationType({
              country: params.country ?? userCountry,
              dob: params.dob,
              fallbackNotificationType: PresaveDialogNotification.NativePresave,
            }),
          },
        });
      }
    },
  });

  const onButtonClick = useCallback<ClickableOnClick<ServiceTypes>>(
    ({ data: serviceType }) => {
      if (presaveGate.isLocked) {
        setPresaveGate({
          isLocked: presaveGate.isLocked,
          hasError: true,
        });

        return;
      }

      if (isEmailFirstPresave(serviceType)) {
        const nextStep = SERVICES_WITH_PRESAVE.includes(serviceType)
          ? PresaveNextStep.NativePresave
          : PresaveNextStep.EmailPresave;

        openPresaveDialog(PresaveDialogStage.Email, {
          params: { serviceType, nextStep },
        });
      } else {
        // NOTE: starting native presave should only happens synchronously
        startNativePresave({ serviceType });
      }
    },
    [presaveGate.isLocked, withEmailAndOptInsBeforePresave]
  );

  const onLinkClick = useCallback<
    ClickableOnClick<{ dataPath: string; serviceType: ServiceTypes }>
  >(
    ({ data: { dataPath, serviceType } }) => {
      if (presaveGate.isLocked) {
        setPresaveGate({
          isLocked: presaveGate.isLocked,
          hasError: true,
        });

        return;
      }

      openPresaveDialog(PresaveDialogStage.Email, {
        params: {
          serviceType,
          dataPath,
          nextStep: PresaveNextStep.LinkRedirect,
        },
      });
    },
    [presaveGate.isLocked]
  );

  return (
    <>
      <div data-testid="presaveButtons2 subscribeButtons">
        <SectionTitle margin="0 0 2rem" text={title} />
        <LegalGate
          padding="0 1.2rem 1.75rem"
          hasError={presaveGate.hasError}
          onCheck={useCallback((isChecked) => {
            setPresaveGate({
              isLocked: !isChecked,
              hasError: false,
            });
          }, [])}
        />
        <Box
          margin="-1.2rem 0"
          padding="1.2rem 1.2rem"
          style={{ overflow: 'hidden' }}
        >
          {useMemo(
            () =>
              sortedItems.map((item, index) => {
                const isFirst = !index;
                const margin = isFirst ? '' : '1rem 0 0';

                switch (item.type) {
                  case 'link': {
                    const resolvedLink = resolveLink(layoutData, item.dataPath);

                    if (!resolvedLink) return;

                    const isGated =
                      !!item.withEmailAndOptIns && resolvedLink.service.match;

                    return (
                      <ServiceButton
                        key={`link.${item.dataPath}`}
                        serviceType={resolvedLink.serviceType}
                        margin={margin}
                        href={isGated ? undefined : resolvedLink.link}
                        text={resolvedLink.text}
                        Icon={resolvedLink.Icon}
                        data={
                          isGated
                            ? {
                                dataPath: item.dataPath,
                                serviceType: resolvedLink.serviceType,
                              }
                            : undefined
                        }
                        onClick={isGated ? onLinkClick : undefined}
                      />
                    );
                  }

                  case 'subscribe':
                  default:
                    return (
                      <ServiceButton
                        serviceType={item.service}
                        key={`subscribe.${item.service}`}
                        text={item.text}
                        margin={margin}
                        data={item.service}
                        withColoredIcon={!!withColoredIcons}
                        onClick={onButtonClick}
                      />
                    );
                }
              }),
            [
              sortedItems,
              onButtonClick,
              onLinkClick,
              layoutData,
              withColoredIcons,
            ]
          )}
        </Box>
        <Box margin="2rem 2.2rem 0">
          <LegalFootnote />
        </Box>
      </div>
      {isPresaveDialogOpened && (
        <PresaveDialogLazy
          item={item}
          dialogId={presaveDialogId}
          layoutData={layoutData}
          sendMarketingConsent={sendMarketingConsent}
          startNativePresave={startNativePresave}
        />
      )}
    </>
  );
};

export default PresaveButtons2;
