<script lang="ts" setup>
/**
 * OA 场景新增/编辑内页表单（由列表页 v-if 挂载，替代原 modal.vue）
 *
 * 三步 C 端化流程（任务 3 大改造）：
 *   Step 1 基础信息：scene_code/scene_name/启用平台/字段绑定模式
 *   Step 2 平台配置（合并原"平台消息 + 投递目标"）：
 *         - 顶部 sticky 平台 Tab（仅展示 Step 1 选中的平台）
 *         - 纵向滚动：消息类型 + payload + 字段绑定 + 推送机器人 + 投递目标 + @人
 *         - 桌面右侧 / 移动底部实时消息预览
 *   Step 3 测试发送：按启用平台独立测试，每平台一行 + 手机号记忆
 *
 * 提交 at_users 统一为双结构（后端兼容旧纯数组）：
 *   { work_wechat: { admin_ids: number[], userids: string[] }, work_wechat_app: { ... } }
 * work_wechat_app 额外提交 targets：人/多群/每群 @
 */

import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';

import { Page, useVbenModal, VbenIcon } from '@vben/common-ui';

import {
  Alert,
  Button,
  Card,
  Checkbox,
  Empty,
  Input,
  message,
  Radio,
  RadioGroup,
  Select,
  Spin,
  Step,
  Steps,
  Tabs,
  Tag,
} from 'ant-design-vue';

import UserTagSelect from '#/components/form/components/user-tag-select.vue';
import { useVbenForm } from '#/adapter/form';
import { getAdminList } from '#/views/system/admin/api';
import { getOaPlatformList } from '#/views/system/oa-platform/api';
import {
  getOaRobotChatMembers,
  getOaRobotList,
} from '#/views/system/oa-robot/api';
import { isWorkWechatApp } from '#/views/system/oa-robot/config/form';
import TestResultModal from '#/views/system/oa-robot/components/test-result-modal.vue';
import {
  createOaScene,
  getOaMessageTypes,
  testSendOaScene,
  updateOaScene,
} from '#/views/system/oa-scene/api';
import { modalFormProps } from '#/views/system/oa-scene/config/form';
import MsgPreview from '#/views/system/oa-scene/components/msg-preview.vue';
import PhoneMemoryPicker from '#/views/system/oa-scene/components/phone-memory-picker.vue';
import PayloadSchemaForm from '#/views/system/oa-scene/components/payload-schema-form.vue';
import SceneTargetsPanel, {
  type SceneTargetItem,
} from '#/views/system/oa-scene/components/scene-targets-panel.vue';
import { getOaWwUserList } from '#/views/system/oa-chat/api/org';
interface MessageTypeOption {
  message_type: string;
  name: string;
  icon: string;
  need_media: number;
  payload_schema: string | null;
}

interface PayloadFieldSchema {
  name: string;
  label: string;
  component: string;
  required?: boolean;
  props?: Record<string, any>;
}

/** TestResultModal 所需的结果项结构 */
interface TestResultItem {
  message_type: string;
  name: string;
  success: boolean;
  message: string;
  request?: any;
  response: any;
  cost_ms: number;
}

const props = defineProps<{
  /** 详情或空对象（新增） */
  values: Record<string, any>;
  /** 是否编辑模式 */
  isUpdate: boolean;
}>();

const emit = defineEmits<{
  back: [];
  saved: [];
}>();

defineOptions({ name: 'OaSceneFormPage' });

const loading = ref(false);
const saving = ref(false);
const testing = ref(false);

/** 分步表单当前步：0=基础信息, 1=平台配置（合并）, 2=测试发送 */
const currentStep = ref(0);
const stepValidating = ref(false);
const STEP_TITLES = ['基础信息', '平台配置', '测试发送'];

/** 平台列表（仅展示启用的平台） */
const platforms = ref<any[]>([]);
/** 启用平台下的所有机器人 */
const allRobots = ref<any[]>([]);
/** 员工列表（用于 @ 人多选） */
const adminList = ref<any[]>([]);
/** 当前选中的 Tab 平台编码 */
const activePlatformTab = ref('');

/** 各平台支持的消息类型列表（来自 message-types 接口） */
const messageTypesMap = ref<Record<string, MessageTypeOption[]>>({});

const selectedMessageType = ref<Record<string, string>>({});
const payloadValues = ref<Record<string, Record<string, any>>>({});

/** 字段绑定（任务 2）：键 "<platform_code>.<payload_path>" → business_key */
const fieldBindings = ref<Record<string, string>>({});

/** 机器人勾选（按平台分组） */
const selectedRobots = ref<Record<string, number[]>>({});
/** @ 配置（按平台）：admin_ids=系统员工，userids=应用群成员 */
const selectedAtUsers = ref<
  Record<string, { admin_ids: number[]; userids: string[] }>
>({});
/** 应用 API 群成员选项（按平台合并已选机器人的成员） */
const membersByPlatform = ref<
  Record<string, { userid: string; name: string }[]>
>({});
/** 应用 API 投递目标（人 / 群 / 每群 @） */
const sceneTargets = ref<SceneTargetItem[]>([]);
const targetsPanelRef = ref<{ validateBeforeSubmit: () => string } | null>(
  null,
);
/** 暂存待回显的 robot_ids */
let pendingRobotIds: number[] = [];
/** 暂存待回显的 targets */
let pendingTargets: SceneTargetItem[] = [];

/** 测试发送：每平台 phones + 可替换业务字段 vars */
const testParamsByPlatform = ref<
  Record<string, { phones: string[]; vars: Record<string, string> }>
>({});
/** 单个平台测试中的 loading（按平台独立） */
const testingPlatform = ref<Record<string, boolean>>({});

/** 41054 客户群未激活类的错误，前端给专门提示 */
const errCode41054Tip =
  '客户群群发任务权限未激活（errcode=41054），请在企微管理端开通「客户联系 → 客户群发」功能';

/**
 * Step3 临时测试成员（仅 work_wechat_app）
 * 选中后合并进本次 testSend 的 targets；未选则仍用场景已配置 targets
 */
const testWwMemberUserids = ref<string[]>([]);
const wwTestUserOptions = ref<{ label: string; value: string }[]>([]);

/** 加载企微组织成员，供 Step3 临时测试选人 */
async function loadWwTestUsers() {
  try {
    const userRes = await getOaWwUserList({
      platform_code: 'work_wechat_app',
      page: 1,
      pageSize: 500,
    });
    const userData = userRes?.data ?? userRes ?? {};
    const items = Array.isArray(userData.items) ? userData.items : [];
    wwTestUserOptions.value = items.map((u: any) => ({
      label: u.name ? `${u.name}（${u.userid}）` : String(u.userid),
      value: String(u.userid),
    }));
  } catch (e) {
    console.error(e);
    wwTestUserOptions.value = [];
  }
}

const pageTitle = computed(() =>
  props.isUpdate ? '编辑通知场景' : '新增通知场景',
);

const [Form, formApi] = useVbenForm(modalFormProps);

/** 复用机器人模块的测试结果弹窗 */
const [TestResultModalComponent, testResultModalApi] = useVbenModal({
  connectedComponent: TestResultModal,
});

/* ============================================================ *
 * 响应式断点：> 768px = 桌面双栏（左配置 + 右预览），否则单栏（底部预览）
 * ============================================================ */

const isDesktop = ref(false);

function syncBreakpoint() {
  isDesktop.value = window.innerWidth >= 1024;
}

onMounted(() => {
  syncBreakpoint();
  window.addEventListener('resize', syncBreakpoint);
});

onBeforeUnmount(() => {
  window.removeEventListener('resize', syncBreakpoint);
});

/* ============================================================ *
 * Step 1 启用平台 + 字段绑定模式
 * ============================================================ */

/**
 * form 值镜像（vben form 的 getValues 非响应式，无法被 computed 跟踪）
 * 这里在 schema 的 componentProps.onChange 里把字段同步到本地 ref
 */
const scenePlatformsMirror = ref<string[]>([]);
const fieldBindingModeMirror = ref(0);

/** 当前选中启用的平台编码数组 */
const scenePlatforms = computed<string[]>(() => scenePlatformsMirror.value);

/** 字段绑定模式（0=固定 1=可替换） */
const fieldBindingMode = computed(() => fieldBindingModeMirror.value);

/** form schema 字段变更同步到本地镜像（用于响应式触发 visiblePlatforms） */
function handleFormScenePlatformsChange(v: any) {
  scenePlatformsMirror.value = Array.isArray(v) ? v.map(String) : [];
}

function handleFormFieldBindingModeChange(v: any) {
  fieldBindingModeMirror.value = Number(v) === 1 ? 1 : 0;
}

/** 实际在 Step 2 展示的平台：Step 1 选中非空 → 取交集；为空 → 全部启用平台（兼容旧行为） */
const visiblePlatforms = computed(() => {
  if (scenePlatforms.value.length === 0) return platforms.value;
  const set = new Set(scenePlatforms.value);
  return platforms.value.filter((p) => set.has(p.platform_code));
});

/* ============================================================ *
 * 数据回显 / 初始化
 * ============================================================ */

function normalizeAtUsers(
  raw: Record<string, any> | undefined,
): Record<string, { admin_ids: number[]; userids: string[] }> {
  const result: Record<string, { admin_ids: number[]; userids: string[] }> = {};
  for (const [code, val] of Object.entries(raw || {})) {
    if (Array.isArray(val)) {
      result[code] = {
        admin_ids: val.map((id) => Number(id)).filter((id) => id > 0),
        userids: [],
      };
    } else {
      result[code] = {
        admin_ids: ((val as any)?.admin_ids || [])
          .map((id: any) => Number(id))
          .filter((id: number) => id > 0),
        userids: ((val as any)?.userids || [])
          .map((u: any) => String(u))
          .filter((u: string) => u !== ''),
      };
    }
  }
  return result;
}

function ensureAtSlot(platformCode: string) {
  if (!selectedAtUsers.value[platformCode]) {
    selectedAtUsers.value = {
      ...selectedAtUsers.value,
      [platformCode]: { admin_ids: [], userids: [] },
    };
  }
}

function adminIdsOf(platformCode: string): number[] {
  return selectedAtUsers.value[platformCode]?.admin_ids || [];
}

function useridsOf(platformCode: string): string[] {
  return selectedAtUsers.value[platformCode]?.userids || [];
}

async function validateCurrentStep(): Promise<boolean> {
  if (currentStep.value === 0) {
    const e = await formApi.validate();
    return !!e.valid;
  }
  if (currentStep.value === 1) {
    // Step 1 平台配置：至少一个平台选了消息类型；必填 payload；至少一个机器人
    const configured = visiblePlatforms.value.some(
      (p) => !!selectedMessageType.value[p.platform_code],
    );
    if (!configured) {
      message.warning('请至少为一个平台选择消息类型');
      return false;
    }
    if (!validatePayloadRequired()) return false;
    const totalRobots = Object.values(selectedRobots.value).reduce(
      (s, arr) => s + arr.length,
      0,
    );
    if (totalRobots === 0) {
      message.warning('请至少勾选一个推送机器人');
      return false;
    }
    const targetErr = targetsPanelRef.value?.validateBeforeSubmit?.() || '';
    if (targetErr) {
      message.warning(targetErr);
      return false;
    }
    return true;
  }
  return true;
}

function validatePayloadRequired(): boolean {
  for (const platform of visiblePlatforms.value) {
    const code = platform.platform_code;
    const schema = currentPayloadSchema(code);
    for (const field of schema) {
      if (!field.required) continue;
      const val = getPayloadValue(code, field.name);
      const isEmpty =
        val === undefined ||
        val === null ||
        val === '' ||
        (Array.isArray(val) && val.length === 0) ||
        (typeof val === 'object' &&
          !Array.isArray(val) &&
          Object.keys(val).length === 0);
      if (isEmpty) {
        message.warning(`${platform.name} 缺少必填字段：${field.label}`);
        return false;
      }
    }
  }
  return true;
}

async function goNext() {
  stepValidating.value = true;
  try {
    const ok = await validateCurrentStep();
    if (ok) {
      currentStep.value = Math.min(currentStep.value + 1, 2);
    }
  } finally {
    stepValidating.value = false;
  }
}

function goPrev() {
  currentStep.value = Math.max(currentStep.value - 1, 0);
}

function initFromValues(values: Record<string, any>) {
  selectedMessageType.value = {};
  payloadValues.value = {};
  selectedRobots.value = {};
  selectedAtUsers.value = {};
  sceneTargets.value = [];
  fieldBindings.value = {};
  testParamsByPlatform.value = {};
  testingPlatform.value = {};
  if (values && Object.keys(values).length > 0) {
    formApi.setValues({
      ...values,
      // 编辑回显时不带 message_config/field_bindings 进 form（避免被 form schema 当字段处理）
      message_config: undefined,
      field_bindings: undefined,
      // scene_platforms / field_binding_mode 直接 formApi.setValues，schema 中已有同名 fieldName
    });
    // 同步镜像（formApi.setValues 不会触发 onChange，所以手动同步）
    handleFormScenePlatformsChange(values.scene_platforms || []);
    handleFormFieldBindingModeChange(values.field_binding_mode || 0);
    const config = values.message_config || {};
    for (const code of Object.keys(config)) {
      selectedMessageType.value[code] = config[code].message_type || 'text';
      payloadValues.value[code] = config[code].payload || {};
    }
    // 回显字段绑定（后端返回 { "<platform>.<field>": "<key>" }）
    const fb = values.field_bindings || {};
    if (fb && typeof fb === 'object') {
      fieldBindings.value = { ...fb };
    }
    pendingRobotIds = values.robot_ids || [];
    selectedAtUsers.value = normalizeAtUsers(values.at_users);
    pendingTargets = Array.isArray(values.targets) ? values.targets : [];
    sceneTargets.value = pendingTargets.map((t: any) => ({
      target_type: Number(t.target_type) === 1 ? 1 : 2,
      platform_code: t.platform_code || 'work_wechat_app',
      userid: String(t.userid || '').trim() || undefined,
      admin_id: Number(t.admin_id || 0) || undefined,
      chat_id: Number(t.chat_id || 0) || undefined,
      at_admin_ids: (t.at_admin_ids || [])
        .map(Number)
        .filter((n: number) => n > 0),
      at_userids: (t.at_userids || [])
        .map(String)
        .filter((s: string) => s !== ''),
      sender_userid: String(t.sender_userid || ''),
    }));
  } else {
    formApi.resetForm();
    pendingRobotIds = [];
    pendingTargets = [];
  }
  currentStep.value = 0;
  loadData();
}

watch(
  () => props.values,
  (values) => {
    initFromValues(values || {});
  },
  { immediate: true },
);

async function loadData() {
  loading.value = true;
  try {
    const [platformList, typesRes, adminRes] = await Promise.all([
      getOaPlatformList(),
      getOaMessageTypes(),
      getAdminList({ page: 1, pageSize: 500 }),
    ]);

    const platformRows: any[] = Array.isArray(platformList)
      ? platformList
      : platformList?.data || [];
    platforms.value = platformRows
      .filter((p: any) => p.enabled === 1)
      .sort((a: any, b: any) => (b.sort || 0) - (a.sort || 0));
    activePlatformTab.value = platforms.value[0]?.platform_code || '';

    // 注入 scene_platforms Checkbox.Group 选项；仅「新增」默认全选，「编辑」严格用详情已选
    try {
      formApi.updateSchema([
        {
          fieldName: 'scene_platforms',
          componentProps: {
            options: platforms.value.map((p) => ({
              label: p.name,
              value: p.platform_code,
            })),
            onChange: (e: any) => {
              const val = Array.isArray(e) ? e : e?.target?.value;
              handleFormScenePlatformsChange(val);
            },
          },
        },
        {
          fieldName: 'field_binding_mode',
          componentProps: {
            onChange: (e: any) => {
              const val = e?.target?.value ?? e;
              handleFormFieldBindingModeChange(val);
            },
          },
        },
      ]);
      const platformsAll = platforms.value.map((p) => p.platform_code);
      if (props.isUpdate) {
        // 编辑：只用详情回显的已选平台，禁止兜底全选（getValues 是异步，不要直接点属性）
        const fromDetail = Array.isArray(props.values?.scene_platforms)
          ? props.values.scene_platforms.map(String)
          : [];
        const formVals = await formApi.getValues();
        const fromForm = Array.isArray(formVals?.scene_platforms)
          ? formVals.scene_platforms.map(String)
          : [];
        const selected =
          fromDetail.length > 0 ? fromDetail : fromForm;
        handleFormScenePlatformsChange(selected);
        if (selected.length > 0) {
          await formApi.setValues({
            ...formVals,
            scene_platforms: selected,
          });
        }
      } else {
        // 新增：默认全选当前启用平台
        const formVals = await formApi.getValues();
        await formApi.setValues({
          ...formVals,
          scene_platforms: platformsAll,
        });
        handleFormScenePlatformsChange(platformsAll);
      }
      // 字段绑定模式：编辑用详情值，新增保持默认；禁止用 getValues 异步结果覆盖（会竞态打回 0）
      if (props.isUpdate) {
        handleFormFieldBindingModeChange(props.values?.field_binding_mode ?? 0);
      }
    } catch (e) {
      console.warn('scene_platforms schema 注入失败', e);
    }

    const typesData = (typesRes as any)?.data ?? typesRes ?? {};
    messageTypesMap.value = typesData;

    const robotRes = await getOaRobotList({
      page: 1,
      pageSize: 500,
    });
    const robotRows: any[] = robotRes?.items || robotRes?.data || [];
    const boundIdSet = new Set(pendingRobotIds.map((id) => Number(id)));
    allRobots.value = robotRows.filter((r: any) => {
      const onPlatform = platforms.value.some(
        (p: any) => p.platform_code === r.platform_code,
      );
      if (!onPlatform) return false;
      return Number(r.status) === 1 || boundIdSet.has(Number(r.id));
    });

    const adminRows: any[] = adminRes?.items || adminRes?.data || [];
    adminList.value = adminRows.map((a: any) => ({
      id: a.id,
      name: a.nick_name || a.name || a.username || `员工${a.id}`,
      avatar: a.avatar || '',
    }));

    if (pendingRobotIds.length > 0) {
      selectedRobots.value = groupRobotsByPlatform(pendingRobotIds);
    }
    await refreshAppMembers();
    await loadWwTestUsers();

    for (const platform of platforms.value) {
      const code = platform.platform_code;
      ensureAtSlot(code);
      if (!selectedMessageType.value[code]) {
        const types = messageTypesMap.value[code] || [];
        const defaultType = types.find((t) => t.message_type === 'text');
        selectedMessageType.value[code] =
          defaultType?.message_type || types[0]?.message_type || '';
        payloadValues.value[code] = {};
      }
    }
  } catch (e) {
    console.error('加载场景数据失败', e);
    message.error('加载场景数据失败，请刷新重试');
  } finally {
    loading.value = false;
  }
}

function groupRobotsByPlatform(robotIds: number[]): Record<string, number[]> {
  const result: Record<string, number[]> = {};
  for (const id of robotIds) {
    const robot = allRobots.value.find((r) => r.id === id);
    if (robot) {
      if (!result[robot.platform_code]) {
        result[robot.platform_code] = [];
      }
      result[robot.platform_code].push(id);
    }
  }
  return result;
}

/* ============================================================ *
 * 字段绑定（任务 2）：自动生成默认 business_key
 *   text 类 → name1/name2，image 类 → img1/img2，
 *   video 类 → video1，voice 类 → voice1，file 类 → file1
 * ============================================================ */

/** 字段类型分类（按 acceptTypes / component 推断） */
function fieldCategory(field: PayloadFieldSchema): 'text' | 'image' | 'video' | 'voice' | 'file' {
  const types = fieldAcceptTypes(field);
  // xk_file.type：0=图片 1=视频 2=音频 3=excel 4=压缩包
  if (types.includes(0)) return 'image';
  if (types.includes(1)) return 'video';
  if (types.includes(2)) return 'voice';
  if (types.length > 0 && types.every((t) => t >= 3)) return 'file';
  // Textarea/Input/InputNumber/RadioGroup/Select 等文本类
  return 'text';
}

const FIELD_PREFIX: Record<string, string> = {
  text: 'name',
  image: 'img',
  video: 'video',
  voice: 'voice',
  file: 'file',
};

/** 计算某平台下某字段的默认 business_key（按字段类型在同类型中递增） */
function defaultBindingKey(platformCode: string, fieldName: string): string {
  const schema = currentPayloadSchema(platformCode);
  const counts: Record<string, number> = {
    text: 0,
    image: 0,
    video: 0,
    voice: 0,
    file: 0,
  };
  for (const f of schema) {
    const cat = fieldCategory(f);
    counts[cat] += 1;
    if (f.name === fieldName) {
      return `${FIELD_PREFIX[cat]}${counts[cat]}`;
    }
  }
  // 兜底：用 text+1
  return 'name1';
}

/** 字段绑定 path：统一 "<platform>.payload.<fieldPath>"（渠道只读 payload） */
function bindingPathKey(platformCode: string, fieldPath: string): string {
  const fp = String(fieldPath || '').replace(/^\.?payload\./, '');
  return `${platformCode}.payload.${fp}`;
}

/**
 * 展开某平台可绑定字段（普通 schema + NewsList/LinkList 按条展开子字段）
 * fieldPath 相对 payload，如 content / articles.0.title / links.1.title
 */
function expandBindingFields(
  platformCode: string,
): Array<{ fieldPath: string; label: string }> {
  const schema = currentPayloadSchema(platformCode);
  const payload = payloadValues.value[platformCode] || {};
  const list: Array<{ fieldPath: string; label: string }> = [];
  for (const field of schema) {
    const comp = String(field.component || '');
    if (comp === 'NewsList') {
      const articles = Array.isArray(payload[field.name])
        ? payload[field.name]
        : [];
      const count = Math.max(articles.length, 1);
      const subKeys: Array<{ key: string; label: string }> = [
        { key: 'title', label: '标题' },
        { key: 'description', label: '描述' },
        { key: 'url', label: '链接' },
        { key: 'picurl', label: '封面' },
      ];
      for (let i = 0; i < count; i++) {
        for (const sub of subKeys) {
          list.push({
            fieldPath: `${field.name}.${i}.${sub.key}`,
            label: `${field.label || '图文'}#${i + 1}.${sub.label}`,
          });
        }
      }
      continue;
    }
    if (comp === 'LinkList') {
      const links = Array.isArray(payload[field.name]) ? payload[field.name] : [];
      const count = Math.max(links.length, 1);
      const subKeys: Array<{ key: string; label: string }> = [
        { key: 'title', label: '标题' },
        { key: 'messageURL', label: '链接' },
        { key: 'picURL', label: '封面' },
      ];
      for (let i = 0; i < count; i++) {
        for (const sub of subKeys) {
          list.push({
            fieldPath: `${field.name}.${i}.${sub.key}`,
            label: `${field.label || '链接'}#${i + 1}.${sub.label}`,
          });
        }
      }
      continue;
    }
    // 列表整块不单独绑；普通输入/图片等一对一
    if (['ButtonList', 'PostEditor', 'TemplateCardEditor'].includes(comp)) {
      continue;
    }
    list.push({
      fieldPath: field.name,
      label: String(field.label || field.name),
    });
  }
  return list;
}

/** 计算展开字段的默认 business_key（按展开顺序 name1/name2…） */
function defaultBindingKeyForPath(
  platformCode: string,
  fieldPath: string,
): string {
  const fields = expandBindingFields(platformCode);
  let idx = 0;
  for (const f of fields) {
    idx += 1;
    if (f.fieldPath === fieldPath) {
      return `name${idx}`;
    }
  }
  return 'name1';
}

/** 读取某字段的 business_key（兼容旧路径 platform.field） */
function getBindingKey(platformCode: string, fieldPath: string): string {
  const path = bindingPathKey(platformCode, fieldPath);
  if (fieldBindings.value[path] !== undefined) {
    return fieldBindings.value[path];
  }
  // 兼容旧数据：work_wechat.content（无 payload. 段）
  const legacy = `${platformCode}.${fieldPath}`;
  if (fieldBindings.value[legacy] !== undefined) {
    return fieldBindings.value[legacy];
  }
  return defaultBindingKeyForPath(platformCode, fieldPath);
}

function setBindingKey(platformCode: string, fieldPath: string, val: string) {
  const path = bindingPathKey(platformCode, fieldPath);
  const next = { ...fieldBindings.value, [path]: val };
  // 清掉同字段旧路径，避免双写歧义
  const legacy = `${platformCode}.${fieldPath}`;
  if (legacy in next) {
    delete next[legacy];
  }
  fieldBindings.value = next;
}

/**
 * Step3 可替换业务字段列表（与 expandBindingFields 对齐）
 */
function replaceableFieldsOf(
  platformCode: string,
): Array<{ businessKey: string; payloadPath: string; label: string }> {
  if (fieldBindingMode.value !== 1) return [];
  const seen = new Set<string>();
  const list: Array<{
    businessKey: string;
    payloadPath: string;
    label: string;
  }> = [];
  for (const f of expandBindingFields(platformCode)) {
    const path = bindingPathKey(platformCode, f.fieldPath);
    const businessKey = String(getBindingKey(platformCode, f.fieldPath) || '').trim();
    if (!businessKey || seen.has(businessKey)) continue;
    seen.add(businessKey);
    list.push({
      businessKey,
      payloadPath: path,
      label: f.label,
    });
  }
  return list;
}

/**
 * 打包入库/测试用的 field_bindings：补齐展开字段默认 key
 */
function resolveFieldBindingsMap(): Record<string, string> {
  if (fieldBindingMode.value !== 1) return {};
  const out: Record<string, string> = { ...fieldBindings.value };
  for (const p of visiblePlatforms.value) {
    const code = p.platform_code;
    for (const f of expandBindingFields(code)) {
      const path = bindingPathKey(code, f.fieldPath);
      if (out[path] === undefined) {
        // 迁移旧路径
        const legacy = `${code}.${f.fieldPath}`;
        out[path] =
          out[legacy] !== undefined
            ? out[legacy]
            : defaultBindingKeyForPath(code, f.fieldPath);
        if (legacy in out) {
          delete out[legacy];
        }
      }
    }
  }
  const cleaned: Record<string, string> = {};
  for (const [k, v] of Object.entries(out)) {
    const key = String(v || '').trim();
    if (key !== '') cleaned[k] = key;
  }
  return cleaned;
}

/** 读取/写入某平台测试 vars */
function getTestVar(platformCode: string, businessKey: string): string {
  return testParamsByPlatform.value[platformCode]?.vars?.[businessKey] || '';
}

function setTestVar(platformCode: string, businessKey: string, val: string) {
  const prev = testParamsByPlatform.value[platformCode] || {
    phones: [],
    vars: {},
  };
  testParamsByPlatform.value = {
    ...testParamsByPlatform.value,
    [platformCode]: {
      phones: prev.phones || [],
      vars: { ...(prev.vars || {}), [businessKey]: val },
    },
  };
}

/* ============================================================ *
 * 草稿组装（保存 + 测试发送共用）
 * ============================================================ */

async function assembleSceneDraft(platformCodes?: string[]) {
  const baseValues = await formApi.getValues();
  const message_config: Record<string, any> = {};
  // 仅组装传入 platformCodes 列表（测试用），未传则全部
  const targetPlatforms =
    platformCodes && platformCodes.length > 0
      ? visiblePlatforms.value.filter((p) =>
          platformCodes.includes(p.platform_code),
        )
      : visiblePlatforms.value;
  for (const platform of targetPlatforms) {
    const code = platform.platform_code;
    const messageType = selectedMessageType.value[code];
    if (!messageType) continue;
    message_config[code] = {
      message_type: messageType,
      payload: payloadValues.value[code] || {},
    };
  }
  const robot_ids: number[] = [];
  const filterSet = platformCodes ? new Set(platformCodes) : null;
  for (const platformCode of Object.keys(selectedRobots.value)) {
    if (filterSet && !filterSet.has(platformCode)) continue;
    robot_ids.push(...(selectedRobots.value[platformCode] || []));
  }
  const at_users: Record<
    string,
    { admin_ids: number[]; userids: string[] }
  > = {};
  for (const platformCode of Object.keys(selectedAtUsers.value)) {
    if (filterSet && !filterSet.has(platformCode)) continue;
    if (isWorkWechatApp(platformCode)) continue;
    const slot = selectedAtUsers.value[platformCode] || {
      admin_ids: [],
      userids: [],
    };
    if (slot.admin_ids.length > 0 || slot.userids.length > 0) {
      at_users[platformCode] = {
        admin_ids: slot.admin_ids,
        userids: slot.userids,
      };
    }
  }
  // 字段绑定：仅当 mode=1 时打包（补齐默认 business_key）
  const fb =
    fieldBindingMode.value === 1 ? resolveFieldBindingsMap() : undefined;
  return {
    ...baseValues,
    message_config,
    robot_ids,
    at_users,
    targets: sceneTargets.value,
    field_binding_mode: fieldBindingMode.value,
    field_bindings: fb,
  };
}

function mapSceneResultsToModalItems(rows: any[]): TestResultItem[] {
  return rows.map((r) => ({
    message_type: String(r.message_type || r.platform_code || 'text'),
    name: String(
      r.name ||
        `${r.robot_name || ''}${r.target_label ? ` → ${r.target_label}` : ''}`,
    ),
    success: !!r.success,
    message: String(r.message || ''),
    request: r.request,
    response: r.response ?? {},
    cost_ms: Number(r.cost_ms || 0),
  }));
}

async function handleSave() {
  const e = await formApi.validate();
  if (!e.valid) return;
  const targetErr = targetsPanelRef.value?.validateBeforeSubmit?.() || '';
  if (targetErr) {
    message.warning(targetErr);
    return;
  }
  saving.value = true;
  try {
    const payload = await assembleSceneDraft();
    const submitApi = props.isUpdate ? updateOaScene : createOaScene;
    await submitApi(payload);
    message.success('保存成功');
    emit('saved');
    emit('back');
  } finally {
    saving.value = false;
  }
}

/**
 * 分平台独立测试发送（任务 6）
 * @param platformCode 单平台测试；不传则全部启用平台
 */
async function handleTestSend(platformCode?: string) {
  const codes = platformCode ? [platformCode] : [];
  const draft = await assembleSceneDraft(codes);
  if (!draft.robot_ids || draft.robot_ids.length === 0) {
    message.warning('请先勾选至少一个推送机器人');
    return;
  }
  if (!draft.message_config || Object.keys(draft.message_config).length === 0) {
    message.warning('请先配置该平台的消息类型');
    return;
  }
  const targetErr = targetsPanelRef.value?.validateBeforeSubmit?.() || '';
  if (targetErr) {
    message.warning(targetErr);
    return;
  }
  // 取该平台测试参数：可替换业务字段 vars + phones
  // 一键测全部时合并各平台 vars（business_key 场景内通常唯一）
  let mergedVars: Record<string, string> = {};
  if (platformCode) {
    const tp = testParamsByPlatform.value[platformCode] || {
      phones: [],
      vars: {},
    };
    mergedVars = { ...(tp.vars || {}) };
  } else {
    for (const p of visiblePlatforms.value) {
      const slot = testParamsByPlatform.value[p.platform_code];
      if (slot?.vars) {
        mergedVars = { ...mergedVars, ...slot.vars };
      }
    }
  }
  const tp = testParamsByPlatform.value[platformCode || ''] || {
    phones: [],
    vars: {},
  };
  const test_params: any = {
    vars: mergedVars,
  };
  // @ 手机号（企微/钉钉用）：把 phones 合并到 atMobiles
  const phones = Array.isArray(tp.phones) ? tp.phones : [];
  if (phones.length > 0) {
    const atUsers = { ...(draft.at_users || {}) };
    if (platformCode) {
      const exist = atUsers[platformCode] || { admin_ids: [], userids: [] };
      atUsers[platformCode] = {
        admin_ids: exist.admin_ids,
        userids: exist.userids,
        atMobiles: phones,
      } as any;
    }
    draft.at_users = atUsers;
  }
  // work_wechat_app：临时测试成员合并进 targets（追加个人目标，不覆盖场景已配群/人）
  let testTargets = Array.isArray(draft.targets) ? [...draft.targets] : [];
  if (
    (!platformCode || isWorkWechatApp(platformCode)) &&
    testWwMemberUserids.value.length > 0
  ) {
    const existUserids = new Set(
      testTargets
        .filter((t: any) => Number(t.target_type) === 1)
        .map((t: any) => String(t.userid || '').trim())
        .filter(Boolean),
    );
    for (const uid of testWwMemberUserids.value) {
      const userid = String(uid || '').trim();
      if (!userid || existUserids.has(userid)) continue;
      testTargets.push({
        target_type: 1,
        platform_code: 'work_wechat_app',
        userid,
        admin_id: 0,
      });
      existUserids.add(userid);
    }
  }
  // loading 标记
  if (platformCode) {
    testingPlatform.value = {
      ...testingPlatform.value,
      [platformCode]: true,
    };
  } else {
    testing.value = true;
  }
  try {
    const result = await testSendOaScene({
      id: Number(draft.id || 0) || undefined,
      scene_code: String(draft.scene_code || ''),
      scene_name: String(draft.scene_name || ''),
      message_config: draft.message_config,
      robot_ids: draft.robot_ids,
      at_users: draft.at_users,
      targets: testTargets,
      platform_codes: codes,
      field_binding_mode: draft.field_binding_mode,
      field_bindings: draft.field_bindings,
      test_params,
    });
    const rows = Array.isArray(result?.results) ? result.results : [];
    const total = result?.total ?? rows.length;
    const succ =
      result?.success_count ?? rows.filter((r: any) => r.success).length;
    const fail = total - succ;
    const summary = `共 ${total} 条，成功 ${succ}，失败 ${fail}`;
    if (fail === 0) {
      message.success(summary);
    } else if (succ === 0) {
      message.error(summary);
    } else {
      message.warning(summary);
    }
    // 41054 客户群未激活专门提示
    const has41054 = rows.some(
      (r: any) =>
        String(r.message || '').includes('41054') ||
        JSON.stringify(r.response || {}).includes('41054'),
    );
    if (has41054) {
      message.error(errCode41054Tip, 8);
    }
    testResultModalApi.setData({
      results: mapSceneResultsToModalItems(rows),
      summary,
      robotName: String(draft.scene_name || ''),
      platformCode: platformCode || 'scene',
      testContent:
        Object.values(test_params.vars || {})
          .filter((v: any) => String(v || '').trim() !== '')
          .join(' / ') || '（使用 Step2 原配置）',
      sentAt: Math.floor(Date.now() / 1000),
    });
    testResultModalApi.open();
  } catch (e: any) {
    message.error(e?.message || '测试发送失败');
  } finally {
    if (platformCode) {
      testingPlatform.value = {
        ...testingPlatform.value,
        [platformCode]: false,
      };
    } else {
      testing.value = false;
    }
  }
}

function robotsOfPlatform(platformCode: string): any[] {
  return allRobots.value.filter((r: any) => r.platform_code === platformCode);
}

function checkedRobotsOfPlatform(platformCode: string): number[] {
  return selectedRobots.value[platformCode] || [];
}

function toggleAllRobots(platformCode: string, e: any) {
  const checked = (e?.target as HTMLInputElement)?.checked;
  const robotIds = robotsOfPlatform(platformCode).map((r) => r.id);
  selectedRobots.value = {
    ...selectedRobots.value,
    [platformCode]: checked ? robotIds : [],
  };
  if (isWorkWechatApp(platformCode)) {
    refreshAppMembers();
  }
}

function toggleAllAtUsers(platformCode: string, e: any) {
  const checked = (e?.target as HTMLInputElement)?.checked;
  const adminIds = adminList.value.map((a) => a.id);
  ensureAtSlot(platformCode);
  selectedAtUsers.value = {
    ...selectedAtUsers.value,
    [platformCode]: {
      admin_ids: checked ? adminIds : [],
      userids: useridsOf(platformCode),
    },
  };
}

/**
 * 按已选应用 API 机器人合并拉取群成员（userid 去重）
 */
async function refreshAppMembers() {
  const appCodes = platforms.value
    .map((p) => p.platform_code)
    .filter((code) => isWorkWechatApp(code));
  for (const code of appCodes) {
    const robotIds = selectedRobots.value[code] || [];
    if (robotIds.length === 0) {
      membersByPlatform.value = { ...membersByPlatform.value, [code]: [] };
      continue;
    }
    try {
      const list = await getOaRobotChatMembers({ robot_ids: robotIds });
      const rows: any[] = Array.isArray(list) ? list : list?.data || [];
      membersByPlatform.value = {
        ...membersByPlatform.value,
        [code]: rows.map((m) => ({
          userid: String(m.userid),
          name: String(m.name || ''),
        })),
      };
    } catch (e) {
      console.error('加载应用群成员失败', e);
      membersByPlatform.value = { ...membersByPlatform.value, [code]: [] };
    }
  }
}

/**
 * 解析字段 acceptTypes（兼容缺省：图片字段默认 [0]）
 * xk_file.type 实际值：0=图片 1=视频 2=音频 3=excel 4=压缩包
 */
function fieldAcceptTypes(field: PayloadFieldSchema): number[] {
  const raw = field.props?.acceptTypes;
  if (Array.isArray(raw) && raw.length > 0) {
    return raw.map(Number).filter((n) => !Number.isNaN(n));
  }
  return [0];
}

function isImageMediaField(field: PayloadFieldSchema) {
  const types = fieldAcceptTypes(field);
  // 仅图片类型（xk_file.type=0）走图片上传器；视频/语音/文件走通用文件上传器
  return types.length === 1 && types[0] === 0;
}

function imageUrlsOf(platformCode: string, fieldName: string): string[] {
  const url = getPayloadValue(platformCode, fieldName);
  return url ? [String(url)] : [];
}

function setImageUrls(
  platformCode: string,
  fieldName: string,
  urls: string[],
) {
  setPayloadValue(platformCode, fieldName, urls[0] || '');
}

function fileInputAccept(field: PayloadFieldSchema) {
  const types = fieldAcceptTypes(field);
  if (types.includes(3)) {
    return 'audio/*,.amr,.mp3,.wav';
  }
  return '.pdf,.doc,.docx,.zip,.rar,.txt,application/pdf,*/*';
}

function currentPayloadSchema(platformCode: string): PayloadFieldSchema[] {
  const messageType = selectedMessageType.value[platformCode];
  if (!messageType) return [];
  const types = messageTypesMap.value[platformCode] || [];
  const typeInfo = types.find((t) => t.message_type === messageType);
  if (!typeInfo?.payload_schema) return [];
  try {
    return JSON.parse(typeInfo.payload_schema);
  } catch {
    return [];
  }
}

function getPayloadValue(platformCode: string, fieldName: string): any {
  return payloadValues.value[platformCode]?.[fieldName];
}

function setPayloadValue(platformCode: string, fieldName: string, value: any) {
  const current = payloadValues.value[platformCode] || {};
  payloadValues.value = {
    ...payloadValues.value,
    [platformCode]: { ...current, [fieldName]: value },
  };
}

function handleMessageTypeChange(platformCode: string, messageType: string) {
  selectedMessageType.value = {
    ...selectedMessageType.value,
    [platformCode]: messageType,
  };
  payloadValues.value = {
    ...payloadValues.value,
    [platformCode]: {},
  };
}

const isEmpty = computed(() => platforms.value.length === 0);

/* ============================================================ *
 * 消息预览：把当前 payload 拼成 MsgPreview 需要的格式
 * ============================================================ */

function previewPayloadOf(platformCode: string): Record<string, any> {
  const messageType = selectedMessageType.value[platformCode];
  const payload = { ...(payloadValues.value[platformCode] || {}) };
  // template_card 类型：把 payload.template_card 暴露给 MsgPreview
  if (messageType === 'template_card' && payload.template_card) {
    return payload;
  }
  // 可替换模式：用 Step3 vars 按绑定路径覆盖预览（仅顶层字段名能直接映射时）
  const vars = testParamsByPlatform.value[platformCode]?.vars || {};
  const merged = { ...payload };
  if (fieldBindingMode.value === 1) {
    for (const item of replaceableFieldsOf(platformCode)) {
      const val = vars[item.businessKey];
      if (val === undefined || val === '') continue;
      // path = "platform.fieldName" → 取 fieldName 写入 payload 预览
      const parts = item.payloadPath.split('.');
      const fieldName = parts.length >= 2 ? parts[parts.length - 1] : '';
      if (fieldName) {
        merged[fieldName] = val;
      }
    }
  }
  return merged;
}

/** 当前预览的平台（默认跟随 activePlatformTab） */
const previewPlatformCode = computed(() => activePlatformTab.value);

const previewMessageType = computed(
  () => selectedMessageType.value[previewPlatformCode.value] || 'text',
);

/* ============================================================ *
 * 机器人 / @ 人 / Tab 切换清理
 * ============================================================ */

watch(
  () => visiblePlatforms.value,
  (list) => {
    const validCodes = new Set(list.map((p) => p.platform_code));
    const newRobots: Record<string, number[]> = {};
    const newAtUsers: Record<
      string,
      { admin_ids: number[]; userids: string[] }
    > = {};
    for (const code of validCodes) {
      newRobots[code] = selectedRobots.value[code] || [];
      newAtUsers[code] = selectedAtUsers.value[code] || {
        admin_ids: [],
        userids: [],
      };
    }
    selectedRobots.value = newRobots;
    selectedAtUsers.value = newAtUsers;
    // 如果当前 Tab 平台不在 visiblePlatforms 中，切换到第一个
    if (!validCodes.has(activePlatformTab.value)) {
      activePlatformTab.value = list[0]?.platform_code || '';
    }
  },
);
</script>

<template>
  <Page :title="pageTitle" auto-content-height>
    <template #extra>
      <div class="flex items-center gap-2">
        <Button @click="emit('back')">返回</Button>
        <Button v-if="currentStep > 0" @click="goPrev">上一步</Button>
        <Button
          v-if="currentStep < 2"
          :loading="stepValidating"
          type="primary"
          @click="goNext"
        >
          下一步
        </Button>
        <Button
          v-if="currentStep === 2"
          type="primary"
          :loading="saving"
          @click="handleSave"
        >
          保存
        </Button>
      </div>
    </template>

    <Spin :spinning="loading">
      <!-- 分步导航（3 步） -->
      <Steps :current="currentStep" class="mb-4" size="small">
        <Step
          v-for="title in STEP_TITLES"
          :key="title"
          :title="title"
        />
      </Steps>

      <!-- ============== Step 1：基础信息 ============== -->
      <div v-show="currentStep === 0">
        <Card :bordered="false" class="scene-card">
          <Form />
        </Card>
      </div>

      <!-- ============== Step 2：平台配置（合并原 2+3 步） ============== -->
      <div v-show="currentStep === 1">
        <Alert
          v-if="scenePlatforms.length === 0 && platforms.length > 0"
          type="info"
          show-icon
          message="未指定启用平台，默认展示所有已启用平台"
          description="可在「基础信息」步骤中精确选择本场景要启用的平台"
          class="mb-3"
        />
        <div v-if="isEmpty" class="empty-hint py-4 text-center">
          暂无启用的平台，请先在「系统配置 → OA 通知」中启用平台
        </div>

        <!-- 桌面：左右双栏（左配置 + 右预览 sticky）；移动：单栏（底部预览） -->
        <div :class="['step2-layout', { 'step2-layout--mobile': !isDesktop }]">
          <!-- 左侧配置区（含顶部 sticky 平台 Tab） -->
          <div class="step2-config">
            <div class="platform-tabs-wrapper">
              <Tabs v-model:active-key="activePlatformTab" size="small">
                <Tabs.TabPane
                  v-for="platform in visiblePlatforms"
                  :key="platform.platform_code"
                >
                  <template #tab>
                    <span class="platform-tab-label">
                      <VbenIcon
                        v-if="platform.icon"
                        :icon="platform.icon"
                        class="platform-icon"
                      />
                      {{ platform.name }}
                    </span>
                  </template>

                  <div class="oa-scene-tab">
                    <!-- 消息类型 -->
                    <Card :bordered="false" size="small" class="sub-card">
                      <div class="section-title">消息类型</div>
                      <RadioGroup
                        :value="selectedMessageType[platform.platform_code]"
                        @update:value="
                          (val) =>
                            handleMessageTypeChange(
                              platform.platform_code,
                              val,
                            )
                        "
                      >
                        <Radio
                          v-for="opt in messageTypesMap[
                            platform.platform_code
                          ] || []"
                          :key="opt.message_type"
                          :value="opt.message_type"
                        >
                          {{ opt.name }}
                        </Radio>
                      </RadioGroup>
                    </Card>

                    <!-- 消息内容（payload 表单，复用 PayloadSchemaForm） -->
                    <Card :bordered="false" size="small" class="sub-card">
                      <div class="section-title">消息内容</div>
                      <div class="payload-form">
                        <PayloadSchemaForm
                          :schema="currentPayloadSchema(platform.platform_code)"
                          :model-value="
                            payloadValues[platform.platform_code] || {}
                          "
                          @update:model-value="
                            (v) => {
                              payloadValues = {
                                ...payloadValues,
                                [platform.platform_code]: v || {},
                              };
                            }
                          "
                        />

                        <div
                          v-if="
                            fieldBindingMode === 1 &&
                            expandBindingFields(platform.platform_code).length >
                              0
                          "
                          class="binding-panel"
                        >
                          <div class="section-title">业务字段绑定</div>
                          <div class="mb-2 text-xs text-gray-500">
                            dispatch 时用业务 key 覆盖对应 payload 路径；图文按每条卡片的标题/描述/链接/封面分别绑定。投递对象与机器人请在下方「推送机器人 / 投递目标」配置。
                          </div>
                          <div
                            v-for="f in expandBindingFields(
                              platform.platform_code,
                            )"
                            :key="f.fieldPath"
                            class="binding-row"
                          >
                            <label class="binding-label">
                              {{ f.label }}
                              <span class="binding-hint">
                                （{{ f.fieldPath }}）
                              </span>
                            </label>
                            <Input
                              :value="
                                getBindingKey(
                                  platform.platform_code,
                                  f.fieldPath,
                                )
                              "
                              placeholder="自动生成，可手改"
                              size="small"
                              allow-clear
                              @update:value="
                                (val) =>
                                  setBindingKey(
                                    platform.platform_code,
                                    f.fieldPath,
                                    val,
                                  )
                              "
                            />
                          </div>
                        </div>
                      </div>
                    </Card>

                    <!-- 推送机器人（合并自原 Step 3） -->
                    <Card :bordered="false" size="small" class="sub-card">
                      <div class="section-title">
                        推送机器人
                        <Checkbox
                          class="float-right"
                          :checked="
                            robotsOfPlatform(platform.platform_code).length > 0 &&
                            checkedRobotsOfPlatform(platform.platform_code)
                              .length ===
                              robotsOfPlatform(platform.platform_code).length
                          "
                          @change="
                            toggleAllRobots(platform.platform_code, $event)
                          "
                        >
                          全选
                        </Checkbox>
                      </div>
                      <div
                        v-if="robotsOfPlatform(platform.platform_code).length === 0"
                        class="empty-hint"
                      >
                        该平台暂无启用的机器人
                      </div>
                      <UserTagSelect
                        v-else
                        :model-value="
                          checkedRobotsOfPlatform(platform.platform_code)
                        "
                        :options="
                          robotsOfPlatform(platform.platform_code).map((r) => ({
                            value: r.id,
                            label: `${r.name}${Number(r.status) === 0 ? '（已禁用）' : ''}`,
                          }))
                        "
                        placeholder="选择该平台下的推送机器人（可多选）"
                        @update:model-value="
                          (v: (string | number)[]) => {
                            selectedRobots = {
                              ...selectedRobots,
                              [platform.platform_code]: v.map((n) => Number(n)),
                            };
                            if (isWorkWechatApp(platform.platform_code)) {
                              refreshAppMembers();
                            }
                          }
                        "
                      />
                    </Card>

                    <!-- 投递目标 / @员工（合并自原 Step 3） -->
                    <Card
                      v-if="isWorkWechatApp(platform.platform_code)"
                      :bordered="false"
                      size="small"
                      class="sub-card"
                    >
                      <div class="section-title">投递目标（企微应用 API）</div>
                      <SceneTargetsPanel
                        ref="targetsPanelRef"
                        v-model="sceneTargets"
                        :admin-list="adminList"
                        :platform-code="platform.platform_code"
                      />
                    </Card>

                    <Card
                      v-else
                      :bordered="false"
                      size="small"
                      class="sub-card"
                    >
                      <div class="section-title">
                        @ 系统员工（按手机号）
                        <Checkbox
                          class="float-right"
                          :checked="
                            adminList.length > 0 &&
                            adminIdsOf(platform.platform_code).length ===
                              adminList.length
                          "
                          @change="
                            toggleAllAtUsers(platform.platform_code, $event)
                          "
                        >
                          全选
                        </Checkbox>
                      </div>
                      <div v-if="adminList.length === 0" class="empty-hint">
                        暂无员工可选
                      </div>
                      <UserTagSelect
                        v-else
                        :model-value="adminIdsOf(platform.platform_code)"
                        :options="
                          adminList.map((a) => ({
                            value: a.id,
                            label: a.name,
                            avatar: a.avatar || '',
                          }))
                        "
                        placeholder="选择要 @ 的员工（可多选）"
                        @update:model-value="
                          (v: (string | number)[]) => {
                            selectedAtUsers = {
                              ...selectedAtUsers,
                              [platform.platform_code]: {
                                admin_ids: v.map((n) => Number(n)),
                                userids: useridsOf(platform.platform_code),
                              },
                            };
                          }
                        "
                      />
                    </Card>
                  </div>
                </Tabs.TabPane>
              </Tabs>
            </div>
          </div>

          <!-- 右侧预览区（桌面 sticky / 移动底部） -->
          <div class="step2-preview">
            <div class="preview-inner">
              <div class="preview-title">实时消息预览</div>
              <MsgPreview
                v-if="previewPlatformCode"
                :platform-code="previewPlatformCode"
                :message-type="previewMessageType"
                :payload="previewPayloadOf(previewPlatformCode)"
              />
              <Empty
                v-else
                description="请先在 Step 1 启用至少一个平台"
                :image-style="{ height: '40px' }"
              />
            </div>
          </div>
        </div>
      </div>

      <!-- ============== Step 3：测试发送（按平台独立测试） ============== -->
      <div v-show="currentStep === 2">
        <Alert
          type="info"
          show-icon
          class="mb-3"
          message="分平台独立测试"
          description="每个平台一行，可单独测试。可替换模式下填写业务字段测试值；固定模式直接用 Step2 配置。@ 手机号会自动记忆"
        />
        <Alert
          v-if="false"
          type="warning"
          show-icon
          class="mb-3"
          :message="errCode41054Tip"
        />

        <div v-if="visiblePlatforms.length === 0" class="empty-hint">
          请先在 Step 1 启用至少一个平台
        </div>

        <Card
          v-for="platform in visiblePlatforms"
          :key="platform.platform_code"
          :bordered="false"
          size="small"
          class="test-row-card"
        >
          <div class="test-row">
            <div class="test-row-head">
              <span class="platform-name">
                <VbenIcon
                  v-if="platform.icon"
                  :icon="platform.icon"
                  class="platform-icon"
                />
                {{ platform.name }}
              </span>
              <Tag v-if="isWorkWechatApp(platform.platform_code)" color="orange">
                企微应用 API
              </Tag>
              <Tag v-else color="blue">群机器人</Tag>
            </div>

            <!-- 企微应用：临时测试成员（不改场景配置，仅本次发送追加个人目标） -->
            <div
              v-if="isWorkWechatApp(platform.platform_code)"
              class="test-row-phones"
            >
              <div class="mb-1 text-xs">临时测试成员</div>
              <Select
                mode="multiple"
                show-search
                allow-clear
                class="w-full"
                placeholder="可选：从组织架构选成员单独测个人消息"
                :value="testWwMemberUserids"
                :options="wwTestUserOptions"
                :filter-option="
                  (input: string, option: any) =>
                    String(option?.label || '')
                      .toLowerCase()
                      .includes(input.toLowerCase())
                "
                :max-tag-count="4"
                @change="(v: string[]) => (testWwMemberUserids = v || [])"
              />
              <div class="mt-1 text-xs text-gray-400">
                选中后追加到本次测试的个人目标；未选则仍用 Step2 已配置的投递目标
              </div>
              <div
                v-if="wwTestUserOptions.length === 0"
                class="mt-1 text-xs text-orange-500"
              >
                请先在 OA 群聊 → 员工 Tab 同步组织架构
              </div>
            </div>

            <!-- 飞书 webhook 不支持手机号 @，隐藏 phones 字段 -->
            <div
              v-if="!isWorkWechatApp(platform.platform_code)"
              class="test-row-phones"
            >
              <div class="mb-1 text-xs">@ 手机号</div>
              <PhoneMemoryPicker
                v-if="platform.platform_code !== 'feishu'"
                :platform-code="platform.platform_code"
                :model-value="
                  (testParamsByPlatform[platform.platform_code] || {}).phones ||
                  []
                "
                placeholder="输入手机号回车添加（@ 用）"
                @update:model-value="
                  (v: string[]) => {
                    const prev = testParamsByPlatform[platform.platform_code] || {
                      phones: [],
                      vars: {},
                    };
                    testParamsByPlatform = {
                      ...testParamsByPlatform,
                      [platform.platform_code]: {
                        phones: v,
                        vars: { ...(prev.vars || {}) },
                      },
                    };
                  }
                "
              />
              <div v-else class="text-xs text-gray-400">
                飞书 webhook 不支持手机号 @，仅 @all 生效
              </div>
            </div>

            <!-- 可替换模式：按 field_bindings 动态业务字段；固定模式用 Step2 配置 -->
            <div class="test-row-params">
              <template v-if="fieldBindingMode === 1">
                <div class="mb-1 text-xs text-gray-500">
                  可替换业务字段（对应 Step2 绑定；留空则用 Step2 payload 原值）
                </div>
                <div
                  v-if="replaceableFieldsOf(platform.platform_code).length === 0"
                  class="text-xs text-orange-500"
                >
                  该平台未配置可替换字段，请先在 Step2 为消息字段填写业务字段名
                </div>
                <div v-else class="param-grid">
                  <div
                    v-for="f in replaceableFieldsOf(platform.platform_code)"
                    :key="f.businessKey"
                    class="param-cell"
                  >
                    <label>
                      {{ f.label }}
                      <span class="text-gray-400">（{{ f.businessKey }}）</span>
                    </label>
                    <Input
                      :value="getTestVar(platform.platform_code, f.businessKey)"
                      :placeholder="`测试值，对应 ${f.businessKey}`"
                      size="small"
                      allow-clear
                      @update:value="
                        (v) =>
                          setTestVar(
                            platform.platform_code,
                            f.businessKey,
                            v || '',
                          )
                      "
                    />
                  </div>
                </div>
              </template>
              <div v-else class="text-xs text-gray-500">
                当前为固定内容模式，测试将直接使用 Step2 已配置的消息内容
              </div>
            </div>

            <div class="test-row-footer">
              <Button
                type="primary"
                size="small"
                :loading="!!testingPlatform[platform.platform_code]"
                :disabled="
                  !selectedMessageType[platform.platform_code] ||
                  (checkedRobotsOfPlatform(platform.platform_code).length === 0 &&
                    !isWorkWechatApp(platform.platform_code))
                "
                @click="handleTestSend(platform.platform_code)"
              >
                测试本平台
              </Button>
              <span class="text-xs text-gray-400">
                {{
                  isWorkWechatApp(platform.platform_code)
                    ? testWwMemberUserids.length > 0
                      ? `将追加 ${testWwMemberUserids.length} 个临时成员 + 场景投递目标`
                      : '仅发送给已配置的投递目标（可上方选临时成员）'
                    : `将发送给 ${checkedRobotsOfPlatform(platform.platform_code).length} 个机器人`
                }}
              </span>
            </div>
          </div>
        </Card>

        <div class="mt-4 text-center">
          <Button :loading="testing" @click="handleTestSend()">
            一键测试全部平台
          </Button>
        </div>
      </div>
    </Spin>

    <TestResultModalComponent />
  </Page>
</template>

<style scoped>
/* ============ Step 2 双栏布局 ============ */
.step2-layout {
  display: grid;
  grid-template-columns: 1fr 380px;
  gap: 16px;
  align-items: start;
}

.step2-layout--mobile {
  grid-template-columns: 1fr;
}

.step2-config {
  min-width: 0;
}

.platform-tabs-wrapper {
  position: sticky;
  top: 0;
  z-index: 10;
}

.step2-preview {
  position: sticky;
  top: 16px;
}

.step2-layout--mobile .step2-preview {
  position: static;
  margin-top: 16px;
}

.preview-inner {
  border-radius: 8px;
  padding: 12px;
  border: 1px solid var(--ant-color-border-secondary, #f0f0f0);
}

.preview-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--ant-color-text, #1d2129);
  margin-bottom: 8px;
  text-align: center;
}

/* ============ Tab 内容容器 ============ */
.oa-scene-tab {
  padding: 12px 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.sub-card {
  border-radius: 8px;
}

.sub-card :deep(.ant-card-body) {
  padding: 12px;
}

.section-title {
  margin-bottom: 12px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ant-color-text, #1d2129);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.section-title .float-right {
  float: right;
  font-weight: 400;
}

.empty-hint {
  color: var(--ant-color-text-tertiary, #86909c);
}

/* ============ Payload 表单 ============ */
.payload-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.payload-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.payload-label {
  font-size: 12px;
  color: var(--ant-color-text-secondary, #4e5969);
}

.payload-label .required {
  color: var(--ant-color-error, #f53f3f);
  margin-left: 2px;
}

.binding-row {
  margin-top: -4px;
  margin-left: 12px;
  padding-left: 8px;
  border-left: 2px solid var(--ant-color-border-secondary, #e8e8e8);
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.binding-panel {
  margin-top: 12px;
  padding-top: 8px;
  border-top: 1px dashed var(--ant-color-border-secondary, #f0f0f0);
}

.binding-row {
  margin-bottom: 8px;
}

.binding-label {
  display: block;
  margin-bottom: 4px;
  font-size: 12px;
  font-weight: 500;
  color: var(--ant-color-text-secondary, #666);
}

.binding-hint {
  margin-left: 4px;
  color: var(--ant-color-text-tertiary, #bbb);
  font-weight: 400;
  font-size: 11px;
}

/* ============ Step 3 测试发送 ============ */
.test-row-card {
  margin-bottom: 8px;
}

.test-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.test-row-head {
  display: flex;
  align-items: center;
  gap: 8px;
}

.platform-name {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-weight: 600;
}

.platform-icon {
  width: 16px;
  height: 16px;
}

.platform-tab-label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.test-row-params {
  margin-top: 4px;
}

.param-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.param-cell label {
  display: block;
  font-size: 11px;
  color: var(--ant-color-text-tertiary, #86909c);
  margin-bottom: 2px;
}

.test-row-footer {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 4px;
}

.scene-card {
  max-width: 900px;
}
</style>
