import React, { Component, createElement, CSSProperties, SyntheticEvent } from 'react';
import classNames from 'classnames';
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
import isFunction from 'lodash/isFunction';
import Icon from '../icon';
import Tooltip from '../tooltip';
import Progress from '../progress';
import { UploadFile, UploadListIconFunc, UploadListReUploadIconFunc, UploadListProps, UploadListType, ShowReUploadIconType } from './interface';
import Animate from '../animate';
import PopConfirm from '../popconfirm';
import { ProgressType } from '../progress/enum';
import { getFileSizeStr, getFileType, isImageUrl, previewImage } from './utils';
import CompressedfileIcon from './icon-svg/compressedfileIcon';
import DocIcon from './icon-svg/docIcon';
import FileuploadIcon from './icon-svg/fileuploadIcon';
import ImageIcon from './icon-svg/imageIcon';
import PdfIcon from './icon-svg/pdfIcon';
import XlsIcon from './icon-svg/xlsIcon';
import { Size } from '../_util/enum';
import ConfigContext, { ConfigContextValue } from '../config-provider/ConfigContext';

// a little function to help us with reordering the result
const reorder = (list, startIndex, endIndex): UploadFile[] => {
  const result: UploadFile[] = Array.from(list);
  const [removed] = result.splice(startIndex, 1);
  result.splice(endIndex, 0, removed);

  return result;
};

function defaultRenderIcon(file: UploadFile, listType: UploadListType, prefixCls?: string) {
  if (listType === 'picture' || listType === 'picture-card') {
    return <Icon type="insert_drive_file" className={`${prefixCls}-list-item-icon`} />;
  }
  if (file.status === 'uploading') {
    return <Progress key='loading' type={ProgressType.loading} size={Size.small} />;
  }
  const filetype = getFileType(file.name);
  switch (filetype) {
    case 'compressedfile':
      return <CompressedfileIcon className={`${prefixCls}-icon-file`} />;
    case 'doc':
      return <DocIcon className={`${prefixCls}-icon-file`} />;
    case 'image':
      return <ImageIcon className={`${prefixCls}-icon-file`} />;
    case 'pdf':
      return <PdfIcon className={`${prefixCls}-icon-file`} />;
    case 'xls':
      return <XlsIcon className={`${prefixCls}-icon-file`} />;
    default:
      return <FileuploadIcon className={`${prefixCls}-icon-file`} />;
  }
}

export default class UploadList extends Component<UploadListProps, any> {
  static displayName = 'UploadList';

  static get contextType(): typeof ConfigContext {
    return ConfigContext;
  }

  static defaultProps = {
    listType: 'text',
    progressAttr: {
      strokeWidth: 2,
      showInfo: false,
    },
    previewFile: previewImage,
    showRemoveIcon: true,
    showPreviewIcon: true,
    dragUploadList: false,
    showFileSize: false,
    showDownloadIcon: true,
    showReUploadIcon: false,
    downloadPropsIntercept: o => o,
  };

  context: ConfigContextValue;

  handleClose = (file: UploadFile) => {
    const { onRemove } = this.props;
    if (onRemove) {
      onRemove(file);
    }
  };

  handlePreview = (file: UploadFile, e: SyntheticEvent<HTMLElement>) => {
    const { onPreview } = this.props;
    if (!onPreview) {
      return;
    }
    e.preventDefault();
    return onPreview(file);
  };

  /**
   * @param {UploadFile} file
   * @param {React.SyntheticEvent<HTMLElement>} e
   */
  handleReUpload = (file: UploadFile, e: React.SyntheticEvent<HTMLElement>) => {
    const { onReUpload } = this.props;
    if (!onReUpload) {
      return;
    }
    e.preventDefault();
    onReUpload(file);
  };

  handleReUploadConfirm = (fileTarget: UploadFile, e: React.SyntheticEvent<HTMLElement>)=>{
    if (fileTarget.status === 'error') {
      this.handleReUpload(fileTarget, e);
    } else if (fileTarget.status && ['success','done','removed'].includes(fileTarget.status)) {
      const { getUploadRef, setReplaceReuploadItem } = this.props;
      const uploadRef = getUploadRef();
      if (uploadRef) {
        const { fileInput } = uploadRef.uploader;
        setReplaceReuploadItem(fileTarget);
        fileInput.click();
      }
    }
  }

  componentDidUpdate() {
    const { listType } = this.props;
    if (listType !== 'picture' && listType !== 'picture-card') {
      return;
    }
    const { items, previewFile } = this.props;
    (items || []).forEach(file => {
      if (
        typeof document === 'undefined' ||
        typeof window === 'undefined' ||
        !(window as any).FileReader ||
        !(window as any).File ||
        !(file.originFileObj instanceof File || file.originFileObj instanceof Blob) ||
        file.thumbUrl !== undefined
      ) {
        return;
      }
      file.thumbUrl = '';
      if (previewFile) {
        previewFile(file.originFileObj as File).then((previewDataUrl: string) => {
          // Need append '' to avoid dead loop
          file.thumbUrl = previewDataUrl || '';
          this.forceUpdate();
        });
      }
    });
  }

  /**
   * 拖拽事件
   * @param result
   */
  onDragEnd = (result) => {
    const { items, onDragEnd } = this.props;
    // dropped outside the list
    if (!result.destination) {
      return;
    }

    const dragItems = reorder(
      items,
      result.source.index,
      result.destination.index,
    );

    onDragEnd(dragItems);
  };

  render() {
    const {
      prefixCls: customizePrefixCls,
      items = [],
      listType,
      showPreviewIcon,
      showRemoveIcon,
      showDownloadIcon,
      showReUploadIcon,
      removePopConfirmTitle,
      reUploadText,
      reUploadPopConfirmTitle,
      locale,
      dragUploadList,
      showFileSize,
      getCustomFilenameTitle,
      downloadPropsIntercept,
      tooltipPrefixCls,
      popconfirmProps,
      renderIcon = defaultRenderIcon,
    } = this.props;
    const { getPrefixCls } = this.context;
    const prefixCls = getPrefixCls('upload', customizePrefixCls);
    const list = items.map((file, index) => {
      let progress;
      let icon = renderIcon(file, listType!, prefixCls);
      const stat = {
        isImg: isImageUrl(file),
        isUploading: file.status === 'uploading',
        isError: file.status === 'error',
        isPictureCard: listType === 'picture-card',
      };
      if (listType === 'picture' || stat.isPictureCard) {
        if (stat.isPictureCard && stat.isUploading) {
          icon = <div className={`${prefixCls}-list-item-uploading-text`}>{locale.uploading}</div>;
        } else if (listType === 'picture' && stat.isUploading) {
          icon = <Progress key='loading' type={ProgressType.loading} size={Size.small} />;
        } else if (!file.thumbUrl && !file.url) {
          icon = <Icon className={`${prefixCls}-list-item-thumbnail`} type="picture" />;
        } else {
          const thumbnail = stat.isImg ? (
            <img src={file.thumbUrl || file.url} alt={file.name} />
          ) : icon;
          icon = (
            <a
              className={`${prefixCls}-list-item-thumbnail`}
              onClick={e => this.handlePreview(file, e)}
              href={file.url || file.thumbUrl}
              target="_blank"
              rel="noopener noreferrer"
            >
              {thumbnail}
            </a>
          );
        }
      }

      if (stat.isUploading) {
        const { progressAttr } = this.props;
        // show loading icon if upload progress listener is disabled
        const loadingProgress =
          'percent' in file ? (
            <Progress type={ProgressType.line} {...progressAttr} percent={file.percent} />
          ) : null;

        progress = (
          <div className={`${prefixCls}-list-item-progress`} key="progress">
            {loadingProgress}
          </div>
        );
      }
      const message = file.response && typeof file.response === 'string' ? file.response : ((file.error && file.error.statusText) || locale.uploadError);
      const preview = file.url ? (
        <a
          href={file.url}
          {...file.linkProps}
          target="_blank"
          rel="noopener noreferrer"
          className={`${prefixCls}-list-item-name`}
          onClick={e => this.handlePreview(file, e)}
          title={file.name}
        >
          {file.name}
        </a>
      ) : (
        <span
          className={`${prefixCls}-list-item-name`}
          onClick={e => this.handlePreview(file, e)}
          title={file.name}
        >
          {file.name}
        </span>
      );
      const style =
        file.url || file.thumbUrl
          ? undefined
          : ({
            pointerEvents: 'none',
            opacity: 0.5,
          } as CSSProperties);
      const previewIcon = (
        isFunction(showPreviewIcon)
          ? (showPreviewIcon as UploadListIconFunc)(file)
          : stat.isImg && showPreviewIcon
      ) ? (
          <a
            href={file.url || file.thumbUrl}
            target="_blank"
            rel="noopener noreferrer"
            style={style}
            onClick={e => this.handlePreview(file, e)}
            title={locale.previewFile}
          >
            <Icon type="visibility" />
          </a>
        ) : null;
      let showReUploadIconType: ShowReUploadIconType | undefined;
      if (isFunction(showReUploadIcon)) {
        const customReUploadIcon: boolean | 'text' = (showReUploadIcon as UploadListReUploadIconFunc)(file, listType!);
        showReUploadIconType = customReUploadIcon ? customReUploadIcon === 'text' ? 'text' : 'icon' : undefined;
      } else if (showReUploadIcon) {
        showReUploadIconType = showReUploadIcon === 'text' ? 'text' : 'icon';
      }
      const pictureCardReUploadIconOrText = showReUploadIconType && !stat.isUploading ? (
        <PopConfirm
          {...popconfirmProps}
          title={reUploadPopConfirmTitle || locale.confirmReUpload}
          onConfirm={(e) => {
            this.handleReUploadConfirm(file, e);
          }}
        >
          {showReUploadIconType === 'icon' ? (
            <Icon
              type="file_upload"
              title={reUploadText}
            />
          ) : (
            stat.isError ?
              <Tooltip prefixCls={tooltipPrefixCls} title={message}>
                <span className={`${prefixCls}-list-item-reupload-${listType}`} title={reUploadText}>{locale.reUpload}</span>
              </Tooltip> :
              <span className={`${prefixCls}-list-item-reupload-${listType}`} title={reUploadText}>{locale.reUpload}</span>
          )}
        </PopConfirm>
      ) : null;
      const removeIcon = showRemoveIcon ? (
        <PopConfirm
          {...popconfirmProps}
          title={removePopConfirmTitle || locale.confirmRemove}
          onConfirm={() => {
            this.handleClose(file);
          }}
        >
          <Icon
            type={stat.isPictureCard ? 'delete' : 'close'}
            className={stat.isPictureCard ? `${prefixCls}-list-item-action-remove` : undefined}
            title={locale.removeFile}
          />
        </PopConfirm>
      ) : null;
      const downloadLinkProps: any = {
        ...file.linkProps,
        rel: 'noopener noreferrer',
      };
      if (stat.isError) {
        downloadLinkProps.style = { color: '#f5222d' };
      }
      if (!(stat.isError || stat.isUploading)) {
        downloadLinkProps.href = file.url || file.thumbUrl;
        downloadLinkProps.target = '_blank';
        if (downloadLinkProps.href) {
          downloadLinkProps.filename = file.name;
        }
      }
      const downloadIcon =
        !stat.isError && !stat.isUploading && (isFunction(showDownloadIcon) ? (showDownloadIcon as UploadListIconFunc)(file) : showDownloadIcon)
        && <a {...downloadPropsIntercept!(downloadLinkProps)} style={style}><Icon type="get_app" /></a>;
      const actionsClass = classNames(`${prefixCls}-list-item-actions`, {
        [`${prefixCls}-list-item-actions-reupload-text`]: showReUploadIconType === 'text' && stat.isError,
      });

      const fileName = (stat.isPictureCard && !stat.isUploading) ? (
        <Tooltip prefixCls={tooltipPrefixCls} title={getCustomFilenameTitle ? getCustomFilenameTitle(file) : file.name} placement="bottom">
          {
            createElement(downloadLinkProps.href ? 'a' : 'span', {
              className: `${prefixCls}-list-item-file-name`,
              ...downloadPropsIntercept!(downloadLinkProps),
            }, file.name)
          }
        </Tooltip>
      ) : null;
      const filesizeStr = getFileSizeStr(file.size);
      const fileSize =
        (showFileSize && filesizeStr !== '' && listType === 'text') ? (
          <span className={`${prefixCls}-list-item-info-filesize`}>
            {filesizeStr}
          </span>
        ) : null;

      const iconAndPreview = stat.isPictureCard ? (
        <span className={`${prefixCls}-list-item-picture-card`}>
          {icon}
        </span>
      ) : <span className={`${prefixCls}-list-item-text`}>{icon}{preview}</span>;

      const iconAndPreviewTooltip =
        stat.isError ? (
          <Tooltip prefixCls={tooltipPrefixCls} title={message}>
            {iconAndPreview}
          </Tooltip>
        ) : (
          iconAndPreview
        );

      const reUpload = showReUploadIconType && (listType === 'text' || listType === 'picture') && !stat.isUploading ? (
        <PopConfirm
          {...popconfirmProps}
          title={reUploadPopConfirmTitle || locale.confirmReUpload}
          onConfirm={(e) => {
            this.handleReUploadConfirm(file, e);
          }}
        >
          <span className={`${prefixCls}-list-item-reupload-${listType}`} title={reUploadText}>{locale.reUpload}</span>
        </PopConfirm>
      ) : null;

      const getActions = ()=>{
        if(!stat.isUploading) {
          if(stat.isPictureCard) {
            return (
              <div className={actionsClass}>
                <span className={`${prefixCls}-reupload-action`}>{pictureCardReUploadIconOrText}</span>
                <span className={`${prefixCls}-other-actions`}>{stat.isError || previewIcon}{downloadIcon}{removeIcon}</span>
              </div>
            )
          }
          return (
            <div className={`${prefixCls}-actions`}>{reUpload}{stat.isError || previewIcon}{downloadIcon}{removeIcon}</div>
          )
        }
        return  <div className={`${prefixCls}-actions`}>{removeIcon}</div>;
      }

      const listItemInfo = (
        <div className={`${prefixCls}-list-item-info`}>
          <span className={`${prefixCls}-list-item-span`}>
            {iconAndPreviewTooltip}
            {fileName}
            {fileSize}
            {getActions()}
          </span>
          <Animate transitionName="fade" component="">
            {progress}
          </Animate>
        </div>
      );
      const infoUploadingClass = classNames({
        [`${prefixCls}-list-item`]: true,
        [`${prefixCls}-list-item-${file.status}`]: true,
        [`${prefixCls}-list-item-error-reupload`]: file.status === 'error' && showReUploadIconType,
        [`${prefixCls}-list-item-done-reupload`]: file.status === 'done' && showReUploadIconType,
      });

      if (dragUploadList) {
        return (
          <Draggable key={file.uid} draggableId={String(file.uid)} index={index}>
            {provided => (
              <div
                className={infoUploadingClass}
                key={file.uid}
                ref={provided.innerRef}
                {...provided.draggableProps}
                {...provided.dragHandleProps}
              >
                {listItemInfo}
              </div>
            )}
          </Draggable>
        );
      }

      return (
        <div className={infoUploadingClass} key={file.uid}>
          {listItemInfo}
        </div>
      );
    });
    const listClassNames = classNames({
      [`${prefixCls}-list`]: true,
      [`${prefixCls}-list-${listType}`]: true,
      [`${prefixCls}-list-drag`]: dragUploadList,
    });
    const animationDirection = listType === 'picture-card' ? 'animate-inline' : 'animate';
    if (dragUploadList) {
      return (
        <DragDropContext onDragEnd={this.onDragEnd}>
          <Droppable droppableId="droppable" direction="horizontal">
            {(provided, snapshot) => (
              <div
                ref={provided.innerRef}
                style={{
                  background: snapshot.isDraggingOver ? '#f2f9f4' : '',
                  border: snapshot.isDraggingOver ? '2px dashed #1ab16f' : '',
                  display: 'inline-flex',
                  maxWidth: '100%',
                  flexWrap: 'wrap',
                  overflow: 'auto',
                }}
                {...provided.droppableProps}
                className={listClassNames}
              >
                {list}
                {provided.placeholder}
              </div>
            )}
          </Droppable>
        </DragDropContext>
      );
    }
    return (
      <Animate
        transitionName={`${prefixCls}-${animationDirection}`}
        component="div"
        className={listClassNames}
      >
        {list}
      </Animate>
    );
  }
}