<template>
  <div>
    <BasicTable @register="registerTable">
      <template #tableTitle>
        <a-button type="primary" @click="openCreate" v-auth="'print:bizBind:add'">新增绑定</a-button>
      </template>
      <template #action="{ record }">
        <TableAction
          :actions="[
            {
              label: '编辑',
              onClick: () => openEdit(record),
              auth: 'print:bizBind:edit',
            },
            {
              label: '删除',
              color: 'error',
              popConfirm: {
                title: '确认删除该绑定？',
                confirm: () => handleDelete(record),
              },
              auth: 'print:bizBind:delete',
            },
          ]"
        />
      </template>
    </BasicTable>

    <BasicModal
      @register="registerModal"
      :title="modalTitle"
      width="920px"
      @ok="submitModal"
      :confirm-loading="modalSubmitLoading"
      destroy-on-close
    >
      <a-spin :spinning="tplLoading || parseLoading">
        <a-space direction="vertical" style="width: 100%" size="middle">
          <a-alert
            type="info"
            show-icon
            message="配置步骤"
            description="1）选择业务类型；2）选择已发布的打印模板；3）为模板每个占位字段（bindField）指定对应的业务 JSON 字段；可点击「同名匹配」快速对齐。"
          />

          <a-form layout="vertical">
            <a-row :gutter="16">
              <a-col :span="12">
                <a-form-item label="业务" required>
                  <a-select
                    v-model:value="form.bizCode"
                    :options="bizSelectOptions"
                    placeholder="选择业务"
                    show-search
                    option-filter-prop="label"
                    :disabled="isEditMode"
                    @change="onBizCodeChange"
                  />
                </a-form-item>
              </a-col>
              <a-col :span="12">
                <a-form-item label="打印模板" required>
                  <a-select
                    v-model:value="form.templateId"
                    :options="tplSelectOptions"
                    placeholder="选择模板"
                    show-search
                    option-filter-prop="label"
                    @change="onTemplateChange"
                  />
                </a-form-item>
              </a-col>
            </a-row>
            <a-form-item label="备注">
              <a-input v-model:value="form.remark" placeholder="可选" />
            </a-form-item>
          </a-form>

          <a-space wrap>
            <a-button type="primary" ghost @click="reloadTemplateFields" :loading="parseLoading">
              解析模板占位字段
            </a-button>
            <a-button @click="autoMatchFields" :disabled="!bizFields.length || !tplFields.length">
              同名自动匹配
            </a-button>
          </a-space>

          <div v-if="tplFields.length">
            <div style="margin-bottom: 8px; font-weight: 500">字段映射</div>
            <a-table
              size="small"
              row-key="templateField"
              :pagination="false"
              :columns="mapTableColumns"
              :data-source="mappingRows"
              bordered
            >
              <template #bodyCell="{ column, record }">
                <template v-if="column.key === 'bizField'">
                  <a-select
                    v-model:value="record.bizField"
                    :options="bizFieldOptions"
                    allow-clear
                    show-search
                    option-filter-prop="label"
                    style="width: 100%"
                    placeholder="选择业务字段"
                  />
                </template>
              </template>
            </a-table>
          </div>
          <a-empty v-else-if="form.templateId && !parseLoading" description="请点击「解析模板占位字段」或切换模板" />

          <a-divider />
          <div style="font-weight: 500">映射预览（可选）</div>
          <a-textarea
            v-model:value="previewBizJson"
            placeholder='粘贴业务 JSON，例如：{"barcode":"TEST001","materialName":"胶料A"}'
            :rows="4"
          />
          <a-button type="dashed" @click="runPreview" :loading="previewLoading">生成打印数据预览</a-button>
          <pre v-if="previewResult" class="preview-pre">{{ previewResult }}</pre>
        </a-space>
      </a-spin>
    </BasicModal>
  </div>
</template>

<script lang="ts" setup>
  import { computed, ref, unref } from 'vue';
  import { BasicTable, TableAction, useTable } from '/@/components/Table';
  import { BasicModal, useModal } from '/@/components/Modal';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { columns } from './bizTemplateBind.data';
  import * as Api from './bizTemplateBind.api';
  import { list as tplList } from '../template/printTemplate.api';

  const { createMessage } = useMessage();

  interface BizTypeItem {
    bizCode: string;
    bizName: string;
    fields: { fieldKey: string; label: string; description?: string }[];
  }

  interface TplFieldItem {
    bindField: string;
    elementType?: string;
    titleHint?: string;
  }

  interface MappingRow {
    templateField: string;
    bizField?: string;
    elementType?: string;
    titleHint?: string;
  }

  const bizTypesRef = ref<BizTypeItem[]>([]);
  const tplListRef = ref<{ id: string; templateCode: string; templateName: string }[]>([]);
  const tplLoading = ref(false);
  const parseLoading = ref(false);
  const modalSubmitLoading = ref(false);
  const previewLoading = ref(false);
  const previewBizJson = ref('');
  const previewResult = ref('');

  const form = ref({
    id: '' as string | undefined,
    bizCode: undefined as string | undefined,
    bizName: '' as string | undefined,
    templateId: undefined as string | undefined,
    remark: '' as string | undefined,
  });

  const tplFields = ref<TplFieldItem[]>([]);
  const bizFields = ref<BizTypeItem['fields']>([]);
  const mappingRows = ref<MappingRow[]>([]);

  const isEditMode = ref(false);
  const modalTitle = computed(() => (unref(isEditMode) ? '编辑业务打印绑定' : '新增业务打印绑定'));

  const bizSelectOptions = computed(() =>
    unref(bizTypesRef).map((b) => ({
      label: `${b.bizName}（${b.bizCode}）`,
      value: b.bizCode,
    })),
  );

  const tplSelectOptions = computed(() =>
    unref(tplListRef).map((t) => ({
      label: `${t.templateName}（${t.templateCode}）`,
      value: t.id,
    })),
  );

  const bizFieldOptions = computed(() =>
    unref(bizFields).map((f) => ({
      label: f.label ? `${f.label}（${f.fieldKey}）` : f.fieldKey,
      value: f.fieldKey,
    })),
  );

  const mapTableColumns = [
    { title: '模板占位（bindField）', dataIndex: 'templateField', width: 220 },
    { title: '类型', dataIndex: 'elementType', width: 100 },
    { title: '标题/提示', dataIndex: 'titleHint', ellipsis: true },
    { title: '业务字段', key: 'bizField', width: 260 },
  ];

  const [registerTable, { reload }] = useTable({
    title: '业务打印绑定',
    api: Api.list,
    columns,
    useSearchForm: false,
    showTableSetting: true,
    bordered: true,
    showIndexColumn: true,
    // 必须与模板插槽 #action 对应，否则操作列不会渲染（useTable 无 useListPage 的默认 slots）
    actionColumn: {
      width: 160,
      title: '操作',
      fixed: 'right',
      dataIndex: 'action',
      slots: { customRender: 'action' },
    },
  });

  const [registerModal, { openModal, closeModal }] = useModal();

  async function loadBizTypes() {
    const res = await Api.bizTypes();
    bizTypesRef.value = res || [];
  }

  async function loadAllTemplates() {
    tplLoading.value = true;
    try {
      const res = await tplList({ pageNo: 1, pageSize: 500 });
      tplListRef.value = res?.records ?? [];
    } finally {
      tplLoading.value = false;
    }
  }

  function onBizCodeChange(code: string) {
    const hit = unref(bizTypesRef).find((b) => b.bizCode === code);
    bizFields.value = hit?.fields ?? [];
    form.value.bizName = hit?.bizName;
  }

  async function onTemplateChange() {
    tplFields.value = [];
    mappingRows.value = [];
    await reloadTemplateFields();
  }

  async function reloadTemplateFields() {
    const tid = form.value.templateId;
    if (!tid) {
      tplFields.value = [];
      mappingRows.value = [];
      return;
    }
    parseLoading.value = true;
    try {
      const list = (await Api.parseTemplateFields(tid)) as TplFieldItem[];
      tplFields.value = list || [];
      rebuildMappingRows();
    } finally {
      parseLoading.value = false;
    }
  }

  function rebuildMappingRows() {
    const saved = unref(savedMappingRef);
    mappingRows.value = unref(tplFields).map((t) => {
      const templateField = t.bindField;
      const hit = saved.find((x) => x.templateField === templateField);
      return {
        templateField,
        bizField: hit?.bizField,
        elementType: t.elementType,
        titleHint: t.titleHint,
      };
    });
  }

  const savedMappingRef = ref<{ templateField: string; bizField?: string }[]>([]);

  function autoMatchFields() {
    const set = new Map(unref(bizFields).map((f) => [f.fieldKey, f.fieldKey]));
    for (const row of unref(mappingRows)) {
      if (set.has(row.templateField)) {
        row.bizField = row.templateField;
      }
    }
    mappingRows.value = [...unref(mappingRows)];
  }

  function buildFieldMappingJson() {
    const arr = unref(mappingRows)
      .filter((r) => r.bizField)
      .map((r) => ({ templateField: r.templateField, bizField: r.bizField }));
    return JSON.stringify(arr);
  }

  async function openCreate() {
    isEditMode.value = false;
    savedMappingRef.value = [];
    form.value = { id: undefined, bizCode: undefined, bizName: undefined, templateId: undefined, remark: undefined };
    tplFields.value = [];
    bizFields.value = [];
    mappingRows.value = [];
    previewBizJson.value = '';
    previewResult.value = '';
    await loadBizTypes();
    await loadAllTemplates();
    openModal(true);
  }

  async function openEdit(record: Recordable) {
    isEditMode.value = true;
    await loadBizTypes();
    await loadAllTemplates();
    form.value = {
      id: record.id,
      bizCode: record.bizCode,
      bizName: record.bizName,
      templateId: record.templateId,
      remark: record.remark,
    };
    onBizCodeChange(record.bizCode);
    try {
      savedMappingRef.value = JSON.parse(record.fieldMappingJson || '[]');
    } catch {
      savedMappingRef.value = [];
    }
    previewBizJson.value = '';
    previewResult.value = '';
    openModal(true);
    await reloadTemplateFields();
  }

  async function submitModal() {
    if (!form.value.bizCode) {
      createMessage.warning('请选择业务');
      return Promise.reject();
    }
    if (!form.value.templateId) {
      createMessage.warning('请选择打印模板');
      return Promise.reject();
    }
    modalSubmitLoading.value = true;
    try {
      const payload = {
        id: form.value.id,
        bizCode: form.value.bizCode,
        bizName: form.value.bizName,
        templateId: form.value.templateId,
        remark: form.value.remark,
        fieldMappingJson: buildFieldMappingJson(),
      };
      if (unref(isEditMode)) {
        await Api.edit(payload);
      } else {
        await Api.add(payload);
      }
      closeModal();
      reload();
    } finally {
      modalSubmitLoading.value = false;
    }
  }

  async function handleDelete(record: Recordable) {
    await Api.deleteOne({ id: record.id }, () => reload());
  }

  async function runPreview() {
    if (!form.value.bizCode) {
      createMessage.warning('请先选择业务；预览读取的是服务器已保存的绑定配置');
      return;
    }
    let obj: Record<string, unknown> = {};
    try {
      obj = previewBizJson.value ? (JSON.parse(previewBizJson.value) as Record<string, unknown>) : {};
    } catch {
      previewResult.value = '业务 JSON 格式不正确';
      return;
    }
    previewLoading.value = true;
    try {
      const res = await Api.previewMappedData({ bizCode: form.value.bizCode, bizDataJson: obj });
      previewResult.value = JSON.stringify(res, null, 2);
    } catch (e: unknown) {
      previewResult.value = e instanceof Error ? e.message : String(e);
    } finally {
      previewLoading.value = false;
    }
  }
</script>

<style scoped>
  .preview-pre {
    background: #f5f5f5;
    padding: 12px;
    border-radius: 4px;
    max-height: 240px;
    overflow: auto;
    font-size: 12px;
  }
</style>
