import { useCallback, useRef } from 'react';
import Debug from 'debug';

import type { ServiceTypes } from '~/lib/types';
import type { DialogBoxApi } from '~/src/components/DialogBox';
import type { FormOnSubmit } from '~/src/components/Form';
import type { TextInputToValidationMessage } from '~/src/components/TextInput';
import type { FC } from 'react';
import type { AddLinkPickStageProps } from './AddLinkPickStage';
import type { SuggestedItem } from './useSuggestedLinks';

import Box from '~/src/components/Box';
import Clickable from '~/src/components/Clickable';
import DialogBox, {
  DIALOG_BOX_CONTENT_PADDING_X,
} from '~/src/components/DialogBox';
import { DialogBoxHeader } from '~/src/components/DialogBox/DialogBoxHeader';
import Form, { FORM_SECTION_SPACING } from '~/src/components/Form';
import { useEditActions } from '~/src/components/ItemPage/ItemPageEdit/useEditActions';
import Text from '~/src/components/Text';
import useHash from '~/src/hooks/useHash';
import useIsLargeScreen from '~/src/hooks/useIsLargeScreen';
import { useI18n } from '~/src/lib/i18n';
import TextInputWithIconButton from '../TextInputWithIconButton';
import AddLinkCreateStage from './AddLinkCreateStage';
import AddLinkPickStage from './AddLinkPickStage';
import useSuggestedLinks from './useSuggestedLinks';

const debug = Debug('songwhip/AddLinkDialogBox');

interface AddLinkDialogBoxProps {
  items: SuggestedItem[];
  onPick: (params: { dataPath: string; serviceType?: ServiceTypes }) => void;
  onCreate: (params: { dataPath: string; serviceType?: ServiceTypes }) => void;
  onPickItemClick?: (params: {
    goToStage: GoToStage;
    item: SuggestedItem;
  }) => void;
  onClose: () => void;
  hashParam: string;
  testId?: string;
  withPagePeekingSetting?: boolean;
}

type GoToStage = (
  params:
    | { stage: 'pick' | '' }
    | { stage: 'create'; link: string }
    | { stage: 'fromItem'; index: string }
) => void;

// TODO: migrate to <DialogBoxWithStages>
export const AddLinkDialogBox: FC<AddLinkDialogBoxProps> = ({
  items,
  onClose,
  onPick,
  onCreate,
  onPickItemClick,
  hashParam,
  testId = 'addLinkDialog',
  withPagePeekingSetting,
}) => {
  const { setHashParam, hashParams } = useHash();
  const dialogBoxRef = useRef<DialogBoxApi>(null);
  const close = () => dialogBoxRef.current?.close();
  const { addCustomLinkObject: addCustomLink } = useEditActions();
  const isLargeScreen = useIsLargeScreen();
  const stage = hashParams[hashParam];

  const suggestedLinkIndex = hashParams.index
    ? Number(hashParams.index)
    : undefined;

  const onPickLink = useCallback(
    ({
      dataPath,
      serviceType,
    }: {
      dataPath: string;
      serviceType?: ServiceTypes;
    }) => {
      debug('on select link', dataPath);

      onPick({ dataPath, serviceType });
      close();
    },
    []
  );

  const goToStage = useCallback<GoToStage>(({ stage, ...params }) => {
    if (stage === 'pick') {
      setHashParam({
        [hashParam]: '',
        link: undefined,
        index: undefined,
      });
    } else {
      setHashParam({
        [hashParam]: stage,
        ...params,
      });
    }
  }, []);

  const onCreateCustomLink = useCallback(({ dataPath }) => {
    onCreate({ dataPath });
    close();
  }, []);

  return (
    <DialogBox
      apiRef={dialogBoxRef}
      fillViewport={!isLargeScreen}
      onClose={onClose}
      testId={testId}
      contentKey={stage}
      renderContent={useCallback(() => {
        return (() => {
          switch (stage) {
            default:
              return (
                <AddLinkPickStage
                  items={items}
                  onBack={close}
                  coverParent={!isLargeScreen}
                  height={isLargeScreen ? '35rem' : undefined}
                  onPick={({ item, index }) => {
                    // if custom pick item click behaviour is defined run and prevent default
                    if (onPickItemClick) {
                      onPickItemClick({
                        goToStage,
                        item,
                      });

                      return;
                    }

                    const { matchingLink, serviceType } = item;

                    if (matchingLink) {
                      onPickLink({
                        dataPath: matchingLink.dataPath,
                        serviceType,
                      });

                      return;
                    }

                    goToStage({
                      stage: 'fromItem',
                      index: String(index),
                    });
                  }}
                  onCreate={(params) => {
                    const dataPath = addCustomLink(params);
                    onCreate({ dataPath });
                    close();
                  }}
                  onCreateCustom={(link) => {
                    goToStage({
                      stage: 'create',
                      link: link || '',
                    });
                  }}
                />
              );

            case 'create':
              return (
                <AddLinkCreateStage
                  onCreate={onCreateCustomLink}
                  withPagePeekingSetting={withPagePeekingSetting}
                  onBack={() => {
                    goToStage({ stage: 'pick' });
                  }}
                />
              );

            case 'fromItem': {
              const suggestedLink = items[suggestedLinkIndex!];

              if (!suggestedLink) {
                return null;
              }

              return (
                <CreateFromItemStage
                  suggestedLink={suggestedLink}
                  onCreate={onCreateCustomLink}
                  onBack={() => {
                    goToStage({ stage: 'pick' });
                  }}
                />
              );
            }
          }
        })();
      }, [stage, isLargeScreen, suggestedLinkIndex, items])}
    />
  );
};

const isUrl = (value: string) => /^https?:\/\//.test(value);

const CreateFromItemStage: FC<{
  suggestedLink: SuggestedItem;
  onCreate: (params: { dataPath: string }) => void;
  onBack: () => void;
}> = ({ onCreate, onBack, suggestedLink }) => {
  const { t } = useI18n();
  const { addCustomLinkObject: addCustomLink } = useEditActions();
  const { Icon, name, urlPattern } = suggestedLink;
  type FormValues = { url: string };

  const onSubmit = useCallback<FormOnSubmit<FormValues>>(
    ({ values }) => {
      const dataPath = addCustomLink({
        link: values.url,
      });

      onCreate({ dataPath });
    },
    [addCustomLink]
  );

  return (
    <Box padding={`0 ${DIALOG_BOX_CONTENT_PADDING_X} ${FORM_SECTION_SPACING}`}>
      <Form<FormValues> onSubmit={onSubmit}>
        <DialogBoxHeader
          title={`Enter ${name} link`}
          isPadded={false}
          onBackClick={onBack}
          withShadow={false}
          renderRight={({ textProps }) => (
            <Clickable isSubmit testId="addButton">
              <Text {...textProps}>{t('app.actions.add')}</Text>
            </Clickable>
          )}
        />
        <TextInputWithIconButton
          name="url"
          autoFocus
          margin="0.3rem 0 0"
          debounceTimeout={800}
          Icon={Icon}
          testId="linkInput"
          onChange={useCallback(({ value, setValue }) => {
            const hasProtocol = /^https?:\/\//.test(value);

            const isPartial =
              !value ||
              !!~'http://'.indexOf(value) ||
              !!~'https://'.indexOf(value);

            const nextValue = (
              !isPartial && !hasProtocol ? `https://${value}` : value
            ).replace(' ', '');

            if (nextValue !== value) {
              setValue(nextValue);
            }
          }, [])}
          toValidationMessage={useCallback<TextInputToValidationMessage>(
            ({ value }) => {
              if (!isUrl(value) || !urlPattern.test(value)) {
                return t('itemEdit.addLinkInvalidLink', { name });
              }
            },
            []
          )}
          placeholder="https://"
          required
        />
      </Form>
    </Box>
  );
};

export { useSuggestedLinks, AddLinkPickStage };
export type { SuggestedItem as SuggestedLink, AddLinkPickStageProps };
