import { useRef, useState } from 'react';

import type { FormApi } from '~/src/components/Form';
import type { CarouselItem } from '../types';

import Box from '~/src/components/Box';
import Clickable from '~/src/components/Clickable';
import DialogBox from '~/src/components/DialogBox';
import { DialogBoxHeader } from '~/src/components/DialogBox/DialogBoxHeader';
import DialogBoxLoading from '~/src/components/DialogBox/DialogBoxLoading';
import ErrorText from '~/src/components/ErrorText';
import Form, { FORM_SECTION_SPACING } from '~/src/components/Form';
import ImagePicker from '~/src/components/ImagePicker';
import InputLabel from '~/src/components/InputLabel';
import { useAppAlert } from '~/src/components/NextApp/lib/CoreUi';
import Sticky from '~/src/components/Sticky';
import Text from '~/src/components/Text';
import TextInput from '~/src/components/TextInput';
import useFetchSessionUser from '~/src/hooks/useFetchSessionUser';
import { useI18n } from '~/src/lib/i18n';
import uploadImageByUrl from '~/src/lib/image/uploadImageByUrl';
import useUploadImage from '~/src/lib/image/useUploadImage';
import LinkInputWithPagePeekingSwitch from '../../lib/LinkInputWithPagePeekingSwitch';
import RemoveButton from '../../lib/RemoveButton';

const EditCarouselItemDialog = ({
  item,
  onSubmit,
  onRemove,
  title,
  ...dialogProps
}: {
  item?: CarouselItem;
  title: string;
  onSubmit: (carouselItem: CarouselItem) => void;
  onRemove?: () => void;
  onClose: () => void;
}) => {
  const formApiRef = useRef<FormApi>(null);
  const { uploadImage } = useUploadImage({ trackingId: 'carouselImage' });
  const [pagePeeking, setWithPagePeeking] = useState(!!item?.pagePeeking);
  const { userAccountId } = useFetchSessionUser();
  const [userText, setUserText] = useState(item?.text);
  const [userImage, setUserImage] = useState(item?.image);
  const [isLoading, setIsLoading] = useState(false);

  const [linkMetadata, setLinkMetadata] = useState<{
    image?: string;
    title?: string;
  }>();

  const imageSrc = userImage !== undefined ? userImage : linkMetadata?.image;
  const text = userText !== undefined ? userText : linkMetadata?.title;

  const appAlert = useAppAlert();
  const { t } = useI18n();

  const resolveImage = async ({
    newImageFile,
    newFetchedImage,
    originalImage,
  }: {
    newImageFile: File | undefined;
    newFetchedImage: string | undefined;
    originalImage: string | undefined;
  }) => {
    // an image file was provided so upload it to songwhip-images
    if (newImageFile) {
      return (
        await uploadImage({
          file: newImageFile,
          scaleToWidth: 1600,
          asType: 'jpeg',
          scaleBeforeUpload: false,
          accountId: userAccountId,
        })
      ).url;
    }

    // An image was fetched from link metadata and needs uploading to our
    // servers we only do this if there is no image defined yet, otherwise
    // we would overwrite the existing image. We treat the fetched metadata
    // image as a initial "suggested" image only.
    if (!originalImage && newFetchedImage) {
      return uploadImageByUrl({
        url: newFetchedImage,
        width: 800,
        asType: 'jpeg',
        accountId: userAccountId,
      });
    }

    // no image file or metadata image defined so use
    if (originalImage) {
      return originalImage;
    }

    throw new Error('Image required');
  };

  return (
    <DialogBox
      testId="editCarouselItemDialog"
      fillViewportOnSmallScreen
      renderContent={({ close, paddingX, paddingY }) => {
        if (isLoading) {
          return <DialogBoxLoading />;
        }

        return (
          <>
            <Sticky>
              <DialogBoxHeader
                onCloseClick={close}
                title={title}
                withShadow
                renderRight={({ textProps }) => (
                  <Clickable
                    testId="submit"
                    onClick={() => formApiRef.current?.submit()}
                  >
                    <Text {...textProps}>{t('app.actions.save')}</Text>
                  </Clickable>
                )}
              />
            </Sticky>
            <Box padding={`3 ${paddingX} ${paddingY}`}>
              <Form
                apiRef={formApiRef}
                onSubmit={async ({ form }) => {
                  const link = form.link.value;
                  const text = form.text.value;
                  const newImageFile = form.image.files[0];

                  try {
                    setIsLoading(true);

                    const image = await resolveImage({
                      newImageFile,
                      newFetchedImage: linkMetadata?.image,
                      originalImage: item?.image,
                    });

                    onSubmit({
                      link,
                      text,
                      image,
                      pagePeeking,
                    });

                    close();
                  } catch (error) {
                    appAlert({
                      content: <ErrorText error={error} />,
                    });

                    setIsLoading(false);
                  }
                }}
              >
                <LinkInputWithPagePeekingSwitch
                  isOptional
                  labelValue={t('itemEdit.carousel.editItem.linkInputTitle')}
                  labelDescription={`${t(
                    'itemEdit.carousel.editItem.linkInputDescription'
                  )}${
                    !item
                      ? ` ${t(
                          'itemEdit.carousel.editItem.linkMetadataFetchPrompt'
                        )}`
                      : ''
                  }`}
                  placeholder={t(
                    'itemEdit.carousel.editItem.linkInputPlaceholder'
                  )}
                  defaultValue={item?.link}
                  onPagePeekingChange={setWithPagePeeking}
                  defaultPagePeekingValue={pagePeeking}
                  fetchLinkMetadata={!userText || !userImage}
                  pagePeekingEnabled
                  autoFocus
                  required={false}
                  onLinkMetadataFetched={(metadata) => {
                    setLinkMetadata(metadata);
                  }}
                />
                <InputLabel
                  value={t('itemEdit.carousel.editItem.textInputTitle')}
                  isOptional
                  description={t(
                    'itemEdit.carousel.editItem.textInputDescription'
                  )}
                  margin={`${FORM_SECTION_SPACING} 0 0`}
                >
                  <TextInput
                    name="text"
                    placeholder={t(
                      'itemEdit.carousel.editItem.textInputPlaceholder'
                    )}
                    testId="carouselTextInput"
                    value={text}
                    onChange={({ value }) => {
                      setUserText(value);
                    }}
                  />
                </InputLabel>
                <ImagePicker
                  margin={`${FORM_SECTION_SPACING} 0 0`}
                  name="image"
                  accept={['png', 'jpeg']}
                  testId="carouselImagePicker"
                  height="20rem"
                  image={imageSrc}
                  onChange={() => {
                    setUserImage(imageSrc);
                  }}
                />
              </Form>

              {onRemove && (
                <RemoveButton
                  margin={`${paddingX} 0 0`}
                  text={t('itemEdit.actions.removeItem')}
                  testId="removeItem"
                  onClick={async () => {
                    await close();

                    onRemove();
                  }}
                />
              )}
            </Box>
          </>
        );
      }}
      {...dialogProps}
    />
  );
};

export default EditCarouselItemDialog;
