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

import type { DialogBoxApi } from '~/src/components/DialogBox';
import type { PageSectionComponent } from '../../types';
import type { PresaveButtonsProps } from '../types';

import Box from '~/src/components/Box';
import Clickable from '~/src/components/Clickable';
import DialogBox from '~/src/components/DialogBox';
import { useEditActions } from '~/src/components/ItemPage/ItemPageEdit/useEditActions';
import ListItem from '~/src/components/ListItem';
import { SortableContainer, SortableItem } from '~/src/components/Sortable';
import Text from '~/src/components/Text';
import useHash from '~/src/hooks/useHash';
import getServiceDisplayData from '~/src/lib/getServiceDisplayData';
import { useI18n } from '~/src/lib/i18n';
import { EditWrapper } from '../../lib/EditWrapper';
import SectionTitle from '../../lib/SectionTitle';
import ServiceButton from '../../lib/ServiceButton';
import { PRESAVE_BUTTONS } from '../PresaveButton';
import { EditPresaveItemDialogBox } from './EditPresaveItemDialogBox';
import PresaveButtonsSettings from './SettingsContent';

const debug = Debug('songwhip/PresaveButtonsEdit');
const HASH_PARAM = 'presaveButtons';

const PresaveButtonsEdit: PageSectionComponent<PresaveButtonsProps> = ({
  title,
  items,
  sectionPath,
  withColoredIcons,
}) => {
  const { hashParams, backToBeforeFirstHash } = useHash();
  const editLinkHashParam = `${HASH_PARAM}:${sectionPath}:edit`;
  const editHashParam = hashParams[editLinkHashParam];
  const itemToEditIndex = Number(editHashParam);
  const itemToEdit = editHashParam ? items[itemToEditIndex] : undefined;
  const { updateLayoutSection } = useEditActions();
  const { setHashParam } = useHash();

  return (
    <div data-testid="PresaveButtonsEdit">
      <EditWrapper
        padding="20 10"
        sectionPath={sectionPath}
        renderSettingsContent={useCallback(
          ({ close }) => (
            <PresaveButtonsSettings
              title={title}
              withColoredIcons={withColoredIcons}
              sectionPath={sectionPath}
              onSubmit={close}
            />
          ),
          [title, withColoredIcons]
        )}
      >
        <PresaveButtonsEditContentMemo
          title={title}
          items={items}
          editLinkHashParam={editLinkHashParam}
          onButtonClick={useCallback(
            ({ data: itemIndex }) => {
              setHashParam({
                [editLinkHashParam]: String(itemIndex),
              });
            },
            [editLinkHashParam]
          )}
          sectionPath={sectionPath}
          withColoredIcons={withColoredIcons}
        />
      </EditWrapper>
      {itemToEdit && (
        <EditPresaveItemDialogBox
          item={itemToEdit}
          onClose={() => {
            debug('on edit item close');
            backToBeforeFirstHash();
          }}
          onDelete={() => {
            debug('on item delete');

            const itemsNext = [...items];

            itemsNext.splice(itemToEditIndex, 1);

            updateLayoutSection({
              sectionPath,

              changedProps: {
                items: itemsNext,
              },
            });
          }}
          onSubmit={(itemNext) => {
            debug('on submit', itemNext);

            const isUnchanged = itemNext.text === itemToEdit.text;

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

            const itemsNext = [...items];

            // update the item that was edited
            itemsNext[itemToEditIndex] = itemNext;

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

              changedProps: {
                items: itemsNext,
              },
            });
          }}
        />
      )}
    </div>
  );
};

const PresaveButtonsEditContent = ({
  items,
  title,
  sectionPath,
  withColoredIcons,
  onButtonClick,
}: {
  editLinkHashParam: string;
  onButtonClick: (params: { data: number }) => void;
  sectionPath: string;
} & PresaveButtonsProps) => {
  const { updateLayoutSection } = useEditActions();

  const unusedServices = Object.keys(PRESAVE_BUTTONS).filter(
    (serviceType) => !items.some(({ type }) => type === serviceType)
  ) as (keyof typeof PRESAVE_BUTTONS)[];

  // as not all links in the list are rendered (eg. if we couldn't resolve data
  // for the )
  // a reference of the
  const renderedItemsRef = useRef({
    indexByRenderedIndex: {},
    renderedIndex: 0,
  });

  return (
    <>
      <SectionTitle text={title} minHeight={35} />
      <Box tag="ul" margin=".5rem 0 0">
        <SortableContainer
          nonDragAreaSelector=".noDrag"
          onDrop={({ removedIndex, addedIndex }) => {
            debug('on drop', { removedIndex, addedIndex });

            const { indexByRenderedIndex } = renderedItemsRef.current;
            const didChange = removedIndex !== addedIndex;

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

            const removedLinksIndex = indexByRenderedIndex[removedIndex!];
            const addedLinksIndex = indexByRenderedIndex[addedIndex!];

            const itemsNext = [...items];
            const [removed] = itemsNext.splice(removedLinksIndex, 1);
            itemsNext.splice(addedLinksIndex, 0, removed);

            debug('links change', itemsNext);

            updateLayoutSection({
              sectionPath,

              changedProps: {
                items: itemsNext,
              },
            });
          }}
        >
          {items.map((item, index) => {
            // reset the index refs on each new loop
            if (index === 0) {
              renderedItemsRef.current = {
                renderedIndex: 0,
                indexByRenderedIndex: {},
              };
            }

            const isLast = index === items.length - 1;
            const margin = !isLast ? `0 0 1rem` : undefined;
            const { type } = item;

            // store a reference to the underlying `links` index
            // so that onDrop we can resolve it from the *rendered* index
            renderedItemsRef.current.indexByRenderedIndex[
              renderedItemsRef.current.renderedIndex
            ] = index;

            renderedItemsRef.current.renderedIndex++;

            return (
              <SortableItem key={type}>
                <ServiceButton
                  serviceType={type}
                  text={item.text}
                  margin={margin}
                  withColoredIcon={withColoredIcons}
                  onClick={() => {
                    onButtonClick({ data: index });
                  }}
                />
              </SortableItem>
            );
          })}
        </SortableContainer>

        {/* show 'Add' button when there are unused items */}
        {unusedServices.length ? (
          <AddItemButton
            sectionPath={sectionPath}
            items={items}
            unusedServices={unusedServices}
          />
        ) : null}
      </Box>
    </>
  );
};

const AddItemButton = ({
  sectionPath,
  unusedServices,
  items,
}: {
  items: PresaveButtonsProps['items'];
  sectionPath: string;
  unusedServices: PresaveButtonsProps['items'][0]['type'][];
}) => {
  const { t } = useI18n();
  const { hasHashParam, backToBeforeFirstHash, setHashParam } = useHash();
  const dialogBoxRef = useRef<DialogBoxApi>(null);
  const addLinkHashParam = `${HASH_PARAM}:${sectionPath}:add`;
  const addLinkDialogOpen = hasHashParam(addLinkHashParam);
  const close = () => dialogBoxRef.current?.close();
  const { updateLayoutSection } = useEditActions();

  return (
    <Clickable
      onClick={useCallback(
        () => setHashParam({ [addLinkHashParam]: '' }),
        [setHashParam]
      )}
      margin="2.2rem 0 0 0"
      testId="addServiceButton"
    >
      <Text size="1.7rem" isBold centered>
        {t('itemEdit.actions.addService')}
      </Text>

      {addLinkDialogOpen && (
        <DialogBox
          apiRef={dialogBoxRef}
          onClose={backToBeforeFirstHash}
          renderContent={() => {
            return (
              <Box padding="0 15">
                {unusedServices.map((serviceType) => (
                  <ListItem
                    key={serviceType}
                    height={50}
                    title={getServiceDisplayData(serviceType)!.name}
                    onClick={() => {
                      const itemsNext = [...items];

                      itemsNext.push({
                        type: serviceType,
                      });

                      updateLayoutSection({
                        sectionPath,

                        changedProps: {
                          items: itemsNext,
                        },
                      });

                      close();
                    }}
                  />
                ))}
              </Box>
            );
          }}
        />
      )}
    </Clickable>
  );
};

const PresaveButtonsEditContentMemo = memo(PresaveButtonsEditContent);

export default PresaveButtonsEdit;
