import CloseIcon from '@mui/icons-material/Close';
import PhotoIcon from '@mui/icons-material/Photo';
import Button from '@mui/material/Button';
import IconButton from '@mui/material/IconButton';
import { styled } from '@mui/material/styles';
import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';

import ObjectWidgetTopBar from '@staticcms/core/components/UI/ObjectWidgetTopBar';
import Outline from '@staticcms/core/components/UI/Outline';
import { borders, effects, lengths, shadows } from '@staticcms/core/components/UI/styles';
import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert';
import useUUID from '@staticcms/core/lib/hooks/useUUID';
import { basename, transientOptions } from '@staticcms/core/lib/util';
import { isEmpty } from '@staticcms/core/lib/util/string.util';

import type {
  FileOrImageField,
  GetAssetFunction,
  WidgetControlProps,
} from '@staticcms/core/interface';
import type { FC, MouseEvent, MouseEventHandler } from 'react';

const MAX_DISPLAY_LENGTH = 50;

const StyledFileControlWrapper = styled('div')`
  display: flex;
  flex-direction: column;
  position: relative;
  width: 100%;
`;

interface StyledFileControlContentProps {
  $collapsed: boolean;
}

const StyledFileControlContent = styled(
  'div',
  transientOptions,
)<StyledFileControlContentProps>(
  ({ $collapsed }) => `
    display: flex;
    flex-direction: column;
    gap: 16px;
    ${
      $collapsed
        ? `
          display: none;
        `
        : `
          padding: 16px;
        `
    }
  `,
);

const StyledSelection = styled('div')`
  display: flex;
  flex-direction: column;
`;

const StyledButtonWrapper = styled('div')`
  display: flex;
  gap: 16px;
`;

interface ImageWrapperProps {
  $sortable?: boolean;
}

const ImageWrapper = styled(
  'div',
  transientOptions,
)<ImageWrapperProps>(
  ({ $sortable }) => `
    flex-basis: 155px;
    width: 155px;
    height: 100px;
    margin-right: 20px;
    margin-bottom: 20px;
    border: ${borders.textField};
    border-radius: ${lengths.borderRadius};
    overflow: hidden;
    ${effects.checkerboard};
    ${shadows.inset};
    cursor: ${$sortable ? 'pointer' : 'auto'};
  `,
);

const SortableImageButtonsWrapper = styled('div')`
  display: flex;
  justify-content: center;
  column-gap: 10px;
  margin-right: 20px;
  margin-top: -10px;
  margin-bottom: 10px;
`;

const StyledImage = styled('img')`
  width: 100%;
  height: 100%;
  object-fit: contain;
`;

interface ImageProps {
  src: string;
}

const Image: FC<ImageProps> = ({ src }) => {
  return <StyledImage key="image" role="presentation" src={src} />;
};

interface SortableImageButtonsProps {
  onRemove: MouseEventHandler;
  onReplace: MouseEventHandler;
}

const SortableImageButtons: FC<SortableImageButtonsProps> = ({ onRemove, onReplace }) => {
  return (
    <SortableImageButtonsWrapper key="image-buttons-wrapper">
      <IconButton key="image-replace" onClick={onReplace}>
        <PhotoIcon key="image-replace-icon" />
      </IconButton>
      <IconButton key="image-remove" onClick={onRemove}>
        <CloseIcon key="image-remove-icon" />
      </IconButton>
    </SortableImageButtonsWrapper>
  );
};

interface SortableImageProps {
  itemValue: string;
  getAsset: GetAssetFunction<FileOrImageField>;
  field: FileOrImageField;
  onRemove: MouseEventHandler;
  onReplace: MouseEventHandler;
}

const SortableImage: FC<SortableImageProps> = ({
  itemValue,
  getAsset,
  field,
  onRemove,
  onReplace,
}: SortableImageProps) => {
  const [assetSource, setAssetSource] = useState('');
  useEffect(() => {
    const getImage = async () => {
      const asset = (await getAsset(itemValue, field))?.toString() ?? '';
      setAssetSource(asset);
    };

    getImage();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [itemValue]);

  return (
    <div>
      <ImageWrapper key="image-wrapper" $sortable>
        <Image key="image" src={assetSource} />
      </ImageWrapper>
      <SortableImageButtons
        key="image-buttons"
        onRemove={onRemove}
        onReplace={onReplace}
      ></SortableImageButtons>
    </div>
  );
};

const StyledMultiImageWrapper = styled('div')`
  display: flex;
  flex-wrap: wrap;
`;

const FileLink = styled('a')`
  margin-bottom: 20px;
  font-weight: normal;
  color: inherit;

  &:hover,
  &:active,
  &:focus {
    text-decoration: underline;
  }
`;

const FileLinks = styled('div')`
  margin-bottom: 12px;
`;

const FileLinkList = styled('ul')`
  list-style-type: none;
`;

function isMultiple(value: string | string[] | null | undefined): value is string[] {
  return Array.isArray(value);
}

export function getValidFileValue(value: string | string[] | null | undefined) {
  if (value) {
    return isMultiple(value) ? value.map(v => basename(v)) : basename(value);
  }

  return value;
}

export interface WithFileControlProps {
  forImage?: boolean;
}

const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => {
  const FileControl: FC<WidgetControlProps<string | string[], FileOrImageField>> = memo(
    ({
      value,
      field,
      onChange,
      openMediaLibrary,
      clearMediaControl,
      removeMediaControl,
      getAsset,
      hasErrors,
      t,
    }) => {
      const controlID = useUUID();
      const [collapsed, setCollapsed] = useState(false);
      const [internalValue, setInternalValue] = useState(value ?? '');

      const handleOnChange = useCallback(
        (newValue: string | string[]) => {
          if (newValue !== internalValue) {
            setInternalValue(newValue);
            setTimeout(() => {
              onChange(newValue);
            });
          }
        },
        [internalValue, onChange],
      );

      const handleOpenMediaLibrary = useMediaInsert(
        internalValue,
        { field, controlID },
        handleOnChange,
      );

      const handleCollapseToggle = useCallback(() => {
        setCollapsed(!collapsed);
      }, [collapsed]);

      useEffect(() => {
        return () => {
          removeMediaControl(controlID);
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, []);

      const mediaLibraryFieldOptions = useMemo(() => {
        return field.media_library ?? {};
      }, [field.media_library]);

      const config = useMemo(
        () => ('config' in mediaLibraryFieldOptions ? mediaLibraryFieldOptions.config : undefined),
        [mediaLibraryFieldOptions],
      );

      const allowsMultiple = useMemo(() => {
        return config?.multiple ?? false;
      }, [config?.multiple]);

      const chooseUrl = useMemo(
        () =>
          'choose_url' in mediaLibraryFieldOptions && (mediaLibraryFieldOptions.choose_url ?? true),
        [mediaLibraryFieldOptions],
      );

      const handleUrl = useCallback(
        (subject: 'image' | 'file') => (e: MouseEvent) => {
          e.preventDefault();

          const url = window.prompt(t(`editor.editorWidgets.${subject}.promptUrl`));

          handleOnChange(url ?? '');
        },
        [handleOnChange, t],
      );

      const handleRemove = useCallback(
        (e: MouseEvent) => {
          e.preventDefault();
          clearMediaControl(controlID);
          handleOnChange('');
        },
        [clearMediaControl, controlID, handleOnChange],
      );

      const onRemoveOne = useCallback(
        (index: number) => () => {
          if (Array.isArray(internalValue)) {
            const newValue = [...internalValue];
            newValue.splice(index, 1);
            handleOnChange(newValue);
          }
        },
        [handleOnChange, internalValue],
      );

      const onReplaceOne = useCallback(
        (index: number) => () => {
          return openMediaLibrary({
            controlID,
            forImage,
            value: internalValue,
            replaceIndex: index,
            allowMultiple: false,
            config,
            field,
          });
        },
        [config, controlID, field, openMediaLibrary, internalValue],
      );

      // TODO Readd when multiple uploads is supported
      // const onSortEnd = useCallback(
      //   ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
      //     if (Array.isArray(internalValue)) {
      //       const newValue = arrayMoveImmutable(internalValue, oldIndex, newIndex);
      //       handleOnChange(newValue);
      //     }
      //   },
      //   [handleOnChange, internalValue],
      // );

      const renderFileLink = useCallback((link: string | undefined | null) => {
        const size = MAX_DISPLAY_LENGTH;
        if (!link || link.length <= size) {
          return link;
        }
        const text = `${link.slice(0, size / 2)}\u2026${link.slice(-(size / 2) + 1)}`;
        return (
          <FileLink key={`file-link-${text}`} href={link} rel="noopener" target="_blank">
            {text}
          </FileLink>
        );
      }, []);

      const [assetSource, setAssetSource] = useState('');
      useEffect(() => {
        if (Array.isArray(internalValue)) {
          return;
        }

        const getImage = async () => {
          const newValue = (await getAsset(internalValue, field))?.toString() ?? '';
          if (newValue !== internalValue) {
            setAssetSource(newValue);
          }
        };

        getImage();
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, [internalValue]);

      const renderedImagesLinks = useMemo(() => {
        if (forImage) {
          if (!internalValue) {
            return null;
          }

          if (isMultiple(internalValue)) {
            return (
              <StyledMultiImageWrapper key="multi-image-wrapper">
                {internalValue.map((itemValue, index) => (
                  <SortableImage
                    key={`item-${itemValue}`}
                    itemValue={itemValue}
                    getAsset={getAsset}
                    field={field}
                    onRemove={onRemoveOne(index)}
                    onReplace={onReplaceOne(index)}
                  />
                ))}
              </StyledMultiImageWrapper>
            );
          }

          return (
            <ImageWrapper key="single-image-wrapper">
              <Image key="single-image" src={assetSource} />
            </ImageWrapper>
          );
        }

        if (isMultiple(internalValue)) {
          return (
            <FileLinks key="mulitple-file-links">
              <FileLinkList key="file-links-list">
                {internalValue.map(val => (
                  <li key={val}>{renderFileLink(val)}</li>
                ))}
              </FileLinkList>
            </FileLinks>
          );
        }

        return <FileLinks key="single-file-links">{renderFileLink(internalValue)}</FileLinks>;
      }, [assetSource, field, getAsset, internalValue, onRemoveOne, onReplaceOne, renderFileLink]);

      const content = useMemo(() => {
        const subject = forImage ? 'image' : 'file';

        if (Array.isArray(internalValue) ? internalValue.length === 0 : isEmpty(internalValue)) {
          return (
            <StyledButtonWrapper>
              <Button
                color="primary"
                variant="outlined"
                key="upload"
                onClick={handleOpenMediaLibrary}
              >
                {t(`editor.editorWidgets.${subject}.choose${allowsMultiple ? 'Multiple' : ''}`)}
              </Button>
              {chooseUrl ? (
                <Button
                  color="primary"
                  variant="outlined"
                  key="choose-url"
                  onClick={handleUrl(subject)}
                >
                  {t(`editor.editorWidgets.${subject}.chooseUrl`)}
                </Button>
              ) : null}
            </StyledButtonWrapper>
          );
        }

        return (
          <StyledSelection key="selection">
            {renderedImagesLinks}
            <StyledButtonWrapper key="controls">
              <Button
                color="primary"
                variant="outlined"
                key="add-replace"
                onClick={handleOpenMediaLibrary}
              >
                {t(
                  `editor.editorWidgets.${subject}.${
                    allowsMultiple ? 'addMore' : 'chooseDifferent'
                  }`,
                )}
              </Button>
              {chooseUrl && !allowsMultiple ? (
                <Button
                  color="primary"
                  variant="outlined"
                  key="replace-url"
                  onClick={handleUrl(subject)}
                >
                  {t(`editor.editorWidgets.${subject}.replaceUrl`)}
                </Button>
              ) : null}
              <Button color="error" variant="outlined" key="remove" onClick={handleRemove}>
                {t(`editor.editorWidgets.${subject}.remove${allowsMultiple ? 'All' : ''}`)}
              </Button>
            </StyledButtonWrapper>
          </StyledSelection>
        );
      }, [
        internalValue,
        renderedImagesLinks,
        handleOpenMediaLibrary,
        t,
        allowsMultiple,
        chooseUrl,
        handleUrl,
        handleRemove,
      ]);

      return useMemo(
        () => (
          <StyledFileControlWrapper key="file-control-wrapper">
            <ObjectWidgetTopBar
              key="file-control-top-bar"
              collapsed={collapsed}
              onCollapseToggle={handleCollapseToggle}
              heading={field.label ?? field.name}
              hasError={hasErrors}
              t={t}
            />
            <StyledFileControlContent $collapsed={collapsed}>{content}</StyledFileControlContent>
            <Outline hasError={hasErrors} />
          </StyledFileControlWrapper>
        ),
        [collapsed, content, field.label, field.name, handleCollapseToggle, hasErrors, t],
      );
    },
  );

  FileControl.displayName = 'FileControl';

  return FileControl;
};

export default withFileControl;
