<!-- 广告位导入备份弹窗 -->
<template>
  <ele-modal
    :width="520"
    :footer="null"
    title="导入备份"
    :visible="visible"
    @update:visible="updateVisible"
  >
    <a-spin :spinning="loading">
      <a-upload-dragger
        accept=".xls,.xlsx"
        :show-upload-list="false"
        :customRequest="doUpload"
        style="padding: 24px 0; margin-bottom: 16px"
      >
        <p class="ant-upload-drag-icon">
          <cloud-upload-outlined />
        </p>
        <p class="ant-upload-hint">将文件拖到此处，或点击上传</p>
      </a-upload-dragger>
    </a-spin>
    <div class="ele-text-center">
      <span>只能上传xls、xlsx文件；恢复时：有广告ID则更新，没有则新增</span>
    </div>
  </ele-modal>
</template>

<script lang="ts" setup>
  import { createVNode, ref } from 'vue';
  import { message, Modal } from 'ant-design-vue';
  import { CloudUploadOutlined } from '@ant-design/icons-vue';
  import { read, utils } from 'xlsx';
  import { addCmsAd, updateCmsAd } from '@/api/cms/cmsAd';
  import type { CmsAd } from '@/api/cms/cmsAd/model';

  const emit = defineEmits<{
    (e: 'done'): void;
    (e: 'update:visible', visible: boolean): void;
  }>();

  defineProps<{
    // 是否打开弹窗
    visible: boolean;
  }>();

  // 导入请求状态
  const loading = ref(false);

  const COL = {
    adId: '广告ID',
    type: '类型',
    code: '唯一标识',
    categoryId: '栏目ID',
    categoryName: '栏目名称',
    name: '广告位名称',
    width: '宽',
    height: '高',
    style: '样式',
    images: '图片数据',
    path: '链接',
    sortNumber: '排序号',
    comments: '备注',
    status: '状态',
    lang: '语言',
    tenantId: '租户ID',
    merchantId: '商户ID'
  } as const;

  const parseNumber = (val: any): number | undefined => {
    if (val === '' || val === null || val === undefined) {
      return undefined;
    }
    const n = Number(val);
    return Number.isFinite(n) ? n : undefined;
  };

  const parseString = (val: any): string | undefined => {
    if (val === '' || val === null || val === undefined) {
      return undefined;
    }
    return String(val);
  };

  const normalizeImages = (val: any): string => {
    const raw = parseString(val);
    if (!raw) {
      return '';
    }
    // 备份文件中图片数据优先使用 JSON；如果是 url，则补成编辑器保存时的数组格式
    try {
      const parsed = JSON.parse(raw);
      return JSON.stringify(parsed ?? []);
    } catch (_e) {
      return JSON.stringify([
        { uid: '', url: raw, status: 'done', title: '', path: '' }
      ]);
    }
  };

  /* 上传 */
  const doUpload = async ({ file }: any) => {
    if (
      ![
        'application/vnd.ms-excel',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
      ].includes(file.type)
    ) {
      message.error('只能选择 excel 文件');
      return false;
    }
    if (file.size / 1024 / 1024 > 10) {
      message.error('大小不能超过 10MB');
      return false;
    }

    loading.value = true;
    try {
      const buffer = await file.arrayBuffer();
      const wb = read(buffer, { type: 'array' });
      const sheetName = wb.SheetNames?.[0];
      if (!sheetName) {
        throw new Error('文件中未找到工作表');
      }
      const sheet = wb.Sheets[sheetName];
      const rows = utils.sheet_to_json<Record<string, any>>(sheet, {
        defval: ''
      });
      if (!rows?.length) {
        throw new Error('文件内容为空');
      }
      if (!(COL.code in rows[0]) && !(COL.name in rows[0])) {
        throw new Error('文件格式不匹配，请使用“备份”导出的文件进行恢复');
      }

      Modal.confirm({
        title: '提示',
        content: `确定要恢复 ${rows.length} 条广告位吗？（有广告ID则更新，没有则新增）`,
        maskClosable: true,
        onOk: async () => {
          const hide = message.loading('恢复中..', 0);
          let ok = 0;
          let fail = 0;
          const errors: string[] = [];

          for (let i = 0; i < rows.length; i++) {
            const row = rows[i] ?? {};
            const payload: CmsAd = {
              adId: parseNumber(row[COL.adId]),
              type: parseNumber(row[COL.type]),
              code: parseString(row[COL.code]),
              categoryId: parseNumber(row[COL.categoryId]),
              categoryName: parseString(row[COL.categoryName]),
              name: parseString(row[COL.name]),
              width: parseString(row[COL.width]),
              height: parseString(row[COL.height]),
              style: parseString(row[COL.style]),
              images: normalizeImages(row[COL.images]),
              path: parseString(row[COL.path]),
              sortNumber: parseNumber(row[COL.sortNumber]),
              comments: parseString(row[COL.comments]),
              status: parseNumber(row[COL.status]),
              lang: parseString(row[COL.lang]),
              tenantId: parseNumber(row[COL.tenantId]),
              merchantId: parseNumber(row[COL.merchantId])
            };

            try {
              const saveOrUpdate = payload.adId ? updateCmsAd : addCmsAd;
              await saveOrUpdate(payload);
              ok++;
            } catch (e: any) {
              fail++;
              errors.push(
                `第${i + 1}行${payload.code ? `(${payload.code})` : ''}: ${
                  e?.message || '失败'
                }`
              );
            }
          }

          hide();
          loading.value = false;
          if (fail > 0) {
            message.warning(`恢复完成：成功${ok}，失败${fail}`);
            // 避免弹窗过长，只展示前几条
            Modal.error({
              title: '部分恢复失败',
              content: createVNode(
                'pre',
                {
                  style:
                    'white-space: pre-wrap; max-height: 320px; overflow: auto; margin: 0;'
                },
                errors.slice(0, 8).join('\n')
              )
            });
          } else {
            message.success(`恢复完成：成功${ok}`);
          }
          updateVisible(false);
          emit('done');
        },
        onCancel: () => {
          loading.value = false;
        }
      });
    } catch (e: any) {
      loading.value = false;
      message.error(e?.message || '解析失败，请重试');
    }
    return false;
  };

  /* 更新 visible */
  const updateVisible = (value: boolean) => {
    emit('update:visible', value);
  };
</script>
