<script lang="ts" setup>
import { ref, computed } from 'vue';

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

import {
  Alert,
  Button,
  Checkbox,
  message,
  Tag,
  TypographyText,
  TypographyTitle,
} from 'ant-design-vue';

import { useVbenForm } from '#/adapter/form';
import { getOaChatListByPlatform } from '#/views/system/oa-chat/api';
import { getOaPlatformList } from '#/views/system/oa-platform/api';
import {
  createOaRobot,
  getOaRobotGroupedByPlatform,
  getOaRobotInfo,
  syncOaRobotChatMembers,
  testSendOaRobot,
  updateOaRobot,
} from '#/views/system/oa-robot/api';
import {
  createFormSchema,
  editFormSchema,
  isUsePlatformCredential,
  isWorkWechatApp,
  modalFormProps,
} from '#/views/system/oa-robot/config/form';

/**
 * OA 机器人新增/编辑弹窗
 *
 * 关键交互：
 * - 打开弹窗时组件内直接调 getOaPlatformList，把 options 合并进 schema 后一次性 setState
 *   （禁止事后 updateSchema，避免与 setState(schema) 竞态导致所属平台选项为空）
 * - 用 update 标志区分新增/编辑：新增走 createFormSchema（含 webhook/secret），编辑走 editFormSchema
 * - 「绑定群聊」复选框组：按当前选中的 platform_code 过滤，仅展示该平台下的群聊
 * - 应用 API：凭证来源 Radio（平台默认 / 自定义）+ 可选「克隆机器人」回填
 * - 弹窗内有「测试发送」按钮，循环跑该平台支持的所有消息类型
 */

interface TestResultItem {
  message_type: string;
  name: string;
  success: boolean;
  message: string;
  response: any;
  cost_ms: number;
}

interface ChatOption {
  id: number;
  name: string;
}

const isUpdate = ref(false);
const gridApi = ref();
/** 测试发送按钮的 loading 状态（与底部「确定」的 confirmLoading 分离，避免互相干扰） */
const testing = ref(false);
/** 最近一次测试发送的结果列表（按消息类型分别展示） */
const testResults = ref<TestResultItem[]>([]);
/** 测试发送结果汇总提示 */
const testSummary = ref('');
/** 按平台分组的群聊列表（结构：{ platform_code: [{ id, name }, ...] }） */
const chatsByPlatform = ref<Record<string, ChatOption[]>>({});
/** 当前已勾选的群聊 ID 列表（编辑场景下回显用） */
const selectedChatIds = ref<number[]>([]);
/** 当前选中的平台编码（控制群聊列表过滤） */
const currentPlatformCode = ref('');
/** 按平台分组的机器人列表（克隆源下拉用） */
const robotsByPlatform = ref<Record<string, { id: number; name: string }[]>>(
  {},
);
/** 同步群成员 loading */
const syncingMembers = ref(false);
/** 最近一次同步的成员数量提示 */
const memberSyncHint = ref('');

/** 应用 API 平台不展示 N:N 群聊绑定 */
const showChatBind = computed(
  () => currentPlatformCode.value !== '' && !isWorkWechatApp(currentPlatformCode.value),
);

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

const [Modal, modalApi] = useVbenModal({
  fullscreenButton: false,
  draggable: true,
  onCancel() {
    modalApi.close();
  },
  onConfirm: async () => {
    formApi.validate().then(async (e: any) => {
      if (e.valid) {
        const values = await formApi.getValues();
        // 合并群聊绑定数组到提交载荷（后端 OaRobotController::notRequest 已包含 chat_ids）
        const payload = { ...values, chat_ids: selectedChatIds.value };
        modalApi.setState({ loading: true, confirmLoading: true });
        const submitApi = isUpdate.value ? updateOaRobot : createOaRobot;
        submitApi(payload)
          .then(() => {
            message.success('保存成功');
            gridApi.value?.reload();
            modalApi.close();
          })
          .finally(() => {
            modalApi.setState({ loading: false, confirmLoading: false });
          });
      }
    });
    await formApi.validateAndSubmitForm();
  },
  onOpenChange(isOpen: boolean) {
    gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
    if (isOpen) {
      // 必须用 update 标志区分，不能用 if (values)：新增时 values={} 也是 truthy，会误走编辑分支
      const data = modalApi.getData<Record<string, any>>() ?? {};
      const values = data.values;
      isUpdate.value = !!data.update;
      // 组件内直接拉平台列表，合并进 schema 后再 setState，避免 updateSchema 竞态
      applySchemaWithPlatforms(isUpdate.value).then(() => {
        if (isUpdate.value && values && Object.keys(values).length > 0) {
          formApi.setValues(values);
          currentPlatformCode.value = String(values.platform_code ?? '');
          selectedChatIds.value = Array.isArray(values.chat_ids)
            ? values.chat_ids.map((id: any) => Number(id))
            : [];
          const memberCount = Array.isArray(values.members)
            ? values.members.length
            : 0;
          memberSyncHint.value = isWorkWechatApp(currentPlatformCode.value)
            ? `已缓存群成员 ${memberCount} 人`
            : '';
        } else {
          currentPlatformCode.value = '';
          selectedChatIds.value = [];
          memberSyncHint.value = '';
        }
      });
      loadChatsByPlatform();
      testResults.value = [];
      testSummary.value = '';
    }
  },
});

/**
 * 当前平台下可绑定的群聊列表
 * 切换平台时自动过滤，仅展示该平台下的群聊
 */
const chatsOfCurrentPlatform = computed<ChatOption[]>(() => {
  return chatsByPlatform.value[currentPlatformCode.value] ?? [];
});

/**
 * 平台切换处理：清理已不属于当前平台的勾选项（避免提交无效绑定）
 */
function handlePlatformChange(value: string) {
  currentPlatformCode.value = value;
  const allowedIds = new Set(chatsOfCurrentPlatform.value.map((c) => c.id));
  selectedChatIds.value = selectedChatIds.value.filter((id) =>
    allowedIds.has(id),
  );
  if (isWorkWechatApp(value)) {
    selectedChatIds.value = [];
    // 切换到应用 API 时刷新克隆源下拉（按平台过滤）
    refreshCloneOptions(value);
  } else {
    formApi.setValues({ clone_from_id: undefined });
  }
  memberSyncHint.value = '';
}

/**
 * 选择克隆源：拉详情回填凭证策略与 chat_id（不覆盖用户已改的名称，除非名称为空）
 */
async function handleCloneFromChange(cloneId: number | undefined) {
  if (!cloneId) return;
  try {
    const detail = await getOaRobotInfo(Number(cloneId));
    if (!detail) return;
    const current = await formApi.getValues();
    formApi.setValues({
      platform_code: detail.platform_code,
      use_platform_credential: Number(detail.use_platform_credential ?? 1),
      corp_id: detail.corp_id || '',
      secret: detail.secret || '',
      agent_id: Number(detail.agent_id || 0) || undefined,
      chat_id: detail.chat_id || '',
      remark: detail.remark || '',
      // 名称：空则用「源名+副本」，已有内容不强制覆盖
      name:
        current.name && String(current.name).trim() !== ''
          ? current.name
          : `${detail.name || '机器人'}副本`,
      clone_from_id: Number(cloneId),
    });
    currentPlatformCode.value = String(detail.platform_code ?? '');
  } catch (e) {
    console.error('加载克隆源失败', e);
    message.error('加载克隆源失败');
  }
}

/**
 * 按当前平台刷新「克隆机器人」下拉 options
 */
function refreshCloneOptions(platformCode: string) {
  const options = (robotsByPlatform.value[platformCode] ?? []).map((r) => ({
    label: r.name,
    value: r.id,
  }));
  formApi.updateSchema([
    {
      fieldName: 'clone_from_id',
      componentProps: {
        options,
        placeholder: '可选，选择后回填凭证与群聊（不含强制改名）',
        allowClear: true,
        showSearch: true,
        optionFilterProp: 'label',
        onChange: (val: number | undefined) => {
          handleCloneFromChange(val);
        },
      },
    },
  ]);
}

/**
 * 编辑态手动同步群成员（需已保存凭证）
 */
async function handleSyncMembers() {
  const values = await formApi.getValues();
  if (!values.id) {
    message.warning('请先保存机器人后再同步成员');
    return;
  }
  syncingMembers.value = true;
  try {
    const data = await syncOaRobotChatMembers({ id: Number(values.id) });
    memberSyncHint.value = `已同步 ${data?.member_count ?? 0} 人${data?.chat_name ? `（${data.chat_name}）` : ''}`;
    message.success(memberSyncHint.value);
  } finally {
    syncingMembers.value = false;
  }
}

/**
 * 组件内直接调 getOaPlatformList，把平台选项合并进 schema 后一次性 setState
 * - 显示全部平台（含已禁用的，便于历史数据回显）
 * - 已禁用平台在 label 上加后缀并禁用选择
 * - 注入 onChange：点选平台时同步 currentPlatformCode 并过滤群聊勾选
 *
 * @param isEdit 是否编辑态（决定用 editFormSchema 还是 createFormSchema）
 */
async function applySchemaWithPlatforms(isEdit: boolean) {
  let options: { label: string; value: string; disabled: boolean }[] = [];
  try {
    const list = await getOaPlatformList();
    const rows: any[] = Array.isArray(list) ? list : list?.data || [];
    options = rows.map((item) => ({
      label: item.enabled === 1 ? item.name : `${item.name}（已禁用）`,
      value: item.platform_code,
      disabled: item.enabled !== 1,
    }));
  } catch (e) {
    console.error('加载平台列表失败', e);
  }
  // 新增态预拉克隆源列表（按平台分组）
  if (!isEdit) {
    try {
      const grouped = await getOaRobotGroupedByPlatform();
      robotsByPlatform.value = grouped ?? {};
    } catch (e) {
      console.error('加载机器人分组失败', e);
      robotsByPlatform.value = {};
    }
  }
  const base = isEdit ? editFormSchema : createFormSchema;
  // 深拷贝字段配置，避免污染导出的静态 schema；把 options/onChange 写进 platform_code
  const schema = base.map((field: any) => {
    if (field.fieldName === 'platform_code') {
      return {
        ...field,
        componentProps: {
          ...(field.componentProps || {}),
          options,
          // RadioGroup 的 change 事件（antd 原生）：e.target.value 取选中值
          onChange: (e: any) => {
            const next = e?.target?.value ?? '';
            if (typeof next === 'string' && next !== '') {
              handlePlatformChange(next);
            }
          },
        },
      };
    }
    if (field.fieldName === 'clone_from_id') {
      return {
        ...field,
        componentProps: {
          ...(field.componentProps || {}),
          options: [],
          onChange: (val: number | undefined) => {
            handleCloneFromChange(val);
          },
        },
      };
    }
    return { ...field };
  });
  formApi.setState({ schema });
}

/**
 * 拉取所有平台下的群聊列表（一次拉全量，前端按 platform_code 过滤）
 */
async function loadChatsByPlatform() {
  try {
    const data = await getOaChatListByPlatform();
    chatsByPlatform.value = data ?? {};
  } catch (e) {
    console.error('加载群聊列表失败', e);
    chatsByPlatform.value = {};
  }
}

/**
 * 测试发送：循环跑该平台支持的所有消息类型
 *
 * 入参策略（与 webhook 入口分离后的新逻辑）：
 * - 编辑态：webhook_url/secret 已不在表单中，直接用 row.id 走后端兜底取密钥
 * - 新增态：用表单填写的 webhook_url/secret 走（保存前验证密钥有效性）
 *
 * 单平台最多 8 种类型，需并行调用以缩短等待时间（约 10~20s）
 * 返回的 results 数组按消息类型分别展示成功/失败
 */
async function handleTestSend() {
  const values = await formApi.getValues();
  if (!values.platform_code) {
    message.warning('请先选择平台');
    return;
  }
  const isApp = isWorkWechatApp(values.platform_code);
  if (!isUpdate.value) {
    if (isApp) {
      if (!values.chat_id) {
        message.warning('请填写群聊 ID');
        return;
      }
      if (
        !isUsePlatformCredential(values) &&
        (!values.corp_id || !values.secret || !values.agent_id)
      ) {
        message.warning('自定义凭证请填写 corp_id、Secret、agent_id');
        return;
      }
    } else if (!values.webhook_url || values.webhook_url === '****') {
      message.warning('请填写 webhook 地址');
      return;
    }
  }
  testing.value = true;
  testResults.value = [];
  testSummary.value = '正在发送测试消息，请稍候（最多 8 种类型）...';
  try {
    const result = await testSendOaRobot(
      isUpdate.value
        ? {
            id: values.id,
            platform_code: values.platform_code,
            test_content: '萧康云医 OA 测试消息',
          }
        : isApp
          ? {
              platform_code: values.platform_code,
              use_platform_credential: Number(
                values.use_platform_credential ?? 1,
              ),
              corp_id: values.corp_id || '',
              secret: values.secret || '',
              agent_id: Number(values.agent_id || 0),
              chat_id: values.chat_id,
              test_content: '萧康云医 OA 测试消息',
            }
          : {
              platform_code: values.platform_code,
              webhook_url: values.webhook_url,
              secret: values.secret || '',
              test_content: '萧康云医 OA 测试消息',
            },
    );
    if (Array.isArray(result?.results)) {
      testResults.value = result.results;
      const total = result.total ?? result.results.length;
      const succ = result.success_count ?? result.results.filter((r: any) => r.success).length;
      const fail = total - succ;
      testSummary.value = `共 ${total} 种类型，成功 ${succ} 种，失败 ${fail} 种`;
      if (fail === 0) {
        message.success(testSummary.value);
      } else if (succ === 0) {
        message.error(testSummary.value);
      } else {
        message.warning(testSummary.value);
      }
    } else if (result?.success !== undefined) {
      testResults.value = [
        {
          message_type: 'text',
          name: '文本消息',
          success: result.success,
          message: result.message || '',
          response: result.response,
          cost_ms: 0,
        },
      ];
      testSummary.value = result.success ? '测试发送成功' : '测试发送失败';
    }
  } finally {
    testing.value = false;
  }
}
</script>

<template>
  <Modal
    :title="`${isUpdate === true ? '编辑' : '新增'}机器人`"
    class="w-[40%]"
  >
    <Form />

    <!-- webhook 平台：N:N 绑定群聊；应用 API 目标群即 chat_id，不展示本区 -->
    <div v-if="showChatBind" class="chat-bind-section">
      <TypographyTitle :level="5" class="chat-bind-title">
        绑定群聊
      </TypographyTitle>
      <TypographyText type="secondary" class="chat-bind-hint">
        仅展示「{{ currentPlatformCode || '请先选平台' }}」平台下的群聊
      </TypographyText>
      <TypographyText
        v-if="chatsOfCurrentPlatform.length === 0"
        type="secondary"
        class="chat-empty"
      >
        当前平台下暂无群聊，可先到「OA群聊管理」中新增
      </TypographyText>
      <Checkbox.Group
        v-else
        v-model:value="selectedChatIds"
        class="chat-checkbox-group"
      >
        <Checkbox
          v-for="chat in chatsOfCurrentPlatform"
          :key="chat.id"
          :value="chat.id"
        >
          {{ chat.name }}
        </Checkbox>
      </Checkbox.Group>
    </div>

    <!-- 应用 API：同步群成员入口（编辑态有 id 时可用） -->
    <div
      v-if="isWorkWechatApp(currentPlatformCode)"
      class="chat-bind-section"
    >
      <TypographyTitle :level="5" class="chat-bind-title">
        群成员
      </TypographyTitle>
      <TypographyText type="secondary" class="chat-bind-hint">
        保存凭证后可同步 appchat 成员，供测试发送与场景 @ 勾选
      </TypographyText>
      <div class="member-sync-row">
        <Button
          v-if="isUpdate"
          size="small"
          :loading="syncingMembers"
          @click="handleSyncMembers"
        >
          同步群成员
        </Button>
        <TypographyText v-if="memberSyncHint" type="secondary">
          {{ memberSyncHint }}
        </TypographyText>
        <TypographyText v-else-if="!isUpdate" type="secondary">
          首次保存后会自动同步成员
        </TypographyText>
      </div>
    </div>

    <!-- 测试发送结果展示区：仅在有过测试时显示 -->
    <div v-if="testResults.length > 0 || testSummary" class="test-results">
      <Alert
        v-if="testSummary"
        :type="
          testResults.length === 0
            ? 'info'
            : testResults.every((r) => r.success)
              ? 'success'
              : testResults.some((r) => r.success)
                ? 'warning'
                : 'error'
        "
        :message="testSummary"
        show-icon
        style="margin-bottom: 12px"
      />

      <div v-if="testResults.length > 0" class="result-list">
        <div
          v-for="item in testResults"
          :key="item.message_type"
          class="result-item"
        >
          <div class="result-header">
            <Tag :color="item.success ? 'success' : 'error'">
              {{ item.success ? '成功' : '失败' }}
            </Tag>
            <span class="result-name">{{ item.name }}</span>
            <span class="result-type">({{ item.message_type }})</span>
            <span v-if="item.cost_ms" class="result-cost">{{ item.cost_ms }}ms</span>
          </div>
          <div v-if="!item.success && item.message" class="result-error">
            {{ item.message }}
          </div>
        </div>
      </div>
    </div>

    <!-- webhook 平台保留表单内测试；应用 API 请到列表「测试」独立弹窗选人/选群 -->
    <template v-if="!isWorkWechatApp(currentPlatformCode)" #prepend-footer>
      <Button type="default" :loading="testing" @click="handleTestSend">
        测试发送（全部类型）
      </Button>
    </template>
  </Modal>
</template>

<style scoped lang="scss">
/*
 * 暗色适配：去掉自定义背景块后，仅保留布局间距相关样式
 * 颜色全部由 antd 原生组件（TypographyTitle / TypographyText / Checkbox）自动适配
 */
.chat-bind-section {
  margin-top: 16px;
}

.chat-bind-title {
  /* 覆盖 TypographyTitle 默认大字号，让其更紧凑 */
  margin-bottom: 4px !important;
  font-size: 14px !important;
}

.chat-bind-hint {
  font-size: 12px;
}

.chat-empty {
  display: block;
  font-size: 12px;
  padding: 8px 0;
}

.chat-checkbox-group {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}

.member-sync-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 8px;
}

.test-results {
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px dashed var(--ant-color-border-secondary, #e5e6eb);
}

.result-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.result-item {
  padding: 8px 12px;
  border-radius: 4px;
  background: var(--ant-color-fill-quaternary, #f7f8fa);
  font-size: 12px;
}

.result-header {
  display: flex;
  align-items: center;
  gap: 6px;
}

.result-name {
  font-weight: 500;
  color: var(--ant-color-text, #1d2129);
}

.result-type {
  color: var(--ant-color-text-tertiary, #86909c);
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
}

.result-cost {
  margin-left: auto;
  color: var(--ant-color-text-tertiary, #86909c);
}

.result-error {
  margin-top: 4px;
  color: var(--ant-color-error, #f53f3f);
  word-break: break-all;
  line-height: 1.5;
}
</style>
