<script lang='ts' setup>
import type { UploadFetchOptions } from "@/utils/aws/s3";
import { toastController } from "@ionic/vue";
import { closeCircleOutline, documentAttachOutline, trashOutline } from "ionicons/icons";
import { merge } from "lodash-es";
import { uploadToS3 } from "@/utils/aws/s3";

interface FileItem {
  name: string;
  size: number;
  url?: string;
  fileId?: string; // S3 文件 ID
  uploading?: boolean;
  progress?: number;
  error?: string;
}

const props = withDefaults(defineProps<{
  modelValue?: string[];
  label?: string;
  placeholder?: string;
  accept?: string;
  multiple?: boolean;
  maxSize?: number; // MB
  maxFiles?: number;
  fetchOptions: Partial<UploadFetchOptions>;
}>(), {
  modelValue: () => [],
  accept: "*/*",
  multiple: true,
  maxSize: 10,
  maxFiles: 5,
});

const emit = defineEmits<{
  (e: "update:modelValue", value: string[]): void;
}>();

const { t } = useI18n();
const fileInput = ref<HTMLInputElement>();
const files = ref<FileItem[]>([]);
const abortControllers = new Map<number, AbortController>();

// 初始化已有文件
watchEffect(() => {
  if (props.modelValue && props.modelValue.length > 0) {
    files.value = props.modelValue.map((fileId) => {
      // 如果是完整 URL，提取文件名
      const name = fileId.includes("/") ? fileId.split("/").pop() || "file" : fileId;
      return {
        name,
        size: 0,
        fileId,
      };
    });
  }
});

async function handleFileSelect(event: Event) {
  const input = event.target as HTMLInputElement;
  const selectedFiles = Array.from(input.files || []);

  if (!selectedFiles.length)
    return;

  // 检查文件数量限制
  if (files.value.length + selectedFiles.length > props.maxFiles) {
    await showToast(t("fileUpload.maxFilesError", { max: props.maxFiles }), "warning");
    return;
  }

  // 批量上传文件
  for (const file of selectedFiles) {
    // 检查文件大小
    if (file.size > props.maxSize * 1024 * 1024) {
      await showToast(t("fileUpload.fileSizeError", { name: file.name, max: props.maxSize }), "warning");
      continue;
    }

    // 添加到列表（显示上传中状态）
    const fileItem: FileItem = {
      name: file.name,
      size: file.size,
      uploading: true,
      progress: 0,
    };
    files.value.push(fileItem);
    const index = files.value.length - 1;

    // 创建 AbortController
    const controller = new AbortController();
    abortControllers.set(index, controller);

    try {
      // 上传到 S3
      const options = merge({
        fileName: file.name,
        fileSize: file.size,
        businessType: props.fetchOptions.businessType,
      }, props.fetchOptions) as UploadFetchOptions;

      const fileId = await uploadToS3(file, {
        fetchOptions: options,
        onProgress: (progress) => {
          if (files.value[index]) {
            files.value[index].progress = progress;
          }
        },
        signal: controller.signal,
      });

      // 上传成功
      if (files.value[index]) {
        files.value[index].fileId = String(fileId);
        files.value[index].uploading = false;
        files.value[index].progress = 100;
        abortControllers.delete(index);
        updateValue();
      }
    }
    catch (error: any) {
      // 上传失败或取消
      if (files.value[index]) {
        if (error.message !== "上传已取消") {
          files.value[index].error = error.message;
          files.value[index].uploading = false;
          await showToast(t("fileUpload.uploadError", { name: file.name }), "danger");
        }
        abortControllers.delete(index);
      }
    }
  }

  // 重置 input
  if (fileInput.value) {
    fileInput.value.value = "";
  }
}

function removeFile(index: number) {
  // 如果正在上传，取消上传
  const controller = abortControllers.get(index);
  if (controller) {
    controller.abort();
    abortControllers.delete(index);
  }

  files.value.splice(index, 1);
  updateValue();
}

function cancelUpload(index: number) {
  const controller = abortControllers.get(index);
  if (controller) {
    controller.abort();
    abortControllers.delete(index);
  }
  removeFile(index);
}

function updateValue() {
  const fileIds = files.value
    .filter(f => f.fileId && !f.uploading && !f.error)
    .map(f => f.fileId!);
  emit("update:modelValue", fileIds);
}

function formatFileSize(bytes: number): string {
  if (bytes === 0)
    return "0 B";
  const k = 1024;
  const sizes = ["B", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
}

function triggerFileInput() {
  fileInput.value?.click();
}

async function showToast(message: string, color: "success" | "warning" | "danger" = "warning") {
  const toast = await toastController.create({
    message,
    duration: 2000,
    position: "top",
    color,
  });
  await toast.present();
}

// 组件卸载时清理
onUnmounted(() => {
  abortControllers.forEach(controller => controller.abort());
  abortControllers.clear();
});
</script>

<template>
  <div class="file-upload">
    <div v-if="label" class="file-upload-label">
      {{ label }}
    </div>

    <input
      ref="fileInput"
      type="file"
      :accept="accept"
      :multiple="multiple"
      class="file-input-hidden"
      @change="handleFileSelect"
    >

    <!-- 上传按钮 -->
    <ion-button
      v-if="files.length < maxFiles"
      expand="block"
      fill="outline"
      class="upload-button"
      @click="triggerFileInput"
    >
      <ion-icon slot="start" :icon="documentAttachOutline" />
      {{ placeholder || t('fileUpload.uploadFile') }}
    </ion-button>

    <!-- 文件列表 -->
    <div v-if="files.length > 0" class="file-list">
      <div v-for="(file, index) in files" :key="index" class="file-item">
        <div class="file-info">
          <ion-icon :icon="documentAttachOutline" class="file-icon" />
          <div class="file-details">
            <div class="file-name">
              {{ file.name }}
            </div>
            <div v-if="file.size && !file.uploading" class="file-size">
              {{ formatFileSize(file.size) }}
            </div>

            <!-- 上传进度 -->
            <div v-if="file.uploading" class="upload-progress">
              <ion-progress-bar :value="(file.progress || 0) / 100" color="primary" />
              <span class="progress-text">{{ file.progress }}%</span>
            </div>

            <!-- 错误信息 -->
            <div v-if="file.error" class="error-text">
              {{ file.error }}
            </div>
          </div>
        </div>

        <!-- 操作按钮 -->
        <div class="file-actions">
          <ion-button
            v-if="file.uploading"
            fill="clear"
            size="small"
            color="medium"
            @click="cancelUpload(index)"
          >
            <ion-icon slot="icon-only" :icon="closeCircleOutline" />
          </ion-button>
          <ion-button
            v-else
            fill="clear"
            size="small"
            color="danger"
            @click="removeFile(index)"
          >
            <ion-icon slot="icon-only" :icon="trashOutline" />
          </ion-button>
        </div>
      </div>
    </div>

    <div v-if="files.length > 0" class="file-count">
      {{ files.filter(f => f.fileId).length }} / {{ maxFiles }} {{ t('fileUpload.files') }}
    </div>
  </div>
</template>

<style scoped>
.file-upload {
  width: 100%;
}

.file-upload-label {
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 8px;
  color: var(--ion-text-color);
}

.file-input-hidden {
  display: none;
}

.upload-button {
  --border-style: dashed;
  --border-width: 2px;
  margin-bottom: 12px;
}

.file-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
}

.file-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px;
  background: var(--ion-color-light);
  border-radius: 8px;
  transition: background-color 0.2s;
}

.file-item:hover {
  background: var(--ion-color-light-shade);
}

.file-info {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
  min-width: 0;
}

.file-icon {
  font-size: 24px;
  color: var(--ion-color-primary);
  flex-shrink: 0;
}

.file-details {
  flex: 1;
  min-width: 0;
}

.file-name {
  font-size: 14px;
  font-weight: 500;
  color: var(--ion-text-color);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-size {
  font-size: 12px;
  color: var(--ion-color-medium);
  margin-top: 2px;
}

.upload-progress {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
}

.upload-progress ion-progress-bar {
  flex: 1;
  height: 4px;
}

.progress-text {
  font-size: 11px;
  color: var(--ion-color-primary);
  font-weight: 500;
  min-width: 35px;
  text-align: right;
}

.error-text {
  font-size: 12px;
  color: var(--ion-color-danger);
  margin-top: 2px;
}

.file-actions {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.file-count {
  font-size: 12px;
  color: var(--ion-color-medium);
  text-align: right;
  margin-top: 8px;
}
</style>
