<template>
  <section>
    <header class="page-head">
      <div>
        <h2>Excel 导入</h2>
        <p>上传后先校验，确认导入后才会正式写入学员和证书数据。</p>
      </div>
      <el-button :disabled="!selectedTemplate" @click="downloadTemplate">下载所选模板</el-button>
    </header>

    <el-alert
      class="import-tip"
      type="info"
      :closable="false"
      title="请先选择证书模板，再下载对应 Excel。不同证书模板的列和必填项不同，不要混用文件。"
    />

    <div class="batch-template">
      <div>
        <strong>本批次证书模板</strong>
        <span>下载、校验和正式签发都以本次选择为准。</span>
      </div>
      <el-select v-model="templateCode" placeholder="选择证书模板">
        <el-option v-for="item in templates" :key="item.code" :label="item.name" :value="item.code" />
      </el-select>
      <img v-if="selectedTemplate" :src="selectedTemplate.preview_url" :alt="selectedTemplate.name" />
      <p v-if="selectedTemplate" class="template-fields">Excel 字段：{{ selectedImportColumns.join("、") }}</p>
    </div>

    <el-upload
      ref="uploadRef"
      class="upload"
      drag
      :auto-upload="false"
      :show-file-list="true"
      :limit="1"
      :disabled="!selectedTemplate"
      accept=".xlsx"
      @change="pickFile"
      @remove="clearSelectedFile"
      @exceed="replaceFile"
    >
      <el-icon class="upload-icon"><UploadFilled /></el-icon>
      <div>{{ selectedTemplate ? "把 Excel 文件拖到这里，或点击选择文件" : "请先选择证书模板" }}</div>
    </el-upload>
    <el-button type="primary" :disabled="!selectedFile || !selectedTemplate" :loading="uploading" @click="uploadFile">上传并校验</el-button>

    <el-card class="panel" shadow="never">
      <el-table :data="batches" border>
        <el-table-column prop="id" label="批次ID" width="90" />
        <el-table-column prop="filename" label="文件名" min-width="220" />
        <el-table-column label="证书模板" width="170">
          <template #default="{ row }">{{ templateName(row.template_code) }}</template>
        </el-table-column>
        <el-table-column label="状态" width="120">
          <template #default="{ row }">{{ statusText(row.status) }}</template>
        </el-table-column>
        <el-table-column prop="total_rows" label="总行数" width="100" />
        <el-table-column prop="valid_rows" label="可导入" width="100" />
        <el-table-column prop="failed_rows" label="失败" width="100" />
        <el-table-column prop="created_at" label="上传时间" width="190" />
        <el-table-column label="操作" width="470">
          <template #default="{ row }">
            <el-button size="small" @click="downloadSource(row)">下载原文件</el-button>
            <el-button
              v-if="row.status === 'validated'"
              size="small"
              type="primary"
              :loading="confirmingId === row.id"
              @click="confirmBatch(row)"
            >
              确认导入
            </el-button>
            <el-button
              v-if="row.status === 'imported'"
              size="small"
              :loading="preGeneratingId === row.id"
              @click="startPregeneration(row)"
            >
              预生成PDF
            </el-button>
            <el-button v-if="row.error_report_path" size="small" @click="downloadErrorReport(row)">错误报告</el-button>
            <el-button size="small" type="danger" @click="deleteBatch(row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-card>

    <el-dialog v-model="pregenerationVisible" title="批量预生成PDF" width="560px" @closed="stopPregenerationPolling">
      <div v-if="pregenerationJob" class="pregeneration-panel">
        <el-progress :percentage="pregenerationJob.percent" />
        <dl class="pregeneration-stats">
          <div><dt>总数</dt><dd>{{ pregenerationJob.total }}</dd></div>
          <div><dt>已完成</dt><dd>{{ pregenerationJob.completed }}</dd></div>
          <div><dt>新生成</dt><dd>{{ pregenerationJob.generated }}</dd></div>
          <div><dt>已缓存</dt><dd>{{ pregenerationJob.cached }}</dd></div>
          <div><dt>跳过</dt><dd>{{ pregenerationJob.skipped }}</dd></div>
          <div><dt>失败</dt><dd>{{ pregenerationJob.failed }}</dd></div>
        </dl>
        <p class="progress-note">当前任务按系统设置的最大并发数执行：{{ pregenerationJob.concurrency_limit }}</p>
        <el-alert
          v-if="pregenerationDone"
          :type="pregenerationJob.failed ? 'warning' : 'success'"
          :closable="false"
          :title="pregenerationJob.failed ? '预生成已完成，但有部分失败。' : '预生成已完成。'"
        />
        <ul v-if="pregenerationJob.errors.length" class="error-list">
          <li v-for="item in pregenerationJob.errors" :key="item">{{ item }}</li>
        </ul>
      </div>
    </el-dialog>
  </section>
</template>

<script setup lang="ts">
import { UploadFilled } from "@element-plus/icons-vue";
import {
  ElMessage,
  ElMessageBox,
  genFileId,
  type UploadFile,
  type UploadInstance,
  type UploadRawFile,
} from "element-plus";
import { computed, onMounted, onUnmounted, ref } from "vue";

import { http, type CertificateTemplate, type ImportBatch, type PdfPregenerationJob } from "../api";
import { apiErrorMessage, downloadFile } from "../download";

const batches = ref<ImportBatch[]>([]);
const templates = ref<CertificateTemplate[]>([]);
const templateCode = ref("");
const selectedFile = ref<File | null>(null);
const uploadRef = ref<UploadInstance>();
const uploading = ref(false);
const confirmingId = ref<number | null>(null);
const preGeneratingId = ref<number | null>(null);
const pregenerationVisible = ref(false);
const pregenerationJob = ref<PdfPregenerationJob | null>(null);
let pregenerationTimer: number | null = null;

const pregenerationDone = computed(() => ["completed", "completed_with_errors"].includes(pregenerationJob.value?.status || ""));
const selectedTemplate = computed(() => templates.value.find((item) => item.code === templateCode.value));
const selectedImportColumns = computed(() => [
  "姓名",
  "手机号",
  "项目代码",
  ...(selectedTemplate.value?.dynamic_fields.filter((field) => field !== "姓名") || []),
]);

function templateName(code: string) {
  return templates.value.find((item) => item.code === code)?.name || code;
}

function statusText(status: string) {
  const labels: Record<string, string> = {
    uploaded: "已上传",
    validated: "已校验",
    imported: "已导入",
    failed: "校验失败",
  };
  return labels[status] || status;
}

function pickFile(file: UploadFile) {
  selectedFile.value = file.raw || null;
}

function clearSelectedFile() {
  selectedFile.value = null;
}

function resetUpload() {
  selectedFile.value = null;
  uploadRef.value?.clearFiles();
}

function replaceFile(files: File[]) {
  const file = files[0] as UploadRawFile | undefined;
  if (!file) return;
  uploadRef.value?.clearFiles();
  file.uid = genFileId();
  uploadRef.value?.handleStart(file);
}

function downloadTemplate() {
  if (!selectedTemplate.value) return;
  downloadFile(
    `/admin/import-batches/template?template_code=${encodeURIComponent(templateCode.value)}`,
    `certificate-import-${templateCode.value}.xlsx`,
  );
}

async function loadBatches() {
  const { data } = await http.get<ImportBatch[]>("/admin/import-batches");
  batches.value = data;
}

async function loadTemplates() {
  const { data } = await http.get<CertificateTemplate[]>("/admin/certificate-templates");
  templates.value = data;
}

async function uploadFile() {
  if (!selectedFile.value) return;
  uploading.value = true;
  try {
    const body = new FormData();
    body.append("file", selectedFile.value);
    body.append("template_code", templateCode.value);
    const { data } = await http.post<ImportBatch>("/admin/import-batches", body);
    if (data.failed_rows) {
      ElMessage.warning(`校验完成：${data.valid_rows} 行可导入，${data.failed_rows} 行失败，请下载错误报告查看原因`);
    } else {
      ElMessage.success(`校验通过，共 ${data.valid_rows} 行可导入`);
    }
    resetUpload();
    await loadBatches();
  } catch (error: any) {
    ElMessage.error(await apiErrorMessage(error, "上传校验失败"));
  } finally {
    uploading.value = false;
  }
}

async function confirmBatch(row: ImportBatch) {
  await ElMessageBox.confirm(`确认把批次 ${row.id} 中 ${row.valid_rows} 行校验通过的数据写入系统？`, "确认导入", { type: "warning" });
  confirmingId.value = row.id;
  try {
    const { data } = await http.post<ImportBatch>(`/admin/import-batches/${row.id}/confirm`);
    row.status = data.status;
    ElMessage.success(`导入完成，当前状态：${statusText(data.status)}`);
    await loadBatches();
  } finally {
    confirmingId.value = null;
  }
}

function downloadSource(row: ImportBatch) {
  downloadFile(`/admin/import-batches/${row.id}/file`, row.filename);
}

function downloadErrorReport(row: ImportBatch) {
  downloadFile(`/admin/import-batches/${row.id}/error-report`, `import-errors-${row.id}.xlsx`);
}

async function deleteBatch(row: ImportBatch) {
  await ElMessageBox.confirm(`确认删除导入批次 ${row.id}？这只删除上传文件和导入记录，不会删除已经生成的学员和证书。`, "删除导入记录", { type: "warning" });
  await http.delete(`/admin/import-batches/${row.id}`);
  ElMessage.success("导入记录已删除");
  await loadBatches();
}

async function startPregeneration(row: ImportBatch) {
  preGeneratingId.value = row.id;
  try {
    const { data } = await http.post<PdfPregenerationJob>("/admin/certificates/pdf-pregeneration-jobs", {
      import_batch_id: row.id,
    });
    pregenerationJob.value = data;
    pregenerationVisible.value = true;
    startPregenerationPolling(data.id);
  } catch (error: any) {
    ElMessage.error(await apiErrorMessage(error, "启动PDF预生成失败"));
  } finally {
    preGeneratingId.value = null;
  }
}

function startPregenerationPolling(jobId: string) {
  stopPregenerationPolling();
  pregenerationTimer = window.setInterval(async () => {
    const { data } = await http.get<PdfPregenerationJob>(`/admin/certificates/pdf-pregeneration-jobs/${jobId}`);
    pregenerationJob.value = data;
    if (["completed", "completed_with_errors"].includes(data.status)) {
      stopPregenerationPolling();
    }
  }, 1000);
}

function stopPregenerationPolling() {
  if (pregenerationTimer) {
    clearInterval(pregenerationTimer);
    pregenerationTimer = null;
  }
}

onMounted(() => Promise.all([loadBatches(), loadTemplates()]));
onUnmounted(stopPregenerationPolling);
</script>

<style scoped>
.page-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
}

.page-head p {
  margin: 6px 0 0;
  color: #64748b;
}

.upload {
  margin-top: 16px;
  margin-bottom: 12px;
}

.batch-template {
  display: grid;
  grid-template-columns: minmax(260px, 1fr) 240px 180px;
  align-items: center;
  gap: 18px;
  margin-top: 16px;
  padding: 14px 16px;
  border: 1px solid var(--app-border);
  border-radius: 8px;
  background: #fff;
}

.batch-template div {
  display: grid;
  gap: 5px;
}

.batch-template span {
  color: var(--app-muted);
  font-size: 13px;
}

.batch-template img {
  width: 180px;
  aspect-ratio: 1.36;
  object-fit: contain;
  border: 1px solid #edf2f4;
}

.template-fields {
  grid-column: 1 / -1;
  margin: 0;
  color: var(--app-muted);
  font-size: 13px;
}

.import-tip {
  margin-bottom: 4px;
}

.upload-icon {
  font-size: 36px;
  color: #208a87;
}

.panel {
  margin-top: 20px;
  border-radius: 8px;
}

.pregeneration-panel {
  display: grid;
  gap: 16px;
}

.pregeneration-stats {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 8px;
  margin: 0;
}

.pregeneration-stats div {
  border: 1px solid #e2edf0;
  border-radius: 8px;
  padding: 10px;
  text-align: center;
}

.pregeneration-stats dt {
  color: #64748b;
  font-size: 12px;
}

.pregeneration-stats dd {
  margin: 4px 0 0;
  font-size: 18px;
  font-weight: 700;
}

.progress-note {
  margin: 0;
  color: #64748b;
}

.error-list {
  margin: 0;
  color: #b91c1c;
  line-height: 1.8;
}

@media (max-width: 900px) {
  .batch-template {
    grid-template-columns: 1fr;
  }

  .batch-template img {
    width: min(100%, 260px);
  }
}
</style>
