import { __awaiter } from "tslib";
import { useIsomorphicLayoutEffect, useSize, useUnmount } from 'ahooks';
import { AddOutline, CloseOutline } from 'antd-mobile-icons';
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
import { measureCSSLength } from '../../utils/measure-css-length';
import { withNativeProps } from '../../utils/native-props';
import { usePropsValue } from '../../utils/use-props-value';
import { mergeProps } from '../../utils/with-default-props';
import { useConfig } from '../config-provider';
import Grid from '../grid';
import ImageViewer from '../image-viewer';
import Space from '../space';
import PreviewItem from './preview-item';
const classPrefix = `adm-image-uploader`;
const defaultProps = {
  disableUpload: false,
  deletable: true,
  deleteIcon: React.createElement(CloseOutline, {
    className: `${classPrefix}-cell-delete-icon`
  }),
  showUpload: true,
  multiple: false,
  maxCount: 0,
  defaultValue: [],
  accept: 'image/*',
  preview: true,
  showFailed: true,
  imageFit: 'cover'
};
export const ImageUploader = forwardRef((p, ref) => {
  const {
    locale
  } = useConfig();
  const props = mergeProps(defaultProps, p);
  const {
    columns
  } = props;
  const [value, setValue] = usePropsValue(props);
  const [tasks, setTasks] = useState([]);
  const containerRef = useRef(null);
  const containerSize = useSize(containerRef);
  const gapMeasureRef = useRef(null);
  const [cellSize, setCellSize] = useState(80);
  const inputRef = useRef(null);
  useIsomorphicLayoutEffect(() => {
    const gapMeasure = gapMeasureRef.current;
    if (columns && containerSize && gapMeasure) {
      const width = containerSize.width;
      const gap = measureCSSLength(window.getComputedStyle(gapMeasure).getPropertyValue('height'));
      setCellSize((width - gap * (columns - 1)) / columns);
    }
  }, [containerSize === null || containerSize === void 0 ? void 0 : containerSize.width]);
  const style = {
    '--cell-size': cellSize + 'px'
  };
  useIsomorphicLayoutEffect(() => {
    setTasks(prev => prev.filter(task => {
      if (task.url === undefined) return true;
      return !value.some(fileItem => fileItem.url === task.url);
    }));
  }, [value]);
  useIsomorphicLayoutEffect(() => {
    var _a;
    (_a = props.onUploadQueueChange) === null || _a === void 0 ? void 0 : _a.call(props, tasks.map(item => ({
      id: item.id,
      status: item.status
    })));
  }, [tasks]);
  const idCountRef = useRef(0);
  const {
    maxCount,
    onPreview,
    renderItem
  } = props;
  function processFile(file, fileList) {
    return __awaiter(this, void 0, void 0, function* () {
      const {
        beforeUpload
      } = props;
      let transformedFile = file;
      transformedFile = yield beforeUpload === null || beforeUpload === void 0 ? void 0 : beforeUpload(file, fileList);
      return transformedFile;
    });
  }
  function getFinalTasks(tasks) {
    return props.showFailed ? tasks : tasks.filter(task => task.status !== 'fail');
  }
  const finalTasks = getFinalTasks(tasks);
  function onChange(e) {
    return __awaiter(this, void 0, void 0, function* () {
      var _a;
      e.persist();
      const {
        files: rawFiles
      } = e.target;
      if (!rawFiles) return;
      let files = [].slice.call(rawFiles);
      e.target.value = ''; // HACK: fix the same file doesn't trigger onChange
      if (props.beforeUpload) {
        const postFiles = files.map(file => processFile(file, files));
        yield Promise.all(postFiles).then(filesList => {
          files = filesList.filter(Boolean);
        });
      }
      if (files.length === 0) {
        return;
      }
      if (maxCount > 0) {
        const exceed = value.length + files.length + finalTasks.length - maxCount;
        if (exceed > 0) {
          files = files.slice(0, files.length - exceed);
          (_a = props.onCountExceed) === null || _a === void 0 ? void 0 : _a.call(props, exceed);
        }
      }
      const newTasks = files.map(file => ({
        id: idCountRef.current++,
        status: 'pending',
        file
      }));
      setTasks(prev => [...getFinalTasks(prev), ...newTasks]);
      const newVal = [];
      yield Promise.all(newTasks.map((currentTask, index) => __awaiter(this, void 0, void 0, function* () {
        try {
          const result = yield props.upload(currentTask.file);
          newVal[index] = result;
          setTasks(prev => {
            return prev.map(task => {
              if (task.id === currentTask.id) {
                return Object.assign(Object.assign({}, task), {
                  status: 'success',
                  url: result.url
                });
              }
              return task;
            });
          });
        } catch (e) {
          setTasks(prev => {
            return prev.map(task => {
              if (task.id === currentTask.id) {
                return Object.assign(Object.assign({}, task), {
                  status: 'fail'
                });
              }
              return task;
            });
          });
          console.error(e);
        }
      })));
      setValue(prev => prev.concat(newVal).filter(Boolean));
    });
  }
  const imageViewerHandlerRef = useRef(null);
  function previewImage(index) {
    imageViewerHandlerRef.current = ImageViewer.Multi.show({
      images: value.map(fileItem => fileItem.url),
      defaultIndex: index,
      onClose: () => {
        imageViewerHandlerRef.current = null;
      }
    });
  }
  useUnmount(() => {
    var _a;
    (_a = imageViewerHandlerRef.current) === null || _a === void 0 ? void 0 : _a.close();
  });
  const showUpload = props.showUpload && (maxCount === 0 || value.length + finalTasks.length < maxCount);
  const renderImages = () => {
    return value.map((fileItem, index) => {
      var _a, _b;
      const originNode = React.createElement(PreviewItem, {
        key: (_a = fileItem.key) !== null && _a !== void 0 ? _a : index,
        url: (_b = fileItem.thumbnailUrl) !== null && _b !== void 0 ? _b : fileItem.url,
        deletable: props.deletable,
        deleteIcon: props.deleteIcon,
        imageFit: props.imageFit,
        onClick: () => {
          if (props.preview) {
            previewImage(index);
          }
          onPreview && onPreview(index, fileItem);
        },
        onDelete: () => __awaiter(void 0, void 0, void 0, function* () {
          var _c;
          const canDelete = yield (_c = props.onDelete) === null || _c === void 0 ? void 0 : _c.call(props, fileItem);
          if (canDelete === false) return;
          setValue(value.filter((x, i) => i !== index));
        })
      });
      return renderItem ? renderItem(originNode, fileItem, value) : originNode;
    });
  };
  const contentNode = React.createElement(React.Fragment, null, renderImages(), tasks.map(task => {
    if (!props.showFailed && task.status === 'fail') {
      return null;
    }
    return React.createElement(PreviewItem, {
      key: task.id,
      file: task.file,
      deletable: task.status !== 'pending',
      deleteIcon: props.deleteIcon,
      status: task.status,
      imageFit: props.imageFit,
      onDelete: () => {
        setTasks(tasks.filter(x => x.id !== task.id));
      }
    });
  }), React.createElement("div", {
    className: `${classPrefix}-upload-button-wrap`,
    style: showUpload ? undefined : {
      display: 'none'
    }
  }, props.children || React.createElement("span", {
    className: `${classPrefix}-cell ${classPrefix}-upload-button`,
    role: 'button',
    "aria-label": locale.ImageUploader.upload
  }, React.createElement("span", {
    className: `${classPrefix}-upload-button-icon`
  }, React.createElement(AddOutline, null))), !props.disableUpload && React.createElement("input", {
    "aria-label": locale.ImageUploader.upload,
    ref: inputRef,
    capture: props.capture,
    accept: props.accept,
    multiple: props.multiple,
    type: 'file',
    className: `${classPrefix}-input`,
    onChange: onChange
  })));
  useImperativeHandle(ref, () => ({
    get nativeElement() {
      return inputRef.current;
    }
  }));
  return withNativeProps(props, React.createElement("div", {
    className: classPrefix,
    ref: containerRef
  }, columns ? React.createElement(Grid, {
    className: `${classPrefix}-grid`,
    columns: columns,
    style: style
  }, React.createElement("div", {
    className: `${classPrefix}-gap-measure`,
    ref: gapMeasureRef
  }), contentNode.props.children) : React.createElement(Space, {
    className: `${classPrefix}-space`,
    wrap: true,
    block: true
  }, contentNode.props.children)));
});