import { useCallback, useMemo, useState } from 'react';
import { useI18n } from '@theorchard/suite-i18n-localization';

import type { ItemLinksCustomLink } from '../../types';
import type {
  FilterableListItemProps,
  FilterableListProps,
} from './FilterableList';
import type { SuggestedItem } from './useSuggestedLinks';

import Box from '~/src/components/Box';
import Button from '~/src/components/Button';
import LinkIcon from '~/src/components/Icon/LinkIcon';
import MoreIcon from '~/src/components/Icon/MoreIcon';
import ModalActions from '~/src/components/ModalActions';
import { useAppConfirm } from '~/src/components/NextApp/lib/CoreUi';
import Text from '~/src/components/Text';
import useHash from '~/src/hooks/useHash';
import { resolveServiceDataFromUrl } from '~/src/lib/getServiceDisplayData';
import { useAppRouter } from '~/src/lib/router2';
import { isUrl } from '~/src/lib/utils/url';
import { useItemContext } from '../../../ItemPageContext';
import FilterableList from './FilterableList';

export interface AddLinkPickStageProps
  extends Pick<
    FilterableListProps,
    | 'inputRenderAfter'
    | 'inputPlaceholder'
    | 'inputTextSize'
    | 'coverParent'
    | 'height'
  > {
  items: SuggestedItem[];

  /**
   * Called when a list item is clicked.
   */
  onPick: (params: { item: SuggestedItem; index: number }) => void;

  /**
   * Called when the user enters a full valid link in search box
   * that matches a known brand and submits. This means we can skip
   * the final stage as we have both button text and link.
   */
  onCreate: (params: Omit<ItemLinksCustomLink, 'type'>) => void;

  /**
   * Called when a user enters an unknown link into the search input
   * meaning we likely need to proceed to the CreateLinkForm so that
   * they can pick and icon and button text.
   */
  onCreateCustom: (url?: string) => void;

  onBack: () => void;
}

/**
 * A list of services that can be added to the list of link buttons.
 */
const AddLinkPickStage = ({
  items,
  onPick,
  onCreate,
  onCreateCustom,
  onBack,
  ...filterableListProps
}: AddLinkPickStageProps) => {
  const { setHashParam, hashParams } = useHash();
  const [actionMenuOpen, setActionMenuOpen] = useState(false);
  const { t } = useI18n('itemEdit');
  const confirm = useAppConfirm();
  const router = useAppRouter();

  const {
    data: { item },
  } = useItemContext();

  const brands = item?.primaryOwnerAccount?.config?.brands;
  const accountId = item.primaryOwnerAccount?.id;

  const onListItemClick = useCallback<FilterableListProps['onItemClick']>(
    ({ item, searchTerm, searchTermAsUrl }) => {
      const { urlPattern, originalIndex } = item.data as any;
      const originalItem = items[originalIndex];

      const searchTermMatchesServiceUrl =
        searchTermAsUrl && urlPattern.test(searchTermAsUrl);

      if (searchTermMatchesServiceUrl) {
        onCreate({
          link: searchTermAsUrl || searchTerm,
        });
      } else {
        onPick({
          item: originalItem,
          index: originalIndex,
        });
      }
    },
    [onCreate, onPick, items]
  );

  return (
    <>
      <FilterableList
        onClose={onBack}
        initialSearchTerm={useMemo(() => hashParams.term, [])}
        onItemClick={onListItemClick}
        trackingId="addLink:searchBrands"
        inputRenderAfter={useCallback(
          () => (
            <Box flexRow alignCenter>
              <MoreIcon
                direction="left"
                size="1.75em"
                opacity={0.9}
                margin="0 0.27em"
                onClick={() => setActionMenuOpen(true)}
                testId="openActions"
              />
            </Box>
          ),
          []
        )}
        toItems={useCallback(
          ({
            searchTerm,
            searchTermRegex,
            searchTermAsUrl,
          }): FilterableListItemProps[] | undefined => {
            const result = items
              .map((item, index) => ({ ...item, originalIndex: index }))
              .filter((item) => {
                const { matchingLink, urlPattern, name, isInList } = item;
                const matchingResolvedLink = matchingLink?.link;

                // when the search term is a url and it matches this item, render it
                // even if the item is already in the the button list
                if (searchTermAsUrl && urlPattern.test(searchTermAsUrl)) {
                  return true;
                }

                // if the items is already in the section's button list
                // we don't include it as a suggested option
                if (isInList) {
                  return false;
                }

                // when there's no search term all items are rendered
                if (!searchTerm) {
                  return true;
                }

                // search term matches name
                if (searchTermRegex.test(name)) {
                  return true;
                }

                // underlying ResolvedLink object matches search term
                if (
                  matchingResolvedLink &&
                  searchTermRegex.test(matchingResolvedLink)
                ) {
                  return true;
                }

                return false;
              })
              .map<FilterableListItemProps>(
                (
                  { matchingLink, Icon, name, urlPattern, key, originalIndex },
                  _,
                  items
                ) => {
                  const onlyOneItem = items.length === 1;

                  const itemMatchesInputUrl =
                    searchTermAsUrl && urlPattern.test(searchTermAsUrl);

                  let subtitle = matchingLink?.link || t('labels.enterLink');

                  // when the search term is a url and it matches this item, render it
                  // even if the item is already in the the button list
                  if (itemMatchesInputUrl) {
                    subtitle = searchTermAsUrl;
                  }

                  return {
                    key,
                    Icon,
                    title: name,
                    subtitle,

                    // By making this a <button type=submit> it'll be auto-clicked when
                    // Enter key is pressed from the search input. This is
                    // default browser behaviour for element in the same <form>.
                    isSubmit: onlyOneItem,

                    data: {
                      originalIndex,
                      urlPattern,
                    },
                  };
                }
              );

            if (result.length) {
              return result;
            }

            if (searchTermAsUrl) {
              const brand = resolveServiceDataFromUrl(searchTermAsUrl, brands);

              return [
                {
                  key: 'item',
                  Icon: brand.Icon,
                  testId: 'resultItem',

                  // By making this a <button type=submit> it'll be auto-clicked when
                  // Enter key is pressed from the search input. This is
                  // default browser behaviour for element in the same <form>.
                  isSubmit: true,

                  title: brand.match ? brand.name : t('actions.addCustomLink'),
                  subtitle: searchTermAsUrl,

                  onClick: () => {
                    if (brand.match) {
                      onCreate({
                        link: searchTermAsUrl,
                      });
                    } else {
                      onCreateCustom(searchTermAsUrl);
                    }
                  },
                },
              ];
            }
          },
          [items]
        )}
        onTermChange={useCallback((value) => {
          setHashParam({ term: value });
        }, [])}
        // when the user jumps to the next stage restore the scroll position.
        // REVIEW: This is cached until the app is reloaded, need to see if
        // the UX feels odd in some cases. If the list of links changes then
        // we should probably invalidate the cache as the scroll position
        // will no longer make sense. We should allow passing a `rememberPositionKey`
        // prop into <AddLinkDialogBox> so that different contexts can get different
        // scroll caches.
        rememberPositionKey="addLinkSuggestions"
        renderEmpty={useCallback<FilterableListProps['renderEmpty']>(
          ({ searchTerm }) => {
            return (
              <Box coverParent centerContent flexColumn>
                <Text centered size="1.7rem" color="#bbb" testId="noResults">
                  {searchTerm
                    ? t('addLinkNoResults')
                    : t('addLinkNoSuggestions')}
                </Text>
                <Button
                  isInline
                  text={t('actions.addCustomLink')}
                  margin="1.4rem 0 2rem"
                  renderBefore={() => (
                    <LinkIcon size="0.5em" color="#ccc" margin="0 0 0 0.05em" />
                  )}
                  // By making this a <button type=submit> it'll be auto-clicked when
                  // Enter key is pressed from the search input. This is
                  // default browser behaviour for element in the same <form>.
                  isSubmit
                  height="4rem"
                  onClick={() => {
                    const link = isUrl(searchTerm) ? searchTerm : undefined;
                    onCreateCustom(link);
                  }}
                />
              </Box>
            );
          },
          []
        )}
        {...filterableListProps}
      />
      {actionMenuOpen && (
        <ModalActions
          onClose={() => setActionMenuOpen(false)}
          items={[
            {
              content: t('actions.addCustomLink'),
              onClick: onCreateCustom,
              testId: 'addCustomLink',
            },
            // Only show for pages that have an owner Account. It is possible
            // for Songwhip admins edit pages without an owner Account.
            !!accountId && {
              content: t('actions.manageCustomBrands'),
              testId: 'manageCustomBrands',

              onClick: () => {
                confirm({
                  content: 'Discard changes?',
                  actionText: 'Continue',
                }).then((confirmed) => {
                  if (confirmed) {
                    router.push(`/account?accountId=${accountId}`);
                  }
                });
              },
            },
          ]}
        />
      )}
    </>
  );
};

export default AddLinkPickStage;
