<template>
  <BasicModal
    v-bind="$attrs"
    @register="registerModal"
    :title="L('Objects:UploadFile')"
    :width="800"
    :min-height="300"
  >
    <BasicTable @register="registerTable">
      <template #toolbar>
        <input ref="btnRef" style="display: none" />
        <a-button type="primary" @click="handleSelect">{{ L('Upload:SelectFile') }}</a-button>
      </template>
      <template #bodyCell="{ column, record }">
        <template v-if="column.key === 'size'">
          <span>{{ fileSize(record.size) }}</span>
        </template>
        <template v-else-if="column.key === 'status'">
          <Tag v-if="record.completed" color="green">{{ L('Upload:Completed') }}</Tag>
          <Tooltip v-else-if="record.error" :title="record.errorMsg">
            <Tag color="red">{{ L('Upload:Error') }}</Tag>
          </Tooltip>
          <Tag v-else-if="record.paused" color="orange">{{ L('Upload:Pause') }}</Tag>
          <span v-else>{{ record.progress }} {{ averageSpeed(record.averageSpeed) }}</span>
        </template>
        <template v-else-if="column.key === 'action'">
          <TableAction
            :stop-button-propagation="true"
            :actions="[
              {
                ifShow: !record.completed,
                color: 'warning',
                label: '',
                icon: record.paused
                  ? 'ant-design:caret-right-outlined'
                  : 'ant-design:pause-outlined',
                onClick: record.paused
                  ? handleResume.bind(null, record)
                  : handlePause.bind(null, record),
              },
              {
                color: 'error',
                label: '',
                icon: 'ant-design:delete-outlined',
                onClick: handleCancel.bind(null, record),
              },
            ]"
          />
        </template>
      </template>
    </BasicTable>
  </BasicModal>
</template>

<script lang="ts" setup>
  import { computed, ref, unref, onMounted, onUnmounted, watch } from 'vue';
  import { useLocalization } from '/@/hooks/abp/useLocalization';
  import { Tag, Tooltip } from 'ant-design-vue';
  import { BasicModal, useModalInner } from '/@/components/Modal';
  import { BasicTable, TableAction, useTable } from '/@/components/Table';
  import { uploadUrl } from '/@/api/oss-management/objects';
  import { useUserStoreWithOut } from '/@/store/modules/user';
  import { Result } from '/#/axios';
  import Uploader from 'simple-uploader.js';

  const emits = defineEmits(['file:uploaded', 'register']);

  let uploader: any = null;
  const { L } = useLocalization(['AbpOssManagement', 'AbpUi']);
  const bucket = ref('');
  const path = ref('');
  const btnRef = ref<any>();
  const [registerModal] = useModalInner((data) => {
    path.value = data.path;
    bucket.value = data.bucket;
  });
  const fileList = ref<Recordable[]>([]);
  const [registerTable] = useTable({
    rowKey: 'id',
    columns: [
      {
        title: 'id',
        dataIndex: 'id',
        width: 1,
        ifShow: false,
      },
      {
        title: L('DisplayName:Name'),
        dataIndex: 'name',
        align: 'left',
        width: 300,
        sorter: true,
      },
      {
        title: L('DisplayName:Size'),
        dataIndex: 'size',
        align: 'left',
        width: 100,
        sorter: true,
      },
      {
        title: L('DisplayName:Status'),
        dataIndex: 'status',
        align: 'left',
        width: 'auto',
        sorter: true,
      },
    ],
    dataSource: fileList,
    pagination: false,
    striped: false,
    useSearchForm: false,
    showTableSetting: false,
    bordered: false,
    showIndexColumn: false,
    canResize: false,
    immediate: false,
    actionColumn: {
      width: 120,
      title: L('Actions'),
      dataIndex: 'action',
    },
  });

  onMounted(() => {
    const userStore = useUserStoreWithOut();
    uploader = new Uploader({
      target: uploadUrl,
      testChunks: false,
      // 加重试机制防止网络波动
      maxChunkRetries: 3,
      chunkRetryInterval: null,
      successStatuses: [200, 201, 202, 204, 205],
      permanentErrors: [400, 401, 403, 404, 415, 500, 501],
      headers: {
        // TODO: 使用缓存存储令牌类型?
        Authorization: `Bearer ${userStore.getToken}`,
      },
      processParams: (params: any) => {
        params.bucket = unref(bucket);
        params.path = unref(path);
        return params;
      },
    });
    // uploader.on('fileSuccess', _fileSuccess);
    uploader.on('filesSubmitted', _filesSubmitted);
    uploader.on('fileError', _fileError);
    uploader.on('fileSuccess', _fileSuccess);
    uploader.on('fileProgress', _fileProgress);
  });

  onUnmounted(() => {
    // uploader.off('fileSuccess', _fileSuccess);
    uploader.off('filesSubmitted', _filesSubmitted);
    uploader.off('fileError', _fileError);
    uploader.off('fileSuccess', _fileSuccess);
    uploader.off('fileProgress', _fileProgress);
    uploader = null;
  });

  watch(
    () => unref(btnRef),
    (btn) => {
      uploader.assignBrowse(btn);
    },
  );

  function _filesSubmitted(_, files) {
    files.forEach((f) => {
      f.paused = true;
      f.progress = '0 %';
    });
    fileList.value.push(...files);
  }

  function _fileProgress(_, file, chunk) {
    // 2024-09-29 处理上传失败的包装错误
    if (chunk.processedState?.res) {
      try {
        const result = JSON.parse(chunk.processedState.res) as Result<any>;
        if (result.code !== '0') {
          file.error = true;
          file.errorMsg = result.message;
          file.pause();
        }
      } catch (error) {
        console.log('upload error ---> ', error);
      }
    }
    if (file._prevUploadedSize) {
      file.progress = `${Math.floor((file._prevUploadedSize / file.size) * 100)} %`;
    }
  }

  function _fileError(_, file, message) {
    file.paused = true;
    if (message) {
      const formatedError = JSON.parse(message);
      file.errorMsg = formatedError.error?.message;
    }
  }

  function _fileSuccess(_, file) {
    emits('file:uploaded', unref(bucket), unref(path), file.name);
  }

  function handleSelect() {
    unref(btnRef)?.click();
  }

  function handleResume(record) {
    if (record.error) {
      record.retry();
      record.errorMsg = '';
    }
    record.resume();
  }

  function handlePause(record) {
    record.pause();
  }

  function handleCancel(record) {
    record.cancel();
    uploader.removeFile(record);
    fileList.value = fileList.value.filter((f) => f.id !== record.id);
  }

  const fileSize = computed(() => {
    return (size) => {
      return Uploader.utils.formatSize(size);
    };
  });

  const averageSpeed = computed(() => {
    return (speed) => {
      return `${Uploader.utils.formatSize(speed)} / s`;
    };
  });
</script>
