import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { message } from '@os-design/core';
import { Loading, Picture } from '@os-design/icons';
import { omitEmotionProps, useEvent } from '@os-design/utils';
import { EditorBlock } from 'draft-js';
import React, { useCallback, useRef, useState } from 'react';
import changeBlock from '../utils/changeBlock.js';
import getCurrentBlock from '../utils/getCurrentBlock.js';
import Figure from './Figure.js';
import FigureCaption from './FigureCaption.js';
import type { BlockProps, BlockToolbarItem } from './types.js';

const widthStyles = (p) =>
  p.width &&
  css`
    width: ${p.width}px;
  `;

interface ImageFigureProps {
  width: number;
}
const ImageFigure = styled(Figure, omitEmotionProps('width'))<ImageFigureProps>`
  position: relative;
  display: inline-block;
  max-width: 100%;
  ${widthStyles};
`;

const Mask = styled.div`
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;

  display: flex;
  justify-content: center;
  align-items: center;

  background-color: hsla(
    0,
    0%,
    0%,
    ${(p) => p.theme.editorBlockImageMaskOpacity}
  );
  color: hsl(0, 0%, 100%);
`;

const LoadingIcon = styled(Loading)`
  font-size: ${(p) => p.theme.editorBlockImageLoadingFontSize}em;
`;

const Image = styled.img`
  max-width: 100%;
  max-height: ${(p) => p.theme.editorBlockImageMaxHeight}em;
  vertical-align: bottom;
`;

const ImageBlock: React.FC<BlockProps> = (props) => {
  const { block } = props;
  const data = block.getData();
  const src = data.get('src') as string;
  const loading = data.get('loading') as boolean;

  // Update the width of the image
  const imageRef = useRef<HTMLImageElement>(null);
  const [width, setWidth] = useState(0);
  const updateWidth = useCallback(() => {
    if (!imageRef.current) return;
    setWidth(imageRef.current.width);
  }, []);
  useEvent(
    (typeof window !== 'undefined' ? window : undefined) as EventTarget,
    'resize',
    updateWidth
  );

  if (!src) return null;

  return (
    <ImageFigure width={width}>
      {loading && (
        <Mask>
          <LoadingIcon />
        </Mask>
      )}
      <Image
        src={src.startsWith('blob:') ? src : `${src}-1024`}
        alt={block.getText()}
        onLoad={updateWidth}
        ref={imageRef}
      />
      {!loading && (
        <FigureCaption>
          <EditorBlock {...props} />
        </FigureCaption>
      )}
    </ImageFigure>
  );
};

export const IMAGE_BLOCK = 'atomic:image';

const imageBlock = (
  onImageUpload: (file: File) => Promise<string>
): BlockToolbarItem => ({
  type: IMAGE_BLOCK,
  component: ImageBlock,
  icon: <Picture />,
  onClick: ({ value, onChange, setReadOnly }) => {
    if (!onImageUpload) throw new Error('Specify the onImageUpload method');

    // Not working in mobile Safari.
    // The input must be actually appended to the DOM.
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = 'image/jpeg,image/png,image/webp';
    input.onchange = async (e) => {
      const target = e.target as HTMLInputElement | null;
      if (!target) return;
      const { files } = target;
      if (!files) return;

      setReadOnly(true);
      const file = files[0];
      let src = URL.createObjectURL(file);

      // Add the local image
      const currentBlock = getCurrentBlock(value);
      let nextEditorState = changeBlock(value, currentBlock, IMAGE_BLOCK, {
        src,
        loading: true,
      });
      onChange(nextEditorState);

      // Replace the local image with the remote one
      try {
        src = await onImageUpload(file);
        nextEditorState = changeBlock(value, currentBlock, IMAGE_BLOCK, {
          src,
        });
      } catch (err) {
        if (err instanceof Error) {
          message.error(err.message);
        }
        nextEditorState = changeBlock(value, currentBlock, 'unstyled');
      }

      setReadOnly(false);
      onChange(nextEditorState);
    };
    input.click();
  },
});

export default imageBlock;
