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

import type { SwitchSettingProps } from '~/src/components/Switch/SwitchSetting';
import type { TextInputProps } from '~/src/components/TextInput';
import type { FC } from 'react';

import { ItemTypes } from '~/lib/types';
import { FORM_SECTION_SPACING } from '~/src/components/Form';
import InputLabel from '~/src/components/InputLabel';
import SwitchSetting from '~/src/components/Switch/SwitchSetting';
import TextInputWithLoading from '~/src/components/TextInput/TextInputWithLoading';
import getPublicEndpoint from '~/src/lib/getPublicEndpoint';
import { useI18n } from '~/src/lib/i18n';
import { getPageMetadataApi } from '~/src/lib/pageMetadata';
import { isUrl } from '~/src/lib/utils/url';
import { fetchItemByPathAction } from '~/src/store/paths/actions';
import { useDispatch } from '~/src/store/redux';

const debug = Debug('songwhip/LinkInputWithPagePeeking');
const NOOP = () => {};

const LinkInputWithPagePeekingSwitch: FC<
  {
    labelValue: string;
    labelDescription?: string;
    margin?: string;
    defaultPagePeekingValue: boolean | undefined;
    onPagePeekingChange?: (pagePeeking: boolean) => void;
    onLinkMetadataFetched?: (metadata: {
      image?: string;
      title?: string;
    }) => void;
    fetchLinkMetadata?: boolean;
    pagePeekingEnabled?: boolean;
    isOptional?: boolean;
  } & Omit<TextInputProps, 'value'>
> = ({
  labelDescription,
  labelValue,
  margin,
  defaultPagePeekingValue: pagePeekingValue,
  onPagePeekingChange = NOOP,
  onInputEnd = NOOP,
  defaultValue,
  pagePeekingEnabled,
  fetchLinkMetadata,
  onLinkMetadataFetched,
  isOptional,
  onChange,
  ...textInputProps
}) => {
  const [pageSupportsPeeking, setPageSupportsPeeking] = useState(
    pagePeekingValue || false
  );

  const textInputRef = useRef<HTMLInputElement>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [link, setLink] = useState(defaultValue);
  const firstRender = useRef(true);
  const dispatch = useDispatch();

  useEffect(() => {
    debug('link changed: %s', link);

    const isFirstRender = firstRender.current;

    if (isFirstRender) {
      firstRender.current = false;
    }

    if (!link) {
      return;
    }

    if (testIsLocalLink(link)) {
      if (!pagePeekingEnabled) {
        return;
      }

      setIsLoading(true);

      dispatch(
        fetchItemByPathAction({
          path: new URL(link).pathname,
          // don't throw when not found
          silent: true,
        })
      )
        .then((result) => {
          const item = result?.item;

          const supportsPeeking =
            item?.type === ItemTypes.ALBUM || item?.type === ItemTypes.TRACK;

          debug('supports PagePeeking: %s', supportsPeeking);

          setPageSupportsPeeking(supportsPeeking);

          onLinkMetadataFetched?.({
            image: item?.image,
            title: item?.name,
          });
        })
        .finally(() => {
          setIsLoading(false);

          // calling focus before triggering a render fails,
          // setTimeout ensures focus is called after the render pass
          setTimeout(() => {
            textInputRef.current?.focus();
          });
        });

      return;
    }

    if (isUrl(link) && fetchLinkMetadata && !isFirstRender) {
      setIsLoading(true);

      getPageMetadataApi(link)
        .then((metadata) => {
          onLinkMetadataFetched?.(metadata);
        })
        .catch((error) => {
          debug('error fetching link metadata', error);
        })
        .finally(() => {
          setIsLoading(false);
        });
    }
  }, [link]);

  return (
    <>
      <InputLabel
        value={labelValue}
        description={labelDescription}
        margin={margin}
        isOptional={isOptional}
      >
        <TextInputWithLoading
          inputRef={textInputRef}
          type="url"
          name="link"
          testId="linkInput"
          isLoading={isLoading}
          required
          defaultValue={defaultValue}
          onChange={useCallback(({ value, setValue }) => {
            const hasProtocol = /^https?:\/\//.test(value);

            const isPartial =
              !value ||
              !!~'http://'.indexOf(value) ||
              !!~'https://'.indexOf(value);

            const nextValue = (
              !isPartial && !hasProtocol ? `https://${value}` : value
            ).replace(' ', '');

            if (nextValue !== value) {
              setValue(nextValue);
            }
          }, [])}
          onInputEnd={useCallback(
            (params) => {
              setLink(params.value);
              onInputEnd(params);
            },
            [setLink, onInputEnd]
          )}
          {...textInputProps}
        />
      </InputLabel>
      {pageSupportsPeeking && !isLoading && (
        <PagePeekingSwitchSetting
          onChange={onPagePeekingChange}
          defaultValue={!!pagePeekingValue}
        />
      )}
    </>
  );
};

export const PagePeekingSwitchSetting = (
  props: Pick<SwitchSettingProps, 'onChange' | 'defaultValue'>
) => {
  const { t } = useI18n();

  return (
    <SwitchSetting
      isBeta
      name="pagePeeking"
      testId="pagePeekingSwitch"
      title={t('itemEdit.pagePeekingSetting.title')}
      description={t('itemEdit.pagePeekingSetting.description')}
      margin={`${FORM_SECTION_SPACING} 0 0`}
      {...props}
    />
  );
};

const testIsLocalLink = (value: string | undefined) =>
  value?.startsWith(getPublicEndpoint());

export default LinkInputWithPagePeekingSwitch;
