<template>
  <module-page-shell title="资料上传">
    <div class="project-material-page">
      <section class="upload-panel query-list-panel">
        <div class="upload-panel__header">
          <div>
            <h3>资料上传</h3>
          </div>
          <el-button type="primary" @click="openUploadDialog()" v-hasPermi="['risk:material:add']">
            上传资料
          </el-button>
        </div>

        <div class="project-filter-bar">
          <div class="project-filter-bar__field project-filter-bar__field--project">
            <el-select v-model="queryParams.projectName" placeholder="请选择项目" filterable>
              <el-option
                v-for="item in projectOptions"
                :key="item.projectId"
                :label="item.projectName"
                :value="item.projectName"
              />
            </el-select>
          </div>
          <div class="project-filter-bar__actions">
            <el-button type="primary" class="project-filter-bar__button" @click="handleQuery">查询</el-button>
            <el-button class="project-filter-bar__button" @click="resetQuery">重置</el-button>
          </div>
        </div>

        <el-table v-loading="loading" :data="pagedRows" class="upload-table">
          <el-table-column label="分类" prop="category" min-width="140" />
          <el-table-column label="文件名" min-width="360">
            <template slot-scope="scope">
              <div class="file-name-cell">
                <el-tooltip :content="scope.row.materialName" placement="top">
                  <el-button v-if="!scope.row.uploaded" type="text" class="pending-name" @click="openUploadDialog(scope.row)">
                    <span class="file-name-cell__text">{{ scope.row.materialName }}</span>
                  </el-button>
                  <span v-else class="file-name-cell__text">{{ scope.row.materialName }}</span>
                </el-tooltip>
              </div>
            </template>
          </el-table-column>
          <el-table-column label="上传人" prop="ownerName" min-width="120" />
          <el-table-column label="状态" min-width="120">
            <template slot-scope="scope">
              <span :class="['status-text', scope.row.uploaded ? 'status-text--uploaded' : 'status-text--pending']">
                {{ scope.row.uploaded ? '已上传' : '待补件' }}
              </span>
            </template>
          </el-table-column>
          <el-table-column label="时间" min-width="180">
            <template slot-scope="scope">
              {{ scope.row.submitDate || '--' }}
            </template>
          </el-table-column>
          <el-table-column label="上传" min-width="120" align="center">
            <template slot-scope="scope">
              <el-button size="mini" @click="openUploadDialog(scope.row)">
                {{ scope.row.uploaded ? '重新上传' : '上传' }}
              </el-button>
            </template>
          </el-table-column>
          <el-table-column label="预览" min-width="100" align="center">
            <template slot-scope="scope">
              <el-button v-if="scope.row.uploaded" size="mini" @click="handlePreview(scope.row)">预览</el-button>
            </template>
          </el-table-column>
          <el-table-column label="下载" min-width="100" align="center">
            <template slot-scope="scope">
              <el-button v-if="scope.row.uploaded" size="mini" @click="handleDownload(scope.row)">下载</el-button>
            </template>
          </el-table-column>
        </el-table>

        <pagination
          v-show="displayRows.length > 0"
          :total="displayRows.length"
          :page.sync="queryParams.pageNum"
          :limit.sync="queryParams.pageSize"
          @pagination="handlePagination"
        />
      </section>

      <el-dialog
        :visible.sync="uploadOpen"
        width="760px"
        top="7vh"
        append-to-body
        :close-on-click-modal="false"
        custom-class="material-upload-dialog"
      >
        <span slot="title" class="dialog-title">资料上传</span>
        <el-form ref="uploadFormRef" :model="uploadForm" :rules="uploadRules" label-position="top">
          <el-form-item label="所属项目" prop="projectName">
            <el-input v-model="uploadForm.projectName" disabled />
          </el-form-item>
          <el-form-item label="资料分类" prop="category">
            <el-select v-model="uploadForm.category" style="width: 100%" @change="handleCategoryChange">
              <el-option
                v-for="item in categoryOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="文件名" prop="materialName">
            <el-input v-model="uploadForm.materialName" placeholder="请输入文件名" />
          </el-form-item>
          <el-form-item label="上传人" prop="ownerName">
            <el-input v-model="uploadForm.ownerName" placeholder="请输入上传人" />
          </el-form-item>
          <el-form-item label="上传附件" prop="fileToken" class="upload-field">
            <el-upload
              ref="uploader"
              drag
              action="#"
              :auto-upload="false"
              :limit="1"
              :file-list="uploadFileList"
              :on-change="handleFileChange"
              :on-remove="handleFileRemove"
              :before-upload="() => false"
            >
              <i class="el-icon-upload"></i>
              <div class="el-upload__text">将文件拖到此处，或<em>点击上传</em></div>
              <div slot="tip" class="el-upload__tip">支持 PDF、Office、图片和压缩包等常见文件</div>
            </el-upload>
          </el-form-item>
          <el-form-item label="备注" prop="remark">
            <el-input
              v-model="uploadForm.remark"
              type="textarea"
              :rows="3"
              placeholder="补充说明资料来源、版本或需注意事项"
            />
          </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
          <el-button @click="uploadOpen = false">取消</el-button>
          <el-button @click="resetUploadForm">清空</el-button>
          <el-button type="primary" @click="submitUpload">上传资料</el-button>
        </div>
      </el-dialog>

      <el-dialog
        :visible.sync="previewOpen"
        width="980px"
        top="5vh"
        append-to-body
        custom-class="material-preview-dialog"
      >
        <span slot="title" class="dialog-title">{{ previewState.title || '资料预览' }}</span>
        <div class="preview-meta" v-if="previewState.row">
          <span>分类：{{ previewState.row.category }}</span>
          <span>上传人：{{ previewState.row.ownerName || '--' }}</span>
          <span>时间：{{ previewState.row.submitDate || '--' }}</span>
        </div>
        <div class="preview-body">
          <iframe v-if="previewState.type === 'pdf'" :src="previewState.url" class="preview-frame"></iframe>
          <img v-else-if="previewState.type === 'image'" :src="previewState.url" class="preview-image" />
          <pre v-else-if="previewState.type === 'text'" class="preview-text">{{ previewState.text }}</pre>
          <div v-else class="preview-placeholder">
            <i class="el-icon-document"></i>
            <p>当前文件类型暂不支持在线预览，可直接下载查看。</p>
            <p class="preview-placeholder__path">{{ previewState.row ? previewState.row.materialName : '' }}</p>
          </div>
        </div>
        <div slot="footer" class="dialog-footer">
          <el-button @click="previewOpen = false">关闭</el-button>
          <el-button v-if="previewState.row" @click="handleDownload(previewState.row)">下载资料</el-button>
          <el-button v-if="previewState.type === 'unsupported'" type="primary" @click="openPreviewInNewWindow">新窗口打开</el-button>
        </div>
      </el-dialog>
    </div>
  </module-page-shell>
</template>

<script>
import ModulePageShell from '@/components/ModulePageShell'
import { listProject } from '@/api/risk/project'
import { addMaterial, listMaterial, updateMaterial, uploadMaterialFile } from '@/api/risk/material'

const MATERIAL_REQUIREMENTS = [
  {
    category: '行业资料',
    materialType: '1',
    files: ['行业研究_2026Q1.pdf', '行业竞争分析.pdf', '区域市场容量评估.docx', '行业政策汇编.pdf', '行业风险清单.xlsx']
  },
  {
    category: '财务资料',
    materialType: '2',
    files: ['财务报表.xlsx', '现金流预测.xlsx', '应收账款明细.xlsx', '预算执行说明.docx']
  },
  {
    category: '法务资料',
    materialType: '3',
    files: ['法务合规.docx', '主体资质文件.pdf', '合同补充协议.pdf', '诉讼仲裁说明.docx']
  },
  {
    category: '担保品资料',
    materialType: '4',
    files: ['担保品清单.pdf', '抵押登记文件.pdf', '估值复核说明.docx']
  },
  {
    category: '其他资料',
    materialType: '1',
    files: ['管理层访谈纪要.docx', '风控补充材料.zip']
  }
]

const CATEGORY_OPTIONS = MATERIAL_REQUIREMENTS.map(item => ({
  value: item.category,
  label: item.category,
  materialType: item.materialType,
  files: item.files
}))

export default {
  name: 'ProjectMaterialUpload',
  components: {
    ModulePageShell
  },
  data() {
    const validateFile = (rule, value, callback) => {
      if (!this.uploadFile) {
        callback(new Error('请先选择上传文件'))
        return
      }
      callback()
    }

    return {
      loading: false,
      projectOptions: [],
      allMaterials: [],
      categoryOptions: CATEGORY_OPTIONS,
      queryParams: {
        projectName: '',
        pageNum: 1,
        pageSize: 10
      },
      uploadOpen: false,
      previewOpen: false,
      uploadForm: {},
      uploadSeed: {},
      uploadFile: null,
      uploadFileList: [],
      previewState: {
        title: '',
        type: 'unsupported',
        url: '',
        text: '',
        row: null
      },
      uploadRules: {
        projectName: [{ required: true, message: '请选择所属项目', trigger: 'change' }],
        category: [{ required: true, message: '请选择资料分类', trigger: 'change' }],
        materialName: [{ required: true, message: '请输入文件名', trigger: 'blur' }],
        ownerName: [{ required: true, message: '请输入上传人', trigger: 'blur' }],
        fileToken: [{ validator: validateFile, trigger: 'change' }]
      }
    }
  },
  computed: {
    currentProject() {
      return this.projectOptions.find(item => item.projectName === this.queryParams.projectName) || null
    },
    currentOwner() {
      return (this.currentProject && this.currentProject.projectManager) || this.$store.getters.nickName || this.$store.getters.name || '管理员'
    },
    displayRows() {
      if (!this.queryParams.projectName) {
        return []
      }
      return this.buildProjectRows(this.queryParams.projectName)
    },
    pagedRows() {
      const pageNum = Number(this.queryParams.pageNum) || 1
      const pageSize = Number(this.queryParams.pageSize) || 10
      const start = (pageNum - 1) * pageSize
      return this.displayRows.slice(start, start + pageSize)
    }
  },
  created() {
    this.refreshData()
  },
  activated() {
    this.refreshData()
  },
  methods: {
    resolvePreferredProjectName(currentProjectName = '') {
      const routeProjectName = `${this.$route.query.projectName || ''}`.trim()
      const names = this.projectOptions.map(item => item.projectName)
      if (routeProjectName && names.includes(routeProjectName)) {
        return routeProjectName
      }
      if (currentProjectName && names.includes(currentProjectName)) {
        return currentProjectName
      }
      return this.projectOptions[0] ? this.projectOptions[0].projectName : ''
    },
    async refreshData() {
      this.loading = true
      try {
        const [projectRes, materialRes] = await Promise.all([
          listProject({ pageNum: 1, pageSize: 1000 }),
          listMaterial({ pageNum: 1, pageSize: 1000 })
        ])
        this.projectOptions = (projectRes.rows || []).map(item => ({
          projectId: item.projectId,
          projectName: item.projectName,
          projectManager: item.projectManager || ''
        }))
        this.allMaterials = (materialRes.rows || []).map(item => this.normalizeMaterial(item))
        this.queryParams.projectName = this.resolvePreferredProjectName(this.queryParams.projectName)
      } finally {
        this.loading = false
      }
    },
    normalizeMaterial(item) {
      return {
        materialId: item.materialId,
        materialName: item.materialName || '',
        materialType: `${item.materialType || '1'}`,
        projectName: item.projectName || '',
        sourceChannel: item.sourceChannel || '',
        ownerName: item.ownerName || '',
        materialStatus: `${item.materialStatus || '0'}`,
        filePath: item.filePath || '',
        submitDate: this.normalizeDate(item.submitDate),
        remark: item.remark || ''
      }
    },
    normalizeDate(value) {
      if (!value) {
        return ''
      }
      return `${value}`.slice(0, 10).replace(/\//g, '-')
    },
    normalizeResourcePath(path) {
      if (!path) {
        return ''
      }
      if (/^https?:\/\//.test(path)) {
        return path
      }
      if (path.startsWith('/profile/')) {
        return path
      }
      if (path.startsWith('profile/')) {
        return `/${path}`
      }
      if (path.startsWith('/upload/')) {
        return `/profile${path}`
      }
      return `/profile/${path.replace(/^\//, '')}`
    },
    getPreviewUrl(path) {
      const normalized = this.normalizeResourcePath(path)
      if (!normalized) {
        return ''
      }
      if (/^https?:\/\//.test(normalized)) {
        return normalized
      }
      return `${process.env.VUE_APP_BASE_API}${normalized}`
    },
    resolveCategory(material) {
      const matchedByName = MATERIAL_REQUIREMENTS.find(item => item.files.includes(material.materialName))
      if (matchedByName) {
        return matchedByName.category
      }
      const matched = CATEGORY_OPTIONS.find(item => item.materialType === `${material.materialType}`)
      return matched ? matched.value : '其他资料'
    },
    buildProjectRows(projectName) {
      const projectMaterials = this.allMaterials.filter(item => item.projectName === projectName)
      const usedIds = new Set()
      const rows = []

      MATERIAL_REQUIREMENTS.forEach(group => {
        group.files.forEach(fileName => {
          const matched = projectMaterials.find(item => !usedIds.has(item.materialId) && item.materialName === fileName)
          if (matched) {
            usedIds.add(matched.materialId)
            rows.push(this.buildUploadedRow(matched, group.category))
            return
          }
          rows.push(this.buildPendingRow(group.category, group.materialType, fileName))
        })
      })

      projectMaterials
        .filter(item => !usedIds.has(item.materialId))
        .forEach(item => {
          rows.push(this.buildUploadedRow(item, this.resolveCategory(item)))
        })

      return rows
    },
    buildPendingRow(category, materialType, materialName) {
      return {
        rowKey: `${this.queryParams.projectName}-${category}-${materialName}`,
        materialId: undefined,
        projectName: this.queryParams.projectName,
        category,
        materialType,
        materialName,
        ownerName: this.currentOwner,
        submitDate: '',
        filePath: '',
        uploaded: false,
        sourceChannel: '项目补件',
        remark: ''
      }
    },
    buildUploadedRow(material, category) {
      return {
        rowKey: material.materialId,
        materialId: material.materialId,
        projectName: material.projectName,
        category,
        materialType: material.materialType,
        materialName: material.materialName,
        ownerName: material.ownerName || this.currentOwner,
        submitDate: material.submitDate,
        filePath: material.filePath,
        uploaded: Boolean(material.filePath),
        sourceChannel: material.sourceChannel,
        remark: material.remark || ''
      }
    },
    handleQuery() {
      this.queryParams.pageNum = 1
      this.refreshData()
    },
    resetQuery() {
      this.queryParams.projectName = this.projectOptions.length > 0 ? this.projectOptions[0].projectName : ''
      this.queryParams.pageNum = 1
      this.refreshData()
    },
    handlePagination() {
      const pageSize = Number(this.queryParams.pageSize) || 10
      const totalPages = Math.max(1, Math.ceil(this.displayRows.length / pageSize))
      if (this.queryParams.pageNum > totalPages) {
        this.queryParams.pageNum = totalPages
      }
    },
    buildUploadSeed(row) {
      const defaultCategory = row ? row.category : CATEGORY_OPTIONS[0].value
      const categoryOption = CATEGORY_OPTIONS.find(item => item.value === defaultCategory) || CATEGORY_OPTIONS[0]
      return {
        materialId: row && row.materialId ? row.materialId : undefined,
        projectName: this.queryParams.projectName,
        category: categoryOption.value,
        materialType: categoryOption.materialType,
        materialName: row ? row.materialName : '',
        ownerName: row && row.ownerName ? row.ownerName : this.currentOwner,
        sourceChannel: row && row.sourceChannel ? row.sourceChannel : '项目补件',
        materialStatus: '2',
        submitDate: this.todayString(),
        remark: row && row.remark ? row.remark : '',
        fileToken: `${Date.now()}`
      }
    },
    todayString() {
      const now = new Date()
      const y = now.getFullYear()
      const m = `${now.getMonth() + 1}`.padStart(2, '0')
      const d = `${now.getDate()}`.padStart(2, '0')
      return `${y}-${m}-${d}`
    },
    cloneForm(form) {
      return JSON.parse(JSON.stringify(form))
    },
    openUploadDialog(row) {
      if (!this.queryParams.projectName) {
        this.$message.warning('请先选择项目')
        return
      }
      this.uploadSeed = this.buildUploadSeed(row)
      this.uploadForm = this.cloneForm(this.uploadSeed)
      this.uploadFile = null
      this.uploadFileList = []
      this.uploadOpen = true
      this.$nextTick(() => {
        if (this.$refs.uploadFormRef) {
          this.$refs.uploadFormRef.clearValidate()
        }
        if (this.$refs.uploader) {
          this.$refs.uploader.clearFiles()
        }
      })
    },
    resetUploadForm() {
      this.uploadForm = this.cloneForm(this.uploadSeed)
      this.uploadFile = null
      this.uploadFileList = []
      this.$nextTick(() => {
        if (this.$refs.uploadFormRef) {
          this.$refs.uploadFormRef.clearValidate()
        }
        if (this.$refs.uploader) {
          this.$refs.uploader.clearFiles()
        }
      })
    },
    handleCategoryChange(value) {
      const categoryOption = CATEGORY_OPTIONS.find(item => item.value === value)
      if (!categoryOption) {
        return
      }
      this.uploadForm.materialType = categoryOption.materialType
      if (!this.uploadForm.materialName) {
        this.uploadForm.materialName = categoryOption.files[0]
      }
    },
    handleFileChange(file, fileList) {
      this.uploadFile = file.raw
      this.uploadFileList = fileList.slice(-1)
      if (!this.uploadForm.materialName) {
        this.uploadForm.materialName = file.name
      }
      if (this.$refs.uploadFormRef) {
        this.$refs.uploadFormRef.validateField('fileToken')
      }
    },
    handleFileRemove() {
      this.uploadFile = null
      this.uploadFileList = []
      if (this.$refs.uploadFormRef) {
        this.$refs.uploadFormRef.validateField('fileToken')
      }
    },
    async submitUpload() {
      this.$refs.uploadFormRef.validate(async valid => {
        if (!valid) {
          return
        }
        try {
          const uploadRes = await uploadMaterialFile(this.uploadFile)
          const normalizedPath = this.normalizeResourcePath(uploadRes.fileName || uploadRes.url || '')
          const payload = {
            materialId: this.uploadForm.materialId,
            materialName: (this.uploadForm.materialName || '').trim(),
            materialType: this.uploadForm.materialType,
            projectName: this.uploadForm.projectName,
            sourceChannel: (this.uploadForm.sourceChannel || '项目补件').trim(),
            ownerName: (this.uploadForm.ownerName || this.currentOwner).trim(),
            materialStatus: '2',
            filePath: normalizedPath,
            submitDate: this.uploadForm.submitDate,
            remark: (this.uploadForm.remark || '').trim()
          }

          if (payload.materialId) {
            await updateMaterial(payload)
          } else {
            await addMaterial(payload)
          }

          this.$message.success('资料上传成功')
          this.uploadOpen = false
          await this.refreshData()
        } catch (error) {
          console.error(error)
        }
      })
    },
    getFileExtension(name) {
      const source = `${name || ''}`.split('?')[0]
      const index = source.lastIndexOf('.')
      return index === -1 ? '' : source.slice(index + 1).toLowerCase()
    },
    resolvePreviewType(row) {
      const ext = this.getFileExtension(row.materialName || row.filePath)
      if (['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp'].includes(ext)) {
        return 'image'
      }
      if (ext === 'pdf') {
        return 'pdf'
      }
      if (['txt', 'md', 'json', 'csv', 'xml', 'log'].includes(ext)) {
        return 'text'
      }
      return 'unsupported'
    },
    async handlePreview(row) {
      const url = this.getPreviewUrl(row.filePath)
      const type = this.resolvePreviewType(row)
      this.previewState = {
        title: row.materialName,
        type,
        url,
        text: '',
        row
      }
      this.previewOpen = true
      if (type === 'text') {
        try {
          const response = await fetch(url, { credentials: 'include' })
          this.previewState.text = await response.text()
        } catch (error) {
          this.previewState.text = '文本预览加载失败，请直接下载查看。'
        }
      }
    },
    handleDownload(row) {
      const resource = this.normalizeResourcePath(row.filePath)
      if (!resource) {
        this.$message.warning('当前资料暂无可下载文件')
        return
      }
      this.$download.resource(resource)
    },
    openPreviewInNewWindow() {
      if (this.previewState.url) {
        window.open(this.previewState.url, '_blank')
      }
    }
  }
}
</script>

<style lang="scss" scoped>
@import "~@/styles/project-filter-bar.scss";
@import "~@/styles/query-list-panel.scss";

.project-material-page {
  display: flex;
  flex-direction: column;
}

.project-material-page .project-filter-bar__field--project {
  flex: 0 1 280px;
  max-width: 320px;
  min-width: 220px;
}

.upload-panel {
  border: 1px solid #e4e8ef;
  border-radius: 8px;
  background: #fff;
  padding: 14px 16px 16px;
}

.upload-panel__header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 12px;
}

.upload-panel__header h3 {
  margin: 0;
  font-size: 16px;
  color: #2f3542;
}

.upload-panel__header p {
  margin: 8px 0 0;
  color: #98a2b3;
  font-size: 13px;
}

.pending-name {
  padding: 0;
  color: #606266;
}

.pending-name ::v-deep span {
  display: block;
  width: 100%;
}

.file-name-cell {
  width: 100%;
}

.file-name-cell__text {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

.status-text--uploaded {
  color: #303133;
}

.status-text--pending {
  color: #909399;
}

.upload-table ::v-deep th {
  padding: 11px 0;
}

.upload-table ::v-deep td {
  padding: 10px 0;
}

.upload-table ::v-deep .cell {
  line-height: 22px;
}

.upload-table ::v-deep .el-button--mini {
  min-height: 28px;
}

.dialog-title {
  font-size: 16px;
  font-weight: 700;
  color: #1f2937;
}

.upload-field ::v-deep .el-upload {
  width: 100%;
}

.upload-field ::v-deep .el-upload-dragger {
  width: 100%;
}

.preview-meta {
  display: flex;
  gap: 20px;
  margin-bottom: 12px;
  color: #6b7280;
  font-size: 13px;
}

.preview-body {
  min-height: 420px;
  max-height: 70vh;
  overflow: auto;
  border: 1px solid #ebeef5;
  border-radius: 6px;
  background: #f8fafc;
}

.preview-frame {
  width: 100%;
  height: 68vh;
  border: 0;
  background: #fff;
}

.preview-image {
  display: block;
  max-width: 100%;
  margin: 0 auto;
}

.preview-text {
  margin: 0;
  padding: 16px;
  white-space: pre-wrap;
  word-break: break-word;
  color: #1f2937;
}

.preview-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 420px;
  color: #6b7280;
}

.preview-placeholder i {
  font-size: 44px;
  margin-bottom: 12px;
}

.preview-placeholder p {
  margin: 4px 0;
}

.preview-placeholder__path {
  color: #9ca3af;
}

@media screen and (max-width: 768px) {
  .upload-panel__header {
    flex-direction: column;
    align-items: stretch;
  }

  .preview-meta {
    flex-direction: column;
    gap: 6px;
  }
}
</style>
