import { useCallback, useMemo, useRef, useState } from 'react';

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

import Clickable from '~/src/components/Clickable';
import { DialogBoxHeader } from '~/src/components/DialogBox/DialogBoxHeader';
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 RangeSlider from '~/src/components/RangeSlider';
import Text from '~/src/components/Text';
import TextInput from '~/src/components/TextInput';
import { useI18n } from '~/src/lib/i18n';
import deleteImage from '~/src/lib/image/deleteImage';
import useUploadImage from '~/src/lib/image/useUploadImage';
import { useItemContext } from '../../../ItemPageContext';
import { useEditActions } from '../../../ItemPageEdit/useEditActions';
import { findSongwhipImages } from '../../../ItemPageEdit/utils';
import { EditWrapper } from '../../lib/EditWrapper';
import { MAX_SCALE, MIN_SCALE } from '../constants';
import { InlineImageSectionView } from '../InlineImageSectionView';
import { scaleToWidth } from '../utils';

const InlineImageSectionEdit: PageSectionComponent<
  Partial<InlineImageProps>
> = ({ src, altText, sectionPath, srcWidth, srcHeight, scale }) => {
  const prevSrc = useMemo(() => src, []);
  const itemContext = useItemContext();
  const liveItemContext = itemContext.originalItemContext;
  const { updateLayoutSection, removeLayoutSection } = useEditActions();
  const { uploadImage } = useUploadImage({ trackingId: 'inlineImage' });
  const [isLoading, setIsLoading] = useState(false);
  const [imageChanged, setImageChanged] = useState(false);
  const [scaleValue, setScaleValue] = useState(scale ?? MAX_SCALE);
  const formApiRef = useRef<FormApi>(null);
  const appAlert = useAppAlert();
  const { t } = useI18n();

  const accountId = itemContext.data.item.primaryOwnerAccount?.id;
  const liveImagesOnPage = findSongwhipImages(liveItemContext?.layout);
  const currentImageIsLive = src && liveImagesOnPage.includes(src);
  const noImageAdded = !prevSrc && !imageChanged;

  return (
    <div data-testid="inlineImageEdit">
      <EditWrapper
        padding="2rem 1rem"
        sectionPath={sectionPath}
        onSettingsClose={useCallback(() => {
          // if the section was added but no image was ever uploaded
          // then we remove the section from the layout
          if (noImageAdded) {
            removeLayoutSection({ layoutSectionPath: sectionPath });
          }
        }, [noImageAdded])}
        renderSettingsHeader={useCallback(
          ({ close }) => (
            <DialogBoxHeader
              title={t('itemEdit.inlineImage.featureTitle')}
              onCloseClick={close}
              renderRight={({ textProps }) => (
                <Clickable
                  isDisabled={isLoading || noImageAdded}
                  testId="saveInlineImage"
                  onClick={() => {
                    formApiRef.current?.submit();
                  }}
                >
                  <Text {...textProps}>{t('app.actions.save')}</Text>
                </Clickable>
              )}
            />
          ),
          [isLoading, noImageAdded, imageChanged]
        )}
        renderSettingsContent={useCallback(
          ({ close }) => {
            return (
              <Form
                apiRef={formApiRef}
                onSubmit={async ({ form }) => {
                  const changedProps: Partial<InlineImageProps> = {};
                  const newAltText = form.altText.value;
                  const newImage = (form.image as HTMLInputElement).files?.[0];

                  if (newAltText !== altText) {
                    changedProps.altText = newAltText;
                  }

                  if (scaleValue !== (scale ?? MAX_SCALE)) {
                    changedProps.scale = scaleValue;
                  }

                  try {
                    if (newImage) {
                      setIsLoading(true);

                      const newSrc = await uploadImage({
                        file: newImage,
                        scaleToWidth: 1200,
                        asType: 'png',
                        scaleBeforeUpload: false,
                        accountId,
                      });

                      changedProps.src = newSrc.url;
                      changedProps.srcWidth = newSrc.width;
                      changedProps.srcHeight = newSrc.height;

                      // Clean up old images when possible. We can only safely
                      // delete images that are not in the live/saved layout.
                      // This is because although the previous image has been replaced
                      // the image is still live and the user may not end up saving
                      // their changes. So we can only safely delete images that they
                      // have uploaded previously in the same edit session.
                      if (src && !currentImageIsLive) {
                        void deleteImage(src, accountId);
                      }
                    }
                  } catch (error) {
                    appAlert({
                      content: <ErrorText error={error} />,
                    });

                    return;
                  } finally {
                    setIsLoading(false);
                  }

                  if (Object.keys(changedProps).length) {
                    updateLayoutSection({
                      sectionPath,
                      changedProps,
                    });
                  }

                  close();
                }}
              >
                <ImagePicker
                  accept={['png', 'jpeg']}
                  isLoading={isLoading}
                  defaultImage={src}
                  sizeToImageAspect
                  name="image"
                  onChange={() => setImageChanged(true)}
                />
                <InputLabel
                  tag="div"
                  value={t('itemEdit.inlineImage.scaleImage')}
                  margin={`${FORM_SECTION_SPACING} 0 0`}
                  renderAfter={() => (
                    <Text tag="span" color="#fff">
                      {scaleToWidth(scaleValue)}
                    </Text>
                  )}
                >
                  <RangeSlider
                    testId="inlineImageScaleSlider"
                    value={scaleValue}
                    min={MIN_SCALE}
                    max={MAX_SCALE}
                    step={0.01}
                    onChange={({ value }) => setScaleValue(value)}
                  />
                </InputLabel>
                <InputLabel
                  value="Alt text"
                  margin={`${FORM_SECTION_SPACING} 0 0`}
                  description={t('itemEdit.inlineImage.altTextDescription')}
                >
                  <TextInput
                    value={altText}
                    name="altText"
                    testId="altTextInput"
                    placeholder={t('itemEdit.inlineImage.altText')}
                  />
                </InputLabel>
              </Form>
            );
          },
          [src, srcWidth, srcHeight, isLoading, scaleValue, scale, altText, t]
        )}
      >
        {src && (
          <InlineImageSectionView
            src={src}
            srcWidth={srcWidth}
            srcHeight={srcHeight}
            altText={altText}
            scale={scale}
            marginTop="2.4rem"
            marginBottom="1rem"
          />
        )}
      </EditWrapper>
    </div>
  );
};

export default InlineImageSectionEdit;
