import { memo, useCallback, useMemo } from 'react';
import classNames from 'classnames';
import Debug from 'debug';

import type { FC } from 'react';
import type { PageSectionComponent, ResolvedLink } from '../../types';
import type { IconLinksSectionProps } from '../types';

import Box from '~/src/components/Box';
import Clickable from '~/src/components/Clickable';
import { useEditActions } from '~/src/components/ItemPage/ItemPageEdit/useEditActions';
import { SortableItem } from '~/src/components/Sortable';
import Text from '~/src/components/Text';
import useHash from '~/src/hooks/useHash';
import { useI18n } from '~/src/lib/i18n';
import { resolveLink } from '../../lib';
import {
  AddLinkDialogBox,
  useSuggestedLinks,
} from '../../lib/AddLinkDialogBox';
import { SOCIAL_SERVICES } from '../../lib/constants';
import { EditLinkDialogBox } from '../../lib/EditLinkDialogBox';
import { EditWrapper } from '../../lib/EditWrapper';
import IconLinkButton from '../../lib/IconLinkButton';
import SectionTitle from '../../lib/SectionTitle';
import SortableHorizontalScroller from '../../lib/SortableHorizontalScroller';
import { shouldGroup, toIconSizeRem } from '../utils';
import IconLinksSettings from './IconLinksSettings';

const debug = Debug('songwhip/IconLinksEdit');
const HASH_PARAM = 'iconLinks';
const MAX_ITEMS = 10;

const IconLinksSectionEdit: PageSectionComponent<IconLinksSectionProps> = ({
  title,
  links,
  layoutData,
  sectionPath,
  withShowMore,
  withColoredIcons,
  isShared,
  isSocial,
}) => {
  // TODO: use useOpenDialog();
  const { hashParams, backToBeforeFirstHash } = useHash();
  const editLinkHashParam = `${HASH_PARAM}:${sectionPath}:edit`;
  const linkToEditPath = hashParams[editLinkHashParam];

  const linkToEdit = linkToEditPath
    ? resolveLink(layoutData, linkToEditPath)
    : undefined;

  const {
    updateLayoutSection: updateLayoutSection,
    updateCustomObject,
    addCustomLinkObject: addCustomLink,
  } = useEditActions();

  const { items, resolvedLinks } = useMemo(() => {
    const items = links
      .map((dataPath) => {
        const link = resolveLink(layoutData, dataPath);
        if (!link) return;

        return {
          link,
          dataPath,
        };
      })
      .filter(Boolean) as { link: ResolvedLink; dataPath: string }[];

    const resolvedLinks = items.map(({ dataPath }) => dataPath);

    return {
      items,
      resolvedLinks,
    };
  }, [layoutData, links]);

  return (
    <div
      data-testid={classNames('iconLinksSectionEdit', {
        socialIconLinksSectionEdit: isSocial,
      })}
    >
      <EditWrapper
        padding="2rem 1rem"
        sectionPath={sectionPath}
        isShared={isShared}
        renderSettingsContent={useCallback(
          ({ close }) => (
            <IconLinksSettings
              title={title}
              withShowMore={withShowMore}
              withColoredIcons={withColoredIcons}
              sectionPath={sectionPath}
              isShared={isShared}
              onSubmit={close}
            />
          ),
          [withShowMore, title, withColoredIcons]
        )}
      >
        <Content
          title={title}
          items={items}
          isSocial={isSocial}
          resolvedLinks={resolvedLinks}
          editLinkHashParam={editLinkHashParam}
          sectionPath={sectionPath}
          withColoredIcons={withColoredIcons}
        />
      </EditWrapper>
      {linkToEdit && (
        <EditLinkDialogBox
          item={linkToEdit}
          onClose={backToBeforeFirstHash}
          onDelete={({ dataPath }) => {
            const linksNext = resolvedLinks.filter((item) => item !== dataPath);

            updateLayoutSection({
              sectionPath,

              changedProps: {
                links: linksNext,
              },
            });
          }}
          onSubmit={({ dataPath, isDefault, customObject }) => {
            const isUnchanged =
              customObject.text === linkToEdit.text &&
              customObject.link === linkToEdit.link &&
              customObject.icon === linkToEdit.icon;

            if (isUnchanged) {
              debug('noop: link unchanged');
              return;
            }

            if (isDefault) {
              // create a new custom link from the edited data
              const nextDataPath = addCustomLink({
                link: customObject.link,
                text: customObject.text,
              });

              const linksNext = resolvedLinks.map((item) =>
                item === dataPath ? nextDataPath : item
              );

              // update the component to use the links with
              // the new custom object instead of the default link
              updateLayoutSection({
                sectionPath,

                changedProps: {
                  links: linksNext,
                },
              });

              return;
            }

            // if the item is already a custom object, we can simply change it in place
            updateCustomObject({
              path: dataPath,
              data: customObject,
            });
          }}
        />
      )}
    </div>
  );
};

IconLinksSectionEdit.shouldGroup = shouldGroup;

const Content = memo<{
  items: { link: ResolvedLink; dataPath: string }[];
  resolvedLinks: string[];
  title?: string;
  withColoredIcons?: boolean;
  editLinkHashParam: string;
  sectionPath: string;
  isSocial?: boolean;
}>(
  ({
    items,
    resolvedLinks,
    title,
    sectionPath,
    editLinkHashParam,
    isSocial,
  }) => {
    const { updateLayoutSection } = useEditActions();
    const { setHashParam } = useHash();

    const onButtonClick = useCallback(
      ({ data: dataPath }) => {
        setHashParam({ [editLinkHashParam]: dataPath });
      },
      [editLinkHashParam]
    );

    const onAddLink = useCallback(
      async ({ dataPath }) => {
        debug('on add link submit', dataPath);

        const linksNext = [...resolvedLinks, dataPath];

        updateLayoutSection({
          sectionPath,

          changedProps: {
            links: linksNext,
          },
        });
      },
      [updateLayoutSection, resolvedLinks]
    );

    const iconLinkSize = toIconSizeRem(items.length);

    return (
      <>
        <SectionTitle text={title} minHeight="3.5rem" isSticky={false} />
        <Box tag="ul">
          <SortableHorizontalScroller
            gradientColor="mask"
            style={{ margin: '0 auto' }}
            onDrop={useCallback(
              ({ removedIndex, addedIndex }) => {
                debug('on drop', { removedIndex, addedIndex });
                const didChange = removedIndex !== addedIndex;

                if (!didChange) {
                  debug('noop: unchanged');
                  return;
                }

                const linksNext = [...resolvedLinks];
                const [removed] = linksNext.splice(removedIndex, 1);
                linksNext.splice(addedIndex, 0, removed);

                debug('links change', linksNext);

                updateLayoutSection({
                  sectionPath,

                  changedProps: {
                    links: linksNext,
                  },
                });
              },
              [resolvedLinks]
            )}
            renderContent={({ itemStyle, itemClassName }) => {
              return items.map(({ link, dataPath }) => {
                // REVIEW: there could be a potential index issue here as IconLinksButton
                // can return `null` if it was unable to resolve button text, this is
                // an edge-case and as we're rendering a Draggable regardless,
                // the indexes should still match up.
                return (
                  <SortableItem
                    key={dataPath}
                    className={itemClassName}
                    style={{
                      ...itemStyle,
                      display: 'inline-flex',
                      flex: 0,
                      justifyContent: 'center',
                      margin: '0 .13em',
                      fontSize: `${iconLinkSize}rem`,
                    }}
                  >
                    <IconLinkButton
                      Icon={link.Icon || link.service.Icon}
                      link={link.link}
                      name={link.text}
                      testId={link.service.type}
                      clickData={dataPath}
                      onClick={onButtonClick}
                      renderLink={false}
                      size="1em"
                      margin="0"
                    />
                  </SortableItem>
                );
              });
            }}
          />
          <AddLinkButton
            sectionPath={sectionPath}
            resolvedItems={items}
            onAdd={onAddLink}
            isSocial={isSocial}
          />
        </Box>
      </>
    );
  }
);

const AddLinkButton: FC<{
  sectionPath: string;
  resolvedItems: { link?: ResolvedLink; dataPath: string }[];
  onAdd: (params: { dataPath: string }) => void;
  isSocial?: boolean;
}> = ({ sectionPath, resolvedItems, onAdd, isSocial }) => {
  const { hasHashParam, backToBeforeFirstHash, setHashParam } = useHash();
  const addLinkHashParam = `${HASH_PARAM}:${sectionPath}:add`;
  const addLinkDialogOpen = hasHashParam(addLinkHashParam);
  const suggestedLinks = useSuggestedLinks();
  const { t } = useI18n();

  const linksInList = useMemo(
    () => resolvedItems.map(({ link }) => link?.link).filter(Boolean),
    [resolvedItems]
  ) as string[];

  const suggestedItems = useMemo(
    () =>
      suggestedLinks.filter(({ matchingLink, serviceType, type }) => {
        const linkAlreadyInList =
          !!matchingLink && linksInList.includes(matchingLink.link);

        if (linkAlreadyInList) {
          return false;
        }

        if (isSocial) {
          const isSocialService =
            !!serviceType && SOCIAL_SERVICES.includes(serviceType);

          const isCustomBrand = type === 'custom';

          // only show custom brands or social services
          return isCustomBrand || isSocialService;
        }

        return true;
      }),
    [suggestedLinks, linksInList]
  );

  return (
    <>
      <Clickable
        testId="addIconLinkButton"
        isDisabled={resolvedItems.length >= MAX_ITEMS}
        onClick={() => setHashParam({ [addLinkHashParam]: '' })}
        margin="2rem 0 0 0"
      >
        <Text size="1.7rem" isBold centered>
          {t('itemEdit.actions.addLink')}
        </Text>
      </Clickable>
      {addLinkDialogOpen && (
        <AddLinkDialogBox
          items={suggestedItems}
          testId="addIconLinkDialog"
          hashParam={addLinkHashParam}
          onClose={backToBeforeFirstHash}
          onPick={onAdd}
          onCreate={onAdd}
        />
      )}
    </>
  );
};

export default IconLinksSectionEdit;
