<template>
  <div class="file-uploader">
    <div v-if="!filePath" class="upload-area">
      <a-upload
        :before-upload="beforeUpload"
        :show-upload-list="false"
        :accept="acceptTypes"
      >
        <a-button>
          <UploadOutlined />
          {{ buttonText }}
        </a-button>
      </a-upload>
      <div class="upload-hint">
        <span>{{ hint }}</span>
      </div>
    </div>
    <div v-else class="file-preview">
      <div class="file-info">
        <FileOutlined class="file-icon" />
        <span class="file-name">{{ fileName || filePath }}</span>
      </div>
      <div class="file-actions">
        <a-tooltip title="预览">
          <a-button type="link" size="small" @click="handlePreview">
            <EyeOutlined />
          </a-button>
        </a-tooltip>
        <a-tooltip title="替换">
          <a-upload
            :before-upload="beforeUpload"
            :show-upload-list="false"
            :accept="acceptTypes"
          >
            <a-button type="link" size="small">
              <SwapOutlined />
            </a-button>
          </a-upload>
        </a-tooltip>
        <a-tooltip title="删除">
          <a-button type="link" size="small" danger @click="handleRemove">
            <DeleteOutlined />
          </a-button>
        </a-tooltip>
      </div>
    </div>

    <!-- 预览弹窗 -->
    <a-modal
      v-model:open="previewVisible"
      :title="fileName || '文件预览'"
      :footer="null"
      width="800px"
    >
      <div class="preview-content">
        <video v-if="fileType === 'video'" :src="previewUrl" controls style="width: 100%" />
        <audio v-else-if="fileType === 'audio'" :src="previewUrl" controls style="width: 100%" />
        <iframe v-else-if="fileType === 'pdf' || isPdfFile" :src="previewUrl" style="width: 100%; height: 600px; border: none" />
        <img
          v-else-if="fileType === 'image'"
          :src="previewUrl"
          style="max-width: 100%"
          @load="imagePreviewLoading = false; imagePreviewLoaded = true"
          @error="imagePreviewLoading = false; imagePreviewError = true"
          :style="{ opacity: imagePreviewLoaded ? 1 : 0, transition: 'opacity 0.3s' }"
        />
        <!-- 图片加载失败提示 -->
        <div v-if="fileType === 'image' && imagePreviewError" class="image-error-tip">
          <FileOutlined style="font-size: 48px; color: #999" />
          <p>图片加载失败</p>
        </div>
        <div v-else class="unsupported">
          <FileOutlined style="font-size: 48px; color: #999" />
          <p>暂不支持预览此类型文件</p>
          <a-button type="primary" :href="previewUrl" target="_blank">下载文件</a-button>
        </div>
      </div>
    </a-modal>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import { message } from 'ant-design-vue';
import {
  UploadOutlined,
  FileOutlined,
  EyeOutlined,
  SwapOutlined,
  DeleteOutlined,
} from '@ant-design/icons-vue';
import { getOssToken, uploadToOss } from '@/api/file';

interface Props {
  filePath?: string;
  fileName?: string;
  fileType?: 'video' | 'ppt' | 'pdf' | 'image' | 'audio' | 'document' | 'other';
  maxSize?: number; // MB
}

const props = withDefaults(defineProps<Props>(), {
  filePath: '',
  fileName: '',
  fileType: 'other',
  maxSize: 100,
});

const emit = defineEmits<{
  (e: 'update:filePath', value: string): void;
  (e: 'update:fileName', value: string): void;
  (e: 'change', value: { filePath: string; fileName: string }): void;
  (e: 'remove'): void;
}>();

const previewVisible = ref(false);
const uploading = ref(false);

// 图片预览加载状态
const imagePreviewLoading = ref(false);
const imagePreviewLoaded = ref(false);
const imagePreviewError = ref(false);

const buttonText = computed(() => {
  const typeMap: Record<string, string> = {
    video: '上传视频',
    ppt: '上传课件',
    pdf: '上传 PDF',
    image: '上传图片',
    audio: '上传音频',
    document: '上传文档',
    other: '上传文件',
  };
  return typeMap[props.fileType] || '上传文件';
});

const hint = computed(() => {
  const typeMap: Record<string, string> = {
    video: `支持 MP4、MOV 格式，最大 ${props.maxSize}MB`,
    ppt: `支持 PPT、PPTX、PDF 格式，最大 ${props.maxSize}MB`,
    pdf: `支持 PDF 格式，最大 ${props.maxSize}MB`,
    image: `支持 JPG、PNG 格式，最大 ${props.maxSize}MB`,
    audio: `支持 MP3、WAV 格式，最大 ${props.maxSize}MB`,
    document: `支持 DOC、DOCX 格式，最大 ${props.maxSize}MB`,
    other: `最大 ${props.maxSize}MB`,
  };
  return typeMap[props.fileType] || `最大 ${props.maxSize}MB`;
});

const acceptTypes = computed(() => {
  const typeMap: Record<string, string> = {
    video: 'video/*',
    ppt: '.ppt,.pptx,.pdf',
    pdf: '.pdf',
    image: 'image/*',
    audio: 'audio/*',
    document: '.doc,.docx',
    other: '*/*',
  };
  return typeMap[props.fileType] || '*/*';
});

const previewUrl = computed(() => {
  if (!props.filePath) return '';
  // 如果已经是完整 URL 或以 /uploads 开头，直接返回
  if (props.filePath.startsWith('http') || props.filePath.startsWith('/uploads')) {
    return props.filePath;
  }
  // 如果路径已经包含 uploads 目录，不再添加前缀
  if (props.filePath.includes('/uploads/')) {
    return props.filePath;
  }
  return `/uploads/${props.filePath}`;
});

// 须在 previewUrl 定义之后：watch 回调会读 previewUrl，避免 TDZ（Cannot access before initialization）
watch(
  () => previewUrl.value,
  (newUrl) => {
    if (newUrl && props.fileType === 'image') {
      imagePreviewLoading.value = true;
      imagePreviewLoaded.value = false;
      imagePreviewError.value = false;
    }
  },
  { immediate: true }
);

// 检查是否为 PDF 文件（用于 ppt 类型也支持 PDF 预览）
const isPdfFile = computed(() => {
  if (props.fileType === 'ppt' && props.filePath) {
    return props.filePath.toLowerCase().endsWith('.pdf');
  }
  return false;
});

const beforeUpload = async (file: File) => {
  // 文件大小验证
  const isLtMaxSize = file.size / 1024 / 1024 < props.maxSize;
  if (!isLtMaxSize) {
    message.error(`文件大小不能超过 ${props.maxSize}MB`);
    return false;
  }

  // 文件类型验证
  if (props.fileType === 'video' && !file.type.startsWith('video/')) {
    message.error('请上传视频文件');
    return false;
  }
  if (props.fileType === 'pdf' && file.type !== 'application/pdf') {
    message.error('请上传 PDF 文件');
    return false;
  }
  if (props.fileType === 'image' && !file.type.startsWith('image/')) {
    message.error('请上传图片文件');
    return false;
  }
  if (props.fileType === 'audio' && !file.type.startsWith('audio/')) {
    message.error('请上传音频文件');
    return false;
  }
  if (props.fileType === 'document' &&
      !file.type.includes('word') &&
      !file.type.includes('document') &&
      !file.name.endsWith('.doc') &&
      !file.name.endsWith('.docx')) {
    message.error('请上传 Word 文档');
    return false;
  }
  // PPT 类型允许 PPT 和 PDF
  if (props.fileType === 'ppt' &&
      !file.type.includes('presentation') &&
      !file.type.includes('powerpoint') &&
      file.type !== 'application/pdf' &&
      !file.name.endsWith('.ppt') &&
      !file.name.endsWith('.pptx') &&
      !file.name.endsWith('.pdf')) {
    message.error('请上传 PPT、PPTX 或 PDF 文件');
    return false;
  }

  uploading.value = true;
  try {
    // 1. 获取 OSS 直传 Token
    const uploadType = props.fileType === 'ppt' ? 'ppt' : props.fileType || 'other';
    const token = await getOssToken(file.name, uploadType);

    // 2. 直接上传文件到 OSS
    await uploadToOss(file, token, (percent) => {
      console.log(`Upload progress: ${percent}%`);
    });

    // 3. 上传成功后返回文件 URL
    const fileUrl = `${token.host}/${token.key}`;
    emit('update:filePath', fileUrl);
    emit('update:fileName', file.name);
    emit('change', { filePath: fileUrl, fileName: file.name });
    message.success('上传成功');
  } catch (error: any) {
    message.error('上传失败：' + (error.response?.data?.message || error.message));
  } finally {
    uploading.value = false;
  }

  return false;
};

const handlePreview = () => {
  previewVisible.value = true;
};

const handleRemove = () => {
  emit('update:filePath', '');
  emit('update:fileName', '');
  emit('remove');
};
</script>

<style scoped lang="scss">
.file-uploader {
  .upload-area {
    .upload-hint {
      margin-top: 8px;
      font-size: 12px;
      color: #999;
    }
  }

  .file-preview {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px;
    background: #fafafa;
    border: 1px solid #e8e8e8;
    border-radius: 6px;

    .file-info {
      display: flex;
      align-items: center;
      gap: 8px;

      .file-icon {
        font-size: 20px;
        color: #1890ff;
      }

      .file-name {
        max-width: 200px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
    }

    .file-actions {
      display: flex;
      gap: 4px;
    }
  }

  .preview-content {
    padding: 20px;
    text-align: center;

    .unsupported {
      padding: 40px;

      p {
        margin: 16px 0;
        color: #666;
      }
    }

    .image-error-tip {
      padding: 40px;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;

      p {
        margin: 16px 0;
        color: #999;
      }
    }
  }
}
</style>
