<script setup lang="ts">
import type { FormKitFrameworkContext } from '@formkit/core';
import FileUpload, { type FileUploadUploaderEvent } from 'primevue/fileupload';
import { computed, onMounted, useTemplateRef } from 'vue';
import FileUploadItem from './file-upload-item.vue';
import type { CustomRequest, FileExt, FileUploadInst, PropFilesToValue, PropValueToFiles } from './types';

const props = defineProps<{
  context: FormKitFrameworkContext & {
    fileLimit?: number;
    maxFileSize?: number;
    customRequest?: CustomRequest;
    valueToFiles?: PropValueToFiles;
    filesToValue?: PropFilesToValue;
    autoUpload?: boolean;
  };
}>();
const formkitContext = props.context;

const customRequest = props.context.customRequest;

const fileUploadRef = useTemplateRef<FileUploadInst>('fileUploadRef');

const cmpt_disabled = computed(() => {
  if (formkitContext.disabled === true) {
    return true;
  }
  if (fileUploadRef.value) {
    // 有上传失败的文件
    if (fileUploadRef.value.uploadedFiles.some((f) => f.status === 'failed')) {
      return true;
    }

    // 已上传文件数量超过限制（这里是大于*等于*）
    if (fileUploadRef.value.uploadedFileCount >= (formkitContext.fileLimit ?? Infinity)) {
      return true;
    }

    // 已上传和待上传文件数量超过限制（这里是*大于*）
    const uploaded_and_pending_count = fileUploadRef.value.uploadedFileCount + fileUploadRef.value.files.length;
    if (uploaded_and_pending_count > (formkitContext.fileLimit ?? Infinity)) {
      return true;
    }
  }
  return false;
});

const cmpt_showUploadButton = computed(() => {
  if (fileUploadRef.value?.files?.length) {
    return true;
  }
  return false;
});

const changeModelValue = () => {
  const uploadedFiles = fileUploadRef.value!.uploadedFiles.filter((f) => f.status === 'uploaded');
  if (!formkitContext.filesToValue) {
    console.warn('[FileUpload] filesToValue is not defined');
    return;
  }
  if (uploadedFiles.length === 0) {
    formkitContext.node.input(null);
  } else {
    formkitContext.node.input(formkitContext.filesToValue(uploadedFiles));
  }
};
const changeUploadedFiles = () => {
  if (!formkitContext.valueToFiles) {
    console.warn('[FileUpload] valueToFiles is not defined');
    return;
  }
  try {
    const files = formkitContext.valueToFiles(formkitContext._value);
    if (files === null) return;

    fileUploadRef.value!.uploadedFiles = files.map((f) => ({
      name: f.name,
      url: f.url,
      status: f.status || 'uploaded',
      progress: f.progress || 100,
    }));
    fileUploadRef.value!.uploadedFileCount = files.length;
  } catch (error) {
    console.warn('[FileUpload] valueToFiles error:', error, 'value:', formkitContext._value);
  }
};

onMounted(() => {
  changeUploadedFiles();
});

const onUploader = (event: FileUploadUploaderEvent) => {
  if (!customRequest) {
    console.warn('[FileUpload] customRequest is not defined');
    return;
  }

  const files = event.files as FileExt[];
  for (const file of files) {
    fileUploadRef.value!.uploadedFiles.push({
      rawFile: file,
      name: file.name,
      url: '',
      status: 'uploading',
      progress: 0,
    });
    const fileItem = fileUploadRef.value!.uploadedFiles[fileUploadRef.value!.uploadedFiles.length - 1];
    customRequest({
      file,
      onProgress: (percent) => {
        fileItem.progress = percent;
      },
    })
      .then((result) => {
        fileItem.status = 'uploaded';
        fileItem.url = result.url;
        changeModelValue();
      })
      .catch(() => {
        fileItem.status = 'failed';
      });
  }
};

// fileUploadRef.value?.uploadedFiles
</script>

<template>
  <FileUpload
    ref="fileUploadRef"
    :auto="formkitContext.autoUpload"
    :disabled="cmpt_disabled"
    :showUploadButton="cmpt_showUploadButton"
    :showCancelButton="false"
    :customUpload="true"
    mode="advanced"
    :multiple="true"
    accept="image/*"
    :maxFileSize="formkitContext.maxFileSize"
    invalidFileSizeMessage="文件 {0} 大小超过限制 {1}"
    :fileLimit="formkitContext.fileLimit"
    invalidFileLimitMessage="最多只能上传 {0} 个文件，请移除多余文件后点击上传"
    @uploader="onUploader"
    :chooseButtonProps="{ size: 'small' }"
    :uploadButtonProps="{ size: 'small', severity: 'secondary' }"
    :cancelButtonProps="{ size: 'small', severity: 'secondary' }"
    @remove="changeModelValue()"
    @removeUploadedFile="changeModelValue()"
  >
    <template #empty>
      <Message
        size="small"
        severity="secondary"
        >未上传附件</Message
      >
    </template>

    <template #content="{ messages, removeFileCallback, removeUploadedFileCallback }">
      <Message
        size="small"
        v-for="msg of messages"
        closable
        @close="fileUploadRef!.messages = []"
        :key="msg"
        severity="error"
        >{{ msg }}</Message
      >

      <!-- 已上传列表（上传中、上传成功、上传失败） -->
      <template
        v-for="(file, index) of fileUploadRef?.uploadedFiles"
        :key="file.name + file.url"
      >
        <FileUploadItem
          :disabled="formkitContext.disabled === true"
          :url="file.url || file.rawFile?.objectURL || ''"
          :filename="file.name || file.url || '未知文件'"
          @remove="
            () => {
              fileUploadRef!.uploadedFileCount--;
              removeUploadedFileCallback(index);
            }
          "
          :status="file.status"
          :progress="file.progress"
        />
      </template>

      <!-- 待上传列表 -->
      <template
        v-for="(file, index) of fileUploadRef?.files"
        :key="file.name + file.type + file.size"
      >
        <FileUploadItem
          :disabled="formkitContext.disabled === true"
          :url="file.objectURL"
          :filename="file.name"
          @remove="removeFileCallback(index)"
          status="pending"
        />
      </template>
    </template>
  </FileUpload>
</template>

<style>
.p-floatlabel:has(.p-fileupload) label {
  top: var(--p-floatlabel-over-active-top);
  transform: translateY(0);
  font-size: var(--p-floatlabel-active-font-size);
  font-weight: var(--p-floatlabel-label-active-font-weight);
}

.p-fileupload-content .p-progressbar {
  --p-fileupload-progressbar-height: 1rem;
}
</style>
