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

import type { PageSectionComponent, ResolvedLink } from '../../types';
import type { ItemLinksProps } from '../types';

import Box from '~/src/components/Box';
import MoreIcon from '~/src/components/Icon/MoreIcon';
import Info from '~/src/components/Info';
import { useEditActions } from '~/src/components/ItemPage/ItemPageEdit/useEditActions';
import { useAppConfirm } from '~/src/components/NextApp/lib/CoreUi';
import { SortableContainer, SortableItem } from '~/src/components/Sortable';
import useHash from '~/src/hooks/useHash';
import { useI18n } from '~/src/lib/i18n';
import { getCountryName } from '~/src/lib/utils/countriesByCode';
import { resolveLink } from '../../lib';
import { EditLinkDialogBox } from '../../lib/EditLinkDialogBox';
import { EditWrapper } from '../../lib/EditWrapper';
import PickTerritory, { DEFAULT_TERRITORY } from '../../lib/PickTerritory';
import SectionTitle from '../../lib/SectionTitle';
import ServiceButton, { DEFAULT_HEIGHT_REM } from '../../lib/ServiceButton';
import AddLinkButton from './AddLinkButton';
import ItemLinksSettings from './ItemLinksSettings';

const debug = Debug('songwhip/ItemLinksEdit');
const HASH_PARAM = 'linkButtons';

const ItemLinksEdit: PageSectionComponent<ItemLinksProps> = ({
  title: defaultTitle,
  links: defaultLinks,
  layoutData,
  sectionPath,
  withShowMore,
  withColoredIcons,

  // All owned/orchard pages should have alphabetical sort by default
  withAlphabeticalSort = layoutData.item.isOwned,

  territoryOverrides,
}) => {
  const [activeTerritory, setActiveTerritory] = useState<string>();
  const { hashParams, backToBeforeFirstHash } = useHash();
  const editLinkHashParam = `${HASH_PARAM}:${sectionPath}:edit`;
  const linkToEditPath = hashParams[editLinkHashParam];
  const withTerritoryOverrides = !!territoryOverrides;
  const { setHashParam } = useHash();
  const confirm = useAppConfirm();

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

  const addedTerritories = useMemo(
    () => (territoryOverrides ? Object.keys(territoryOverrides) : []),
    [territoryOverrides]
  );

  const titleResolved =
    (activeTerritory && territoryOverrides?.[activeTerritory]?.title) ||
    defaultTitle;

  const { items, linksResolved } = useMemo(() => {
    const dataPaths =
      (activeTerritory && territoryOverrides?.[activeTerritory]?.links) ||
      defaultLinks;

    const mappedItems = dataPaths
      .map((dataPath) => {
        return resolveLink(layoutData, dataPath);
      })
      .filter(Boolean) as ResolvedLink[];

    const items = withAlphabeticalSort
      ? mappedItems.sort((a, b) => a.text.localeCompare(b.text))
      : mappedItems;

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

    return {
      items,
      linksResolved,
    };
  }, [activeTerritory, territoryOverrides, defaultLinks, withAlphabeticalSort]);

  const updateTerritoryProps = useCallback(
    (
      params: Partial<Pick<ItemLinksProps, 'title' | 'links'>>,
      settings: Pick<
        ItemLinksProps,
        'withShowMore' | 'withColoredIcons' | 'withAlphabeticalSort'
      > = {},
      territory = activeTerritory
    ) => {
      const changedProps = territory
        ? {
            ...settings,
            territoryOverrides: {
              ...territoryOverrides,

              [territory]: {
                ...territoryOverrides?.[territory],
                ...params,
              },
            },
          }
        : {
            ...settings,
            ...params,
          };

      updateLayoutSection<ItemLinksProps>({
        sectionPath,
        changedProps,
      });
    },
    [activeTerritory, territoryOverrides, updateLayoutSection]
  );

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

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

      updateTerritoryProps({
        links: linksNext,
      });
    },
    [linksResolved, updateLayoutSection]
  );

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

  return (
    <div data-testid="itemLinksEdit">
      <EditWrapper
        withInlineHeader={!!territoryOverrides}
        sectionPath={sectionPath}
        renderHeaderContent={useCallback(() => {
          if (!withTerritoryOverrides) return;

          return (
            <PickTerritory
              items={addedTerritories}
              value={activeTerritory || DEFAULT_TERRITORY}
              onChange={(country) =>
                setActiveTerritory(
                  country === DEFAULT_TERRITORY ? undefined : country
                )
              }
              onAdd={(country) => {
                setActiveTerritory(country);

                // add an empty entry to `territoryOverrides`
                updateTerritoryProps({}, undefined, country);
              }}
            />
          );
        }, [
          withTerritoryOverrides,
          activeTerritory,
          addedTerritories,
          updateTerritoryProps,
        ])}
        renderSettingsContent={useCallback(
          ({ close }) => (
            <ItemLinksSettings
              title={titleResolved}
              activeTerritory={activeTerritory}
              onTitleChange={(value) => {
                updateTerritoryProps({
                  title: value,
                });
              }}
              onTerritoryRemove={(territory) => {
                const territoryOverridesNext = {
                  ...territoryOverrides,
                };

                delete territoryOverridesNext[territory];

                updateLayoutSection({
                  sectionPath,

                  changedProps: {
                    territoryOverrides: territoryOverridesNext,
                  },
                });

                setActiveTerritory(undefined);
                close();
              }}
              withShowMore={withShowMore}
              withColoredIcons={withColoredIcons}
              withAlphabeticalSort={withAlphabeticalSort}
              sectionPath={sectionPath}
              onSubmit={close}
              withTerritoryOverrides={!!territoryOverrides}
              onTerritoryOverridesChange={async (value, updateValue) => {
                const hasTerritoryOverrides = !!(
                  territoryOverrides && Object.keys(territoryOverrides).length
                );

                if (!value && hasTerritoryOverrides) {
                  const confirmed = await confirm({
                    content: 'Remove all territory customizations?',
                  });

                  if (!confirmed) {
                    updateValue(true);
                    return;
                  }
                }

                if (!value) {
                  updateLayoutSection<ItemLinksProps>({
                    sectionPath,

                    changedProps: {
                      territoryOverrides: undefined,
                    },
                  });

                  setActiveTerritory(undefined);

                  // when territories are cleared, close the modal to show the user
                  if (hasTerritoryOverrides) {
                    close();
                  }

                  return;
                }

                // Create an empty object to store overrides. This is
                // also used to infer when the feature is enabled.
                updateLayoutSection({
                  sectionPath,

                  changedProps: {
                    territoryOverrides: {},
                  },
                });
              }}
            />
          ),
          [
            withShowMore,
            titleResolved,
            withColoredIcons,
            withAlphabeticalSort,
            territoryOverrides,
            sectionPath,
            activeTerritory,
            updateTerritoryProps,
          ]
        )}
      >
        <ItemLinksEditContent
          title={titleResolved}
          items={items}
          links={linksResolved}
          editLinkHashParam={editLinkHashParam}
          onSubmitNewLink={onSubmitNewLink}
          onButtonClick={useCallback(
            ({ data: dataPath }) => {
              setHashParam({
                [editLinkHashParam]: dataPath,
              });
            },
            [editLinkHashParam]
          )}
          onButtonOrderChange={useCallback(
            (linksNext) => {
              updateTerritoryProps(
                { links: linksNext },
                // NOTE: Button order change means sorting is not applied anymore
                { withAlphabeticalSort: false }
              );
            },
            [updateTerritoryProps]
          )}
          sectionPath={sectionPath}
          withColoredIcons={withColoredIcons}
        />
      </EditWrapper>
      {linkToEdit && (
        <EditLinkDialogBox
          item={linkToEdit}
          onClose={backToBeforeFirstHash}
          withPagePeekingSetting
          onDelete={({ dataPath }) => {
            const linksNext = linksResolved.filter((item) => item !== dataPath);

            updateTerritoryProps({
              links: linksNext,
            });

            // NOTE: we don't remove the custom link from the layout here
            // as it might be handy for the user to be able to place it
            // elsewhere in this edit session. On save we clean any disused
            // custom objects all in one go.
          }}
          onSubmit={({ dataPath, isDefault, customObject }) => {
            const isUnchanged =
              customObject.text === linkToEdit.text &&
              customObject.link === linkToEdit.link &&
              customObject.pagePeeking === linkToEdit.pagePeeking &&
              customObject.icon === linkToEdit.icon;

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

            // TODO: update to match SubscribeButtons implementation
            if (
              isDefault ||
              (activeTerritory && defaultLinks.includes(dataPath))
            ) {
              // create a new custom link from the edited data
              const nextDataPath = addCustomLink({
                link: customObject.link,
                text: customObject.text,
              });

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

              // update the component to use the links with
              // the new custom object instead of the default link
              updateTerritoryProps({
                links: linksNext,
              });

              return;
            }

            // TODO: if there's an activeTerritory and the link is in the baseLinks then we must clone it and replace
            // the reference in the current list so that this territory list has it's own dedicated version.
            // Until a link is edited in a specific territory all territories will be referencing the the original
            // base link and changes to the base will sync to other territories that have this link.
            if (activeTerritory && defaultLinks.includes(dataPath)) {
            }

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

// TODO: don't pass links that weren't resolved. This means that when we
// save customization we're not going to be including any placeholders that
// might surprise populate in the future
const ItemLinksEditContent = memo<{
  items: ResolvedLink[];
  links: string[];
  title: string;
  withColoredIcons?: boolean;
  activeTerritory?: string;
  editLinkHashParam: string;
  sectionPath: string;
  onButtonClick: (params: { data: string }) => void;
  onButtonOrderChange: (links: string[]) => void;
  onSubmitNewLink: (params: { dataPath: string }) => void;
}>(
  ({
    items,
    links,
    title,
    sectionPath,
    withColoredIcons,
    onButtonOrderChange,
    onButtonClick,
    onSubmitNewLink,
  }) => {
    const { tx } = useI18n();
    const marginBottomRem = DEFAULT_HEIGHT_REM * 0.175;

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

                const didChange = removedIndex !== addedIndex;

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

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

                debug('links change', linksNext);

                onButtonOrderChange(linksNext);
              },
              [links, onButtonOrderChange]
            )}
          >
            {items.map(({ dataPath, service, text, Icon }, index) => {
              const isLast = index === links.length - 1;
              const margin = !isLast ? `0 0 ${marginBottomRem}rem` : undefined;
              const { countries } = service;

              return (
                <SortableItem key={dataPath}>
                  <ServiceButton
                    text={text}
                    serviceType={service.type}
                    Icon={Icon}
                    withHref={false}
                    margin={margin}
                    withColoredIcon={withColoredIcons}
                    data={dataPath}
                    onClick={onButtonClick as any}
                    renderAfter={() => {
                      return (
                        <>
                          {countries && (
                            <Info
                              size="2.2rem"
                              withHoverOpacityFrom={0.3}
                              text={tx('itemEdit.serviceCountryInfo', {
                                serviceName: service.name,
                                countries: (
                                  <>
                                    {countries.map((code, index) => (
                                      <b key={code}>
                                        {index ? ', ' : ''}
                                        {getCountryName(code)}
                                      </b>
                                    ))}
                                  </>
                                ),
                              })}
                            />
                          )}
                          <MoreIcon
                            className="noDrag"
                            direction="left"
                            margin="0 .07em 0 0"
                            size=".45em"
                            opacity={0.3}
                          />
                        </>
                      );
                    }}
                  />
                </SortableItem>
              );
            })}
          </SortableContainer>
          <AddLinkButton
            sectionPath={sectionPath}
            existingLinks={links}
            onSubmit={onSubmitNewLink}
          />
        </Box>
      </Box>
    );
  }
);

export default ItemLinksEdit;
