<template>
  <div class="vehicle-attachment-upload">
    <el-upload
      :action="uploadAction"
      name="file"
      :headers="uploadHeaders"
      :file-list="fileList"
      :accept="acceptText"
      :multiple="multiple"
      :limit="limit"
      :disabled="readonly"
      :show-file-list="showFileList"
      :on-success="handleSuccess"
      :on-error="handleError"
      :on-remove="handleRemove"
      :on-preview="handlePreview"
      :before-upload="beforeUpload"
    >
      <el-button v-if="!readonly" type="primary" plain :icon="buttonIcon">
        {{ buttonText }}
      </el-button>
      <template #tip>
        <div v-if="showTip && tipText && !readonly" class="el-upload__tip">
          {{ tipText }}
        </div>
      </template>
    </el-upload>
    <span v-if="readonly && fileList.length === 0" class="vehicle-attachment-upload__empty">
      -
    </span>

    <el-dialog
      v-model="previewVisible"
      :title="previewFile.name || '附件预览'"
      append-to-body
      destroy-on-close
      width="90%"
      top="4vh"
      class="vehicle-attachment-upload__viewer-dialog"
    >
      <open-file-viewer
        v-if="previewVisible && previewSource"
        :file="previewSource"
        :file-name="previewFile.name"
        :mime-type="previewFile.mimeType"
        width="100%"
        height="72vh"
        fit="contain"
        theme="auto"
        locale="zh-CN"
        :toolbar="viewerToolbar"
        :plugins="viewerPlugins"
        @unsupported="handleUnsupported"
        @error="handleViewerError"
      />
    </el-dialog>
  </div>
</template>

<script>
import { OpenFileViewer } from '@open-file-viewer/vue';
import {
  fallbackPlugin,
  imagePlugin,
  officePlugin,
  pdfPlugin,
  textPlugin,
} from '@open-file-viewer/core';
import '@open-file-viewer/core/style.css';
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
import { baseUrl } from '@/config/env';
import { getUploadHeaders } from '@/utils/upload';

const MIME_MAP = {
  jpg: 'image/jpeg',
  jpeg: 'image/jpeg',
  png: 'image/png',
  gif: 'image/gif',
  bmp: 'image/bmp',
  webp: 'image/webp',
  pdf: 'application/pdf',
  doc: 'application/msword',
  docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  xls: 'application/vnd.ms-excel',
  xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  ppt: 'application/vnd.ms-powerpoint',
  pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  eml: 'message/rfc822',
  msg: 'application/vnd.ms-outlook',
  zip: 'application/zip',
  txt: 'text/plain',
};

const viewerPlugins = [
  imagePlugin(),
  pdfPlugin({
    workerSrc: pdfWorkerSrc,
    useFetchData: true,
  }),
  officePlugin({
    pdf: {
      workerSrc: pdfWorkerSrc,
      useFetchData: true,
    },
  }),
  textPlugin(),
  fallbackPlugin(),
];

export default {
  name: 'VehicleAttachmentUpload',
  components: {
    OpenFileViewer,
  },
  props: {
    modelValue: {
      type: [Array, String, Object],
      default: () => [],
    },
    action: {
      type: String,
      default: '',
    },
    accept: {
      type: [String, Array],
      default: '',
    },
    fileTypes: {
      type: Array,
      default: () => [],
    },
    multiple: {
      type: Boolean,
      default: true,
    },
    limit: {
      type: Number,
      default: 20,
    },
    maxSize: {
      type: Number,
      default: 0,
    },
    readonly: {
      type: Boolean,
      default: false,
    },
    buttonText: {
      type: String,
      default: '上传附件',
    },
    buttonIcon: {
      type: String,
      default: 'el-icon-upload',
    },
    tip: {
      type: String,
      default: '',
    },
    showTip: {
      type: Boolean,
      default: true,
    },
    showFileList: {
      type: Boolean,
      default: true,
    },
  },
  emits: ['update:modelValue', 'change', 'success'],
  data() {
    return {
      fileList: [],
      previewVisible: false,
      previewFile: {},
      previewSource: '',
      viewerToolbar: {
        download: true,
        fullscreen: true,
        print: true,
        rotate: true,
        zoom: true,
      },
      viewerPlugins,
    };
  },
  computed: {
    uploadAction() {
      return this.action || `${baseUrl}/blade-resource/oss/endpoint/put-file`;
    },
    uploadHeaders() {
      return getUploadHeaders();
    },
    acceptedList() {
      const types = this.fileTypes.length ? this.fileTypes : this.accept;
      if (Array.isArray(types)) {
        return types.map(item => String(item).trim()).filter(Boolean);
      }
      return String(types || '')
        .split(',')
        .map(item => item.trim())
        .filter(Boolean);
    },
    acceptText() {
      return this.acceptedList.join(',');
    },
    tipText() {
      if (this.tip) return this.tip;
      const typeText = this.acceptText || '所有类型';
      const sizeText = this.maxSize > 0 ? `，单文件不超过 ${this.maxSize}MB` : '';
      return `支持上传 ${typeText}${sizeText}`;
    },
  },
  watch: {
    modelValue: {
      handler(value) {
        this.fileList = this.toUploadList(value);
      },
      immediate: true,
      deep: true,
    },
  },
  methods: {
    toUploadList(value) {
      const list = this.parseValue(value);
      return list.map((item, index) => {
        const url = this.getFileUrl(item);
        return {
          ...item,
          uid: item.uid || `${url || item.name || 'file'}-${index}`,
          name: item.name || this.getFileName(url) || '附件',
          url,
          status: 'success',
        };
      });
    },
    parseValue(value) {
      if (!value) return [];
      if (Array.isArray(value)) return value.filter(Boolean);
      if (typeof value === 'object') return [value];
      try {
        const list = JSON.parse(value);
        return Array.isArray(list) ? list : [];
      } catch (error) {
        return String(value)
          .split(',')
          .map(item => item.trim())
          .filter(Boolean)
          .map(item => ({
            name: this.getFileName(item),
            url: item,
          }));
      }
    },
    getFileUrl(file) {
      return file.url || file.link || file.src || file.domain || '';
    },
    getFileName(url) {
      if (!url) return '';
      const path = String(url).split('?')[0];
      return decodeURIComponent(path.substring(path.lastIndexOf('/') + 1));
    },
    getExtension(file) {
      const name = String(file.name || this.getFileUrl(file) || '').split('?')[0];
      const index = name.lastIndexOf('.');
      return index >= 0 ? name.substring(index + 1).toLowerCase() : '';
    },
    beforeUpload(file) {
      if (!this.validFileType(file)) {
        this.$message.warning('文件类型不符合上传要求');
        return false;
      }
      if (this.maxSize > 0 && file.size / 1024 / 1024 > this.maxSize) {
        this.$message.warning(`单文件大小不能超过 ${this.maxSize}MB`);
        return false;
      }
      return true;
    },
    validFileType(file) {
      if (!this.acceptedList.length) return true;
      const fileName = String(file.name || '').toLowerCase();
      const mimeType = String(file.type || '').toLowerCase();
      return this.acceptedList.some(type => {
        const value = String(type).toLowerCase();
        if (value === '*/*') return true;
        if (value.startsWith('.')) return fileName.endsWith(value);
        if (value.endsWith('/*')) return mimeType.startsWith(value.slice(0, -1));
        if (value.includes('/')) return mimeType === value;
        return fileName.endsWith(`.${value}`);
      });
    },
    handleSuccess(response, file, files) {
      if (!response || response.success === false || (response.code && response.code !== 200)) {
        this.$message.error((response && response.msg) || '上传失败');
        return;
      }
      this.emitUploadFiles(this.multiple ? files : [file]);
      this.$emit('success', this.normalizeUploadFile(file));
      this.$message.success('上传成功');
    },
    handleError() {
      this.$message.error('上传失败');
    },
    handleRemove(file, files) {
      if (this.readonly) return false;
      this.emitUploadFiles(files);
      return true;
    },
    emitUploadFiles(files) {
      const list = files
        .filter(file => file.status === 'success' || this.getFileUrl(file))
        .map(this.normalizeUploadFile)
        .filter(item => item.url);
      this.$emit('update:modelValue', list);
      this.$emit('change', list);
    },
    normalizeUploadFile(file) {
      const data = (file.response && file.response.data) || file.data || file;
      const url = data.link || data.url || data.domain || file.url || '';
      const name = data.name || data.originalName || file.name || this.getFileName(url) || '附件';
      const extension = this.getExtension({ name, url });
      return {
        ...data,
        uid: file.uid || data.uid,
        name,
        url,
        link: data.link || url,
        size: data.size || data.attachSize || file.size || '',
        extension,
        mimeType: data.mimeType || data.contentType || file.raw?.type || MIME_MAP[extension] || '',
      };
    },
    handlePreview(file) {
      const previewFile = this.normalizeUploadFile(file);
      if (!previewFile.url) {
        this.$message.warning('附件地址为空，无法预览');
        return;
      }
      this.previewFile = previewFile;
      this.previewSource = previewFile.url;
      this.previewVisible = true;
    },
    handleUnsupported() {
      this.$message.warning('当前文件暂不支持在线预览');
    },
    handleViewerError() {
      this.$message.error('附件预览失败');
    },
  },
};
</script>

<style lang="scss" scoped>
.vehicle-attachment-upload {
  width: 100%;

  :deep(.el-upload-list__item-name) {
    cursor: pointer;
  }

  &__empty {
    color: #909399;
  }
}

:global(.vehicle-attachment-upload__viewer-dialog .el-dialog__body) {
  padding: 12px 16px 16px;
}
</style>
