import {
  computed,
  defineComponent,
  onBeforeUnmount,
  onMounted,
  shallowRef,
  watch,
  Transition,
} from 'vue';
import type { ExtractPropTypes, CSSProperties } from 'vue';
import EyeOutlined from '@ant-design/icons-vue/EyeOutlined';
import DeleteOutlined from '@ant-design/icons-vue/DeleteOutlined';
import DownloadOutlined from '@ant-design/icons-vue/DownloadOutlined';
import Tooltip from '../../tooltip';
import Progress from '../../progress';

import type {
  ItemRender,
  UploadFile,
  UploadListProgressProps,
  UploadListType,
  UploadLocale,
} from '../interface';
import type { VueNode } from '../../_util/type';
import useConfigInject from '../../config-provider/hooks/useConfigInject';
import { getTransitionProps } from '../../_util/transition';
import { booleanType, stringType, functionType, arrayType, objectType } from '../../_util/type';

export const listItemProps = () => {
  return {
    prefixCls: String,
    locale: objectType<UploadLocale>(undefined as UploadLocale),
    file: objectType<UploadFile>(),
    items: arrayType<UploadFile[]>(),
    listType: stringType<UploadListType>(),
    isImgUrl: functionType<(file: UploadFile) => boolean>(),

    showRemoveIcon: booleanType(),
    showDownloadIcon: booleanType(),
    showPreviewIcon: booleanType(),
    removeIcon: functionType<(opt: { file: UploadFile }) => VueNode>(),
    downloadIcon: functionType<(opt: { file: UploadFile }) => VueNode>(),
    previewIcon: functionType<(opt: { file: UploadFile }) => VueNode>(),

    iconRender: functionType<(opt: { file: UploadFile }) => VueNode>(),
    actionIconRender:
      functionType<
        (opt: {
          customIcon: VueNode;
          callback: () => void;
          prefixCls: string;
          title?: string | undefined;
        }) => VueNode
      >(),
    itemRender: functionType<ItemRender>(),
    onPreview: functionType<(file: UploadFile, e: Event) => void>(),
    onClose: functionType<(file: UploadFile) => void>(),
    onDownload: functionType<(file: UploadFile) => void>(),
    progress: objectType<UploadListProgressProps>(),
  };
};

export type ListItemProps = Partial<ExtractPropTypes<ReturnType<typeof listItemProps>>>;
export default defineComponent({
  name: 'ListItem',
  inheritAttrs: false,
  props: listItemProps(),
  setup(props, { slots, attrs }) {
    const showProgress = shallowRef(false);
    const progressRafRef = shallowRef();
    onMounted(() => {
      progressRafRef.value = setTimeout(() => {
        showProgress.value = true;
      }, 300);
    });
    onBeforeUnmount(() => {
      clearTimeout(progressRafRef.value);
    });
    const mergedStatus = shallowRef(props.file?.status);
    watch(
      () => props.file?.status,
      status => {
        if (status !== 'removed') {
          mergedStatus.value = status;
        }
      },
    );
    const { rootPrefixCls } = useConfigInject('upload', props);
    const transitionProps = computed(() => getTransitionProps(`${rootPrefixCls.value}-fade`));
    return () => {
      const {
        prefixCls,
        locale,
        listType,
        file,
        items,
        progress: progressProps,
        iconRender = slots.iconRender,
        actionIconRender = slots.actionIconRender,
        itemRender = slots.itemRender,
        isImgUrl,
        showPreviewIcon,
        showRemoveIcon,
        showDownloadIcon,
        previewIcon: customPreviewIcon = slots.previewIcon,
        removeIcon: customRemoveIcon = slots.removeIcon,
        downloadIcon: customDownloadIcon = slots.downloadIcon,
        onPreview,
        onDownload,
        onClose,
      } = props;
      const { class: className, style } = attrs;
      // This is used for legacy span make scrollHeight the wrong value.
      // We will force these to be `display: block` with non `picture-card`

      const iconNode = iconRender({ file });
      let icon = <div class={`${prefixCls}-text-icon`}>{iconNode}</div>;
      if (listType === 'picture' || listType === 'picture-card') {
        if (mergedStatus.value === 'uploading' || (!file.thumbUrl && !file.url)) {
          const uploadingClassName = {
            [`${prefixCls}-list-item-thumbnail`]: true,
            [`${prefixCls}-list-item-file`]: mergedStatus.value !== 'uploading',
          };
          icon = <div class={uploadingClassName}>{iconNode}</div>;
        } else {
          const thumbnail = isImgUrl?.(file) ? (
            <img
              src={file.thumbUrl || file.url}
              alt={file.name}
              class={`${prefixCls}-list-item-image`}
              crossorigin={file.crossOrigin}
            />
          ) : (
            iconNode
          );
          const aClassName = {
            [`${prefixCls}-list-item-thumbnail`]: true,
            [`${prefixCls}-list-item-file`]: isImgUrl && !isImgUrl(file),
          };
          icon = (
            <a
              class={aClassName}
              onClick={e => onPreview(file, e)}
              href={file.url || file.thumbUrl}
              target="_blank"
              rel="noopener noreferrer"
            >
              {thumbnail}
            </a>
          );
        }
      }

      const infoUploadingClass = {
        [`${prefixCls}-list-item`]: true,
        [`${prefixCls}-list-item-${mergedStatus.value}`]: true,
      };
      const linkProps =
        typeof file.linkProps === 'string' ? JSON.parse(file.linkProps) : file.linkProps;

      const removeIcon = showRemoveIcon
        ? actionIconRender({
            customIcon: customRemoveIcon ? customRemoveIcon({ file }) : <DeleteOutlined />,
            callback: () => onClose(file),
            prefixCls,
            title: locale.removeFile,
          })
        : null;
      const downloadIcon =
        showDownloadIcon && mergedStatus.value === 'done'
          ? actionIconRender({
              customIcon: customDownloadIcon ? customDownloadIcon({ file }) : <DownloadOutlined />,
              callback: () => onDownload(file),
              prefixCls,
              title: locale.downloadFile,
            })
          : null;
      const downloadOrDelete = listType !== 'picture-card' && (
        <span
          key="download-delete"
          class={[
            `${prefixCls}-list-item-actions`,
            {
              picture: listType === 'picture',
            },
          ]}
        >
          {downloadIcon}
          {removeIcon}
        </span>
      );
      const listItemNameClass = `${prefixCls}-list-item-name`;
      const fileName = file.url
        ? [
            <a
              key="view"
              target="_blank"
              rel="noopener noreferrer"
              class={listItemNameClass}
              title={file.name}
              {...linkProps}
              href={file.url}
              onClick={e => onPreview(file, e)}
            >
              {file.name}
            </a>,
            downloadOrDelete,
          ]
        : [
            <span
              key="view"
              class={listItemNameClass}
              onClick={e => onPreview(file, e)}
              title={file.name}
            >
              {file.name}
            </span>,
            downloadOrDelete,
          ];
      const previewStyle: CSSProperties = {
        pointerEvents: 'none',
        opacity: 0.5,
      };
      const previewIcon = showPreviewIcon ? (
        <a
          href={file.url || file.thumbUrl}
          target="_blank"
          rel="noopener noreferrer"
          style={file.url || file.thumbUrl ? undefined : previewStyle}
          onClick={e => onPreview(file, e)}
          title={locale.previewFile}
        >
          {customPreviewIcon ? customPreviewIcon({ file }) : <EyeOutlined />}
        </a>
      ) : null;

      const pictureCardActions = listType === 'picture-card' &&
        mergedStatus.value !== 'uploading' && (
          <span class={`${prefixCls}-list-item-actions`}>
            {previewIcon}
            {mergedStatus.value === 'done' && downloadIcon}
            {removeIcon}
          </span>
        );

      const dom = (
        <div class={infoUploadingClass}>
          {icon}
          {fileName}
          {pictureCardActions}
          {showProgress.value && (
            <Transition {...transitionProps.value}>
              <div
                v-show={mergedStatus.value === 'uploading'}
                class={`${prefixCls}-list-item-progress`}
              >
                {'percent' in file ? (
                  <Progress
                    {...(progressProps as UploadListProgressProps)}
                    type="line"
                    percent={file.percent}
                  />
                ) : null}
              </div>
            </Transition>
          )}
        </div>
      );
      const listContainerNameClass = {
        [`${prefixCls}-list-item-container`]: true,
        [`${className}`]: !!className,
      };
      const message =
        file.response && typeof file.response === 'string'
          ? file.response
          : file.error?.statusText || file.error?.message || locale.uploadError;
      const item =
        mergedStatus.value === 'error' ? (
          <Tooltip title={message} getPopupContainer={node => node.parentNode as HTMLElement}>
            {dom}
          </Tooltip>
        ) : (
          dom
        );

      return (
        <div class={listContainerNameClass} style={style as CSSProperties}>
          {itemRender
            ? itemRender({
                originNode: item,
                file,
                fileList: items,
                actions: {
                  download: onDownload.bind(null, file),
                  preview: onPreview.bind(null, file),
                  remove: onClose.bind(null, file),
                },
              })
            : item}
        </div>
      );
    };
  },
});
