<template>
  <div class="big-file-uploader">
    <!-- 
      关键修改：
      1. 添加 ref="uploadRef" 以便直接访问内部文件列表
      2. 监听 @change 获取原生文件
    -->
    <el-upload
      ref="uploadRef"
      class="upload-demo"
      drag
      multiple
      :auto-upload="false"
      :on-change="handleFileSelect"
      :file-list="uiFileList"
      :disabled="isUploading"
      :limit="100"
    >
      <el-icon class="el-icon--upload"><upload-filled /></el-icon>
      <div class="el-upload__text">
        拖拽文件到此处 或 <em>点击上传</em>
      </div>
      <template #tip>
        <div class="el-upload__tip">
          支持大文件切片上传，最大并发数：{{ uploadConcurrency }}
        </div>
      </template>
    </el-upload>

    <!-- 控制栏 -->
    <div class="control-bar" v-if="fileList.length > 0">
      <el-button 
        type="primary" 
        @click="startUpload" 
        :disabled="isUploading || !hasPendingFiles"
        :loading="isUploading"
        size="default"
      >
        {{ isUploading ? '正在上传...' : '开始上传' }}
      </el-button>
      
      <el-button @click="clearList" :disabled="isUploading" size="default">
        清空列表
      </el-button>

      <div class="stats">
        共 {{ fileList.length }} 个文件 | 
        成功: <span class="text-success">{{ successCount }}</span> | 
        失败: <span class="text-danger">{{ failCount }}</span>
      </div>
    </div>

    <!-- 文件列表 -->
    <div class="file-list-container">
      <div 
        v-for="(item, index) in fileList" 
        :key="item.uid" 
        class="file-item"
        :class="{ 'is-error': item.status === 'fail' }"
      >
        <div class="file-info">
          <el-icon class="file-icon"><document /></el-icon>
          <div class="file-meta">
            <div class="file-name" :title="item.name">{{ item.name }}</div>
            <div class="file-size">{{ formatSize(item.size) }}</div>
          </div>
        </div>

        <div class="progress-wrapper">
          <el-progress 
            :percentage="item.progress" 
            :status="getProgressStatus(item.status)"
            :stroke-width="16"
          >
            <template v-if="item.status === 'uploading'">
              <span class="speed-text">{{ item.speed }}</span>
            </template>
            <template v-else-if="item.status === 'success'">
              <el-icon><check /></el-icon>
            </template>
            <template v-else-if="item.status === 'fail'">
              <el-icon><close /></el-icon>
            </template>
          </el-progress>
        </div>

        <div class="action-area">
          <el-tag :type="getStatusType(item.status)" size="small">
            {{ statusMap[item.status] }}
          </el-tag>
          
          <el-button 
            v-if="item.status === 'fail'" 
            link 
            type="primary" 
            size="small" 
            @click="retryUpload(item)"
          >
            重试
          </el-button>
          <el-button 
            v-if="item.status === 'waiting'" 
            link 
            type="danger" 
            size="small" 
            @click="removeFile(item)"
          >
            移除
          </el-button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, nextTick } from 'vue';
import { 
  UploadFilled, 
  Document, 
  Check, 
  Close 
} from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import OSS from 'ali-oss';
import pLimit from 'p-limit';

// 请确保路径正确
import { getStstoken, batchSaveFiles } from '@/api/common'; 

const props = defineProps({
  tenantBizId: { type: String, required: true,default: '' },
  projectBizId: { type: String, required: true, default: '' },
  objectBizId: { type: String, required: true, default: '' },
  objectType: { type: String, default: '' },
  objectTableName: { type: String, default: '' },
  objectName: { type: String, default: '' },
  uploadConcurrency: { type: Number, default: 3 },
  partSize: { type: Number, default: 1 * 1024 * 1024 },
});

const uploadRef = ref(null);
const fileList = ref([]); 
const uiFileList = ref([]); 
const isUploading = ref(false);
const ossClient = ref(null);

const statusMap = {
  waiting: '等待上传',
  uploading: '上传中',
  success: '成功',
  fail: '失败',
};

const hasPendingFiles = computed(() => {
  return fileList.value.some(f => f.status === 'waiting' || f.status === 'fail');
});

const successCount = computed(() => fileList.value.filter(f => f.status === 'success').length);
const failCount = computed(() => fileList.value.filter(f => f.status === 'fail').length);

const formatSize = (bytes) => {
  if (!bytes) return '0 B';
  const k = 1024;
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};

const getProgressStatus = (status) => {
  if (status === 'success') return 'success';
  if (status === 'fail') return 'exception';
  return undefined;
};

const getStatusType = (status) => {
  const map = { waiting: 'info', uploading: 'warning', success: 'success', fail: 'danger' };
  return map[status] || 'info';
};

// 【优化】处理文件选择（增加关闭再打开时的去重清理）
const handleFileSelect = (rawFile, uploadFiles) => {
  // 如果是重新选择文件，建议先检查是否需要清理“失败”或“成功”的旧状态，视业务需求而定
  // 这里保持原有逻辑，但增强了对 raw 对象的获取
  
  const filesToProcess = Array.isArray(rawFile) ? rawFile : [rawFile];
  
  filesToProcess.forEach(file => {
    let nativeFile = null;
    
    // 优先从 file.raw 获取
    if (file.raw && (file.raw instanceof File || file.raw instanceof Blob)) {
      nativeFile = file.raw;
    } else if (file instanceof File || file instanceof Blob) {
      nativeFile = file;
    } else {
      // 尝试从 uploadRef 中找回原生对象（防止 element-plus 包装丢失）
      if (uploadRef.value && uploadRef.value.files) {
        const found = uploadRef.value.files.find(f => f.name === file.name && f.size === file.size);
        if (found && found.raw) {
          nativeFile = found.raw;
        }
      }
    }

    if (!nativeFile) {
      console.warn('Skipping invalid file:', file.name);
      return;
    }

    // 【关键去重逻辑】
    // 如果列表中已存在同名同大小的文件，且状态不是 success（允许重新上传成功的？通常不允许），则跳过
    // 如果你的需求是：关闭弹窗再打开，之前的列表应该被清空。请确保在弹窗关闭事件 (@close) 中调用了 clearList()
    const exists = fileList.value.some(
      f => f.name === nativeFile.name && f.size === nativeFile.size && f.status !== 'success'
    );
    
    if (!exists) {
      const uid = nativeFile.uid || Date.now() + Math.random();
      
      const newItem = {
        uid,
        name: nativeFile.name,
        size: nativeFile.size,
        raw: nativeFile,
        status: 'waiting',
        progress: 0,
        speed: '0 KB/s',
        url: '',
        startTime: 0,
      };

      fileList.value.push(newItem);
      
      // 同步到 uiFileList，确保 el-upload 显示
      // 注意：不要直接 push 到 uploadRef.value.files，而是更新绑定的 uiFileList
      const uiExists = uiFileList.value.some(f => f.uid === uid);
      if (!uiExists) {
        uiFileList.value.push({
          uid,
          name: nativeFile.name,
          size: nativeFile.size,
          status: 'ready',
          raw: nativeFile
        });
      }
    }
  });
};

// 【修复】移除单个文件方法
const removeFile = (item) => {
  if (isUploading.value && item.status === 'uploading') {
    ElMessage.warning('文件正在上传，无法移除');
    return;
  }

  // 1. 从逻辑列表移除
  fileList.value = fileList.value.filter(f => f.uid !== item.uid);
  
  // 2. 从 UI 列表移除
  uiFileList.value = uiFileList.value.filter(f => f.uid !== item.uid);

  // 3. 同步清除 el-upload 内部对应的文件记录
  if (uploadRef.value) {
    // el-upload 的 clearFiles 通常清空所有，若要移除单个，可以直接操作 its internal files 数组
    // 但最简单可靠的方法是：既然我们控制了 uiFileList，让 el-upload 依赖 uiFileList 即可
    // 如果 UI 没刷新，可以强制触发一次 update
    const uploadInstance = uploadRef.value;
    if (uploadInstance.files) {
       uploadInstance.files = uploadInstance.files.filter(f => f.uid !== item.uid);
    }
  }
};
// 【修复】清空列表方法
const clearList = () => {
  if (isUploading.value) {
    ElMessage.warning('上传进行中，无法清空');
    return;
  }

  // 1. 清空内部逻辑列表
  fileList.value = [];
  
  // 2. 清空 UI 绑定列表
  uiFileList.value = [];

  // 3. 【关键】调用 Element Plus 组件实例的方法清除内部状态
  if (uploadRef.value) {
    uploadRef.value.clearFiles();
  }
  
  // 4. 如果是在弹窗中使用，且关闭弹窗时调用此方法，确保父组件也能感知变化（如果需要）
  // emit('update:modelValue', []); // 如果有双向绑定
};

const initOssClient = async () => {
  try {
    const res = await getStstoken(props.projectBizId);
    if (res.code !== 200 && res.data?.code !== 200) {
      throw new Error(res.msg || res.data?.msg || '获取 STS 凭证失败');
    }

    const data = res.data || res;
    const rawData = data.data || data;
    const { region, accessKeyId, accessKeySecret, stsToken, bucket, endpoint } = rawData;

    if (!accessKeyId || !bucket) throw new Error('STS 凭证信息不完整');

    let ossEndpoint = endpoint;
    if (!ossEndpoint) ossEndpoint = `oss-${region}.aliyuncs.com`;

    ossClient.value = new OSS({
      region,
      accessKeyId,
      accessKeySecret,
      stsToken,
      bucket,
      endpoint: ossEndpoint,
      secure: true,
      timeout: 60000,
    });
    return ossClient.value;
  } catch (error) {
    console.error('Init OSS Error:', error);
    throw error;
  }
};

const uploadSingleFile = async (item) => {
  // 【核心修复】再次校验 raw 是否为原生 File
  const file = item.raw;
  
  if (!(file instanceof File) && !(file instanceof Blob)) {
    console.error('Invalid file object for upload:', file);
    throw new Error('文件对象无效，必须是 File 或 Blob 类型');
  }

  const objectName = `uploads/${props.projectBizId}/${Date.now()}_${item.uid}_${file.name}`;
  
  item.status = 'uploading';
  item.startTime = Date.now();

  try {
    const client = ossClient.value;
    
    await client.multipartUpload(objectName, file, {
      parallel: 4,
      partSize: props.partSize,
      progress: (p) => {
        item.progress = Math.floor(p * 100);
        const duration = (Date.now() - item.startTime) / 1000;
        if (duration > 0 && p > 0) {
          const speed = (file.size * p) / duration;
          item.speed = `${formatSize(speed)}/s`;
        }
      },
    });

    item.status = 'success';
    item.progress = 100;
    item.speed = '-';
    
    // const endpoint = client.options.endpoint.replace(/^https?:\/\//, '');
    const endpoint = client.options.endpoint.host;
    item.url = `https://${client.options.bucket}.${endpoint}/${objectName}`;

    return {
      fileName: item.name,
      fileSize: item.size,
      fileUrl: item.url,
    };
  } catch (error) {
    console.error(`Upload failed: ${item.name}`, error);
    item.status = 'fail';
    item.speed = '-';
    return null;
  }
};

const startUpload = async () => {
  if (!hasPendingFiles.value) return;
  isUploading.value = true;
  const successResults = [];

  try {
    await initOssClient();
    const pendingItems = fileList.value.filter(f => f.status === 'waiting' || f.status === 'fail');

    if (pendingItems.length === 0) return;

    const limit = pLimit(props.uploadConcurrency);
    const tasks = pendingItems.map(item => limit(() => uploadSingleFile(item)));
    const results = await Promise.all(tasks);

    results.forEach(res => { if (res) successResults.push(res); });

    if (successResults.length > 0) {
      const payload = {
        tenantBizId: props.tenantBizId,
        projectBizId: props.projectBizId,
        objectBizId: props.objectBizId,
        objectType: props.objectType,
        objectTableName: props.objectTableName,
        objectName: props.objectName,
        apiOssFileDtoList: successResults,
      };
      await batchSaveFiles(payload);
      ElMessage.success(`上传完成！成功 ${successResults.length} 个文件`);
    } else {
      ElMessage.error('所有文件上传均失败');
    }
  } catch (globalError) {
    console.error('Global Upload Error:', globalError);
    ElMessage.error(`上传初始化失败: ${globalError.message}`);
  } finally {
    isUploading.value = false;
  }
};

const retryUpload = async (item) => {
  if (isUploading.value) return;
  item.status = 'uploading';
  item.progress = 0;
  try {
    if (!ossClient.value) await initOssClient();
    await uploadSingleFile(item);
    if (item.status === 'success') ElMessage.success(`${item.name} 重试成功`);
  } catch (e) { /* ignored */ }
};

// 暴露方法给父组件调用
defineExpose({
  clearList,
  startUpload, // 如果父组件需要主动触发上传也可以暴露
  fileList     // 如果需要访问文件列表也可以暴露
});
</script>

<style scoped>
/* 样式保持不变 */
.big-file-uploader { width: 100%; max-width: 900px; margin: 0 auto; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; }
.control-bar { display: flex; align-items: center; gap: 15px; margin: 20px 0; padding: 15px; background: #f5f7fa; border-radius: 8px; }
.stats { margin-left: auto; font-size: 14px; color: #606266; }
.text-success { color: #67c23a; font-weight: bold; }
.text-danger { color: #f56c6c; font-weight: bold; }
.file-list-container { display: flex; flex-direction: column; gap: 12px; }
.file-item { display: grid; grid-template-columns: 250px 1fr 120px; align-items: center; gap: 20px; padding: 15px; border: 1px solid #ebeef5; border-radius: 6px; background: #fff; transition: all 0.3s; }
.file-item.is-error { border-color: #fde2e2; background-color: #fef0f0; }
.file-info { display: flex; align-items: center; gap: 10px; overflow: hidden; }
.file-icon { font-size: 24px; color: #409eff; flex-shrink: 0; }
.file-meta { display: flex; flex-direction: column; overflow: hidden; }
.file-name { font-size: 14px; color: #303133; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 500; }
.file-size { font-size: 12px; color: #909399; margin-top: 2px; }
.progress-wrapper { width: 100%; }
.speed-text { font-size: 12px; color: #909399; }
.action-area { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; }
@media (max-width: 600px) {
  .file-item { grid-template-columns: 1fr; gap: 10px; }
  .action-area { flex-direction: row; justify-content: space-between; align-items: center; }
  .control-bar { flex-direction: column; align-items: stretch; }
  .stats { margin-left: 0; margin-top: 10px; }
}
</style>