import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import type { ClickableOnClick } from '~/src/components/Clickable';
import type { DialogBoxApi } from '~/src/components/DialogBox';
import type { FormApi } from '~/src/components/Form';
import type { Icon } from '~/src/components/Icon/toIcon';
import type { FC, ReactNode, RefObject } from 'react';
import type { IconKey } from '../icons';

import Box from '~/src/components/Box';
import Clickable from '~/src/components/Clickable';
import DialogBox from '~/src/components/DialogBox';
import { DialogBoxHeader } from '~/src/components/DialogBox/DialogBoxHeader';
import Form, { FORM_SECTION_SPACING } from '~/src/components/Form';
import InputLabel from '~/src/components/InputLabel';
import { useAppAlert } from '~/src/components/NextApp/lib/CoreUi';
import SwitchSetting from '~/src/components/Switch/SwitchSetting';
import useHash from '~/src/hooks/useHash';
import { resolveServiceDataFromUrl } from '~/src/lib/getServiceDisplayData';
import { useI18n } from '~/src/lib/i18n';
import { useItemContext } from '../../../ItemPageContext';
import { ICONS } from '../icons';
import LinkInputWithPagePeekingSwitch from '../LinkInputWithPagePeekingSwitch';
import TextInputWithIconButton from '../TextInputWithIconButton';

export type CreateLinkFormOnSubmit = (params: {
  text: string | undefined;
  link: string;
  icon: IconKey | undefined;
  pagePeeking: boolean | undefined;
  withEmailAndOptIns: boolean | undefined;
}) => void;

export interface EditLinkFormProps {
  onSubmit: CreateLinkFormOnSubmit;
  autoFocus?: boolean;
  renderBefore?: () => ReactNode;
  initialLinkValue?: string;
  initialTextValue?: string;
  initialPagePeekingValue?: boolean;
  initialIconValue?: IconKey;
  initialEmailAndOptInsValue?: boolean;
  withLinkField?: boolean;
  withTextField?: boolean;
  formApiRef?: RefObject<FormApi>;
  withPagePeekingSetting?: boolean;
  withEmailAndOptInsSetting?: boolean;
}

export const EditLinkForm: FC<EditLinkFormProps> = ({
  autoFocus = false,
  onSubmit,
  renderBefore,
  initialLinkValue = '',
  initialTextValue = '',
  initialPagePeekingValue,
  initialIconValue,
  initialEmailAndOptInsValue,
  withTextField = true,
  withLinkField = true,
  withPagePeekingSetting,
  withEmailAndOptInsSetting,
  formApiRef,
}) => {
  const [iconState, setIconState] = useState<IconKey | undefined>(
    initialIconValue
  );

  const { t } = useI18n();
  const context = useItemContext();
  const customBrands = context.data.item.primaryOwnerAccount?.config?.brands;
  const [textState, setTextState] = useState(initialTextValue);
  const [linkState, setLinkState] = useState(initialLinkValue);
  const [emailAndOptInsState, setEmailAndOptInsState] = useState(
    !!initialEmailAndOptInsValue
  );

  const serviceData = useMemo(
    () => resolveServiceDataFromUrl(linkState, customBrands),
    [linkState, customBrands]
  );

  const { setHashParam, hasHashParam, removeHashParam } = useHash();
  const iconPickerOpen = hasHashParam('pickIcon');
  const Icon = iconState ? ICONS[iconState] : serviceData.Icon;
  const textResolved = textState || (linkState ? serviceData.name : '');
  const linkMatchesKnownService = serviceData.match;
  const appAlert = useAppAlert();

  // When the link switches from unknown to matching a known service
  // we unset any custom icon, this is to enforce official branding
  // of icons to known services.
  useEffect(() => {
    if (linkMatchesKnownService) {
      setIconState(undefined);
    } else {
      setEmailAndOptInsState(false);
    }
  }, [linkMatchesKnownService]);

  return (
    <Form
      apiRef={formApiRef}
      onSubmit={useCallback(
        ({ values }) => {
          onSubmit({
            link: values.link,
            text: values.text || undefined,
            icon: iconState,
            pagePeeking: values.pagePeeking,
            withEmailAndOptIns: withEmailAndOptInsSetting
              ? emailAndOptInsState
              : undefined,
          });
        },
        [iconState, emailAndOptInsState, withEmailAndOptInsSetting, onSubmit]
      )}
    >
      {renderBefore && renderBefore()}
      {withLinkField && (
        <LinkInputWithPagePeekingSwitch
          labelValue={t('itemEdit.labels.link')}
          placeholder="https://"
          autoFocus={autoFocus}
          defaultValue={initialLinkValue}
          pagePeekingEnabled={withPagePeekingSetting}
          defaultPagePeekingValue={initialPagePeekingValue}
          onInputEnd={({ value }) => {
            setLinkState(value);
          }}
        />
      )}
      {withTextField && (
        <InputLabel
          flexGrow
          value={t('itemEdit.labels.titleAndIcon')}
          flexRow
          // avoid <button> nested in <label> hover/active quirks
          tag="div"
          margin={`${withLinkField ? FORM_SECTION_SPACING : 0} 0 0`}
        >
          <TextInputWithIconButton
            Icon={Icon}
            placeholder={t('itemEdit.labels.buttonText')}
            defaultValue={textResolved}
            testId="buttonTextInput"
            name="text"
            required
            onChange={({ value }) => {
              setTextState(value);
            }}
            onIconClick={() => {
              if (linkMatchesKnownService) {
                appAlert({
                  content: t('itemEdit.iconOfficialMatchWarning'),
                });

                return;
              }

              setHashParam({ pickIcon: '' });
            }}
          />
        </InputLabel>
      )}
      {withEmailAndOptInsSetting && (
        <SwitchSetting
          flexRow
          margin={`${FORM_SECTION_SPACING} 0 0`}
          title={t('itemEdit.labels.emailAndOptInsForLink')}
          description={t('itemEdit.emailAndOptInsForLinkHelp')}
          disabledDescription={t('itemEdit.emailAndOptInsForLinkDisabledHelp')}
          infoText={
            !linkMatchesKnownService
              ? t('itemEdit.emailAndOptInsForLinkDisabledHelp')
              : undefined
          }
          testId="emailAndOptInsForLinkSwitch"
          isDisabled={!linkMatchesKnownService}
          value={emailAndOptInsState}
          onChange={(value) => {
            setEmailAndOptInsState(value);
          }}
        />
      )}
      {iconPickerOpen && (
        <PickIconDialog
          icons={ICONS}
          onClose={() => removeHashParam('pickIcon')}
          onSelect={({ key }) => {
            setIconState(key as IconKey);
          }}
        />
      )}
    </Form>
  );
};

const PickIconDialog: FC<{
  icons: { [key: string]: Icon };
  onClose: () => void;
  onSelect: (params: { key: string }) => void;
}> = ({ icons, onClose, onSelect }) => {
  const { t } = useI18n();
  const apiRef = useRef<DialogBoxApi>(null);

  const onIconClick = useCallback<ClickableOnClick<string>>(({ data: key }) => {
    onSelect({ key });
    apiRef.current?.close();
  }, []);

  return (
    <>
      <DialogBox
        testId="pickIconDialog"
        zIndex={2}
        onClose={onClose}
        apiRef={apiRef}
        renderHeader={({ close }) => {
          return (
            <DialogBoxHeader
              onCloseClick={close}
              title={t('itemEdit.labels.pickAnIcon')}
            />
          );
        }}
        renderContent={useCallback(() => {
          return (
            <div className="list" style={{ padding: '0 2rem 2rem' }}>
              <Box tag="ul" flexRow flexWrap justifyCenter margin="0 -.8rem 0">
                {Object.entries(icons).map(([key, Icon], index) => (
                  <li key={index}>
                    <Clickable
                      padding="1rem"
                      onClick={onIconClick}
                      data={key}
                      withActiveStyle={false}
                      testId={`${key}Icon`}
                    >
                      <Icon size="3.2rem" />
                    </Clickable>
                  </li>
                ))}
              </Box>
            </div>
          );
        }, [])}
      />
      <style jsx>{`
        li {
          transition: opacity 0.5s;
          opacity: 1;
        }

        .list:hover li {
          opacity: 0.8;
        }

        .list:hover li:hover {
          opacity: 1;
          transform: scale(1.15);
        }
      `}</style>
    </>
  );
};
