<script setup lang="ts">
import type { VbenFormProps } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { DataPushRule } from '#/api-client';

import type { Dayjs } from 'dayjs';

import { computed, h, nextTick, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';

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

import {
  Button,
  Checkbox,
  DatePicker,
  Input,
  message as Message,
  Modal,
  Popover,
  Select,
  Switch,
  Table,
  Tag,
} from 'ant-design-vue';

import dayjs from 'dayjs';

import { useVbenForm } from '#/adapter/form';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
  getCommonGetSelectList,
  postAggregationDeviceBatchCreateAsync,
  postAggregationDeviceBatchDeleteAsync,
  postAggregationDeviceBatchSyncDeviceFromOneNetAsync,
  postAggregationDeviceBindingDeviceThingModel,
  postAggregationDeviceCallDeviceServiceForApiAsync,
  getAggregationDeviceGetMasterStationListForApiAsync,
  postAggregationDeviceCreateAsync,
  postAggregationDeviceDeleteAsync,
  postAggregationDeviceDeviceBatchMasterSwitchForApiAsync,
  postAggregationDeviceDeviceBatchUpgradeForApiAsync,
  postAggregationDeviceDeviceCommandForApiAsync,
  postAggregationDeviceDeviceUpgradeForApiAsync,
  postAggregationDeviceEnableDeviceForApiAsync,
  postAggregationDeviceGetDevicePropertyReadResultForApiAsync,
  postAggregationDeviceGetDevicePropertyValueForApiAsync,
  postAggregationDeviceIssueDevicePropertyReadForApiAsync,
  postAggregationDeviceRecoverDeviceToOneNet,
  postAggregationDeviceRepushDeviceInfoToIoTplatform,
  postAggregationDeviceUpdateDeviceEncryptionTypeForApiAsync,
  postAggregationDeviceUpdateDeviceSecKeyForApiAsync,
  postDeviceInfoCacheDeviceDataToRedis,
  postDeviceInfoDeviceDataFlowConfig,
  postDeviceInfoPage,
  postIoTplatformThingModelInfoGetIoTplatformThingModelService,
  postIoTplatformThingModelInfoPageAsync,
} from '#/api-client';
import { Icon } from '#/components/icon';
import { Loading } from '#/components/Loading';
import { TableAction } from '#/components/table-action';
import { $t } from '#/locales';
import DataPushRuleEditor from '#/views/components/DataPushRuleEditor.vue';

import {
  addDeviceFormSchema,
  batchAddDeviceFormSchema,
  batchDeleteDeviceFormSchema,
  batchUpgradeDeviceFormSchema,
  bindDeviceThingModelFormSchema,
  commandFormSchema,
  deviceUpgradeFormSchema,
  editDeviceFormSchemaEdit,
  querySchema,
  tableSchema,
} from './schema';

defineOptions({
  name: 'DeviceInfo',
});

const router = useRouter();
const route = useRoute();

// 标志：是否正在从路由参数设置表单值（用于防止重复触发搜索）
const isSettingFromRoute = ref(false);

const formOptions: VbenFormProps = {
  schema: querySchema.value,
  submitOnChange: false,
  handleValuesChange: async (values, changedFields) => {
    // 如果正在从路由参数设置值，则不触发搜索（避免重复请求）
    if (isSettingFromRoute.value) {
      return;
    }

    // 当平台类型变化时，清除产品ID（因为不同平台的产品列表不同）
    if (changedFields.includes('ioTPlatform') && gridApi?.formApi) {
      await gridApi.formApi.setFieldValue('ioTPlatformProductId', undefined);
    }

    // 当产品切换时，触发搜索
    if (changedFields.includes('ioTPlatformProductId')) {
      // 延迟一下，确保表单值已更新
      await nextTick();
      if (gridApi?.reload && gridApi?.formApi) {
        try {
          // 获取最新的表单值并传递给 reload
          const latestValues = await gridApi.formApi.getValues();
          await gridApi.reload(latestValues);
        } catch (error) {
          console.error('重新加载数据时出错:', error);
        }
      }
    }
  },
};
const gridOptions: VxeGridProps<any> = {
  checkboxConfig: {
    highlight: true,
    labelField: 'name',
  },
  columns: tableSchema.value,
  height: 'auto',
  keepSource: true,
  pagerConfig: {},
  toolbarConfig: {
    custom: true,
  },
  customConfig: {
    storage: true,
  },
  proxyConfig: {
    ajax: {
      query: async ({ page }, formValues) => {
        // 总是从表单API获取最新的表单值，确保参数完整（分页、刷新时formValues可能不完整）
        const currentFormValues = gridApi?.formApi
          ? await gridApi.formApi.getValues()
          : formValues || {};

        // 优先使用从表单API获取的值（最新的），如果没有则使用传入的formValues
        const finalFormValues = { ...formValues, ...currentFormValues };

        const { data } = await postDeviceInfoPage({
          body: {
            pageIndex: page.currentPage,
            pageSize: page.pageSize,
            ...finalFormValues,
          },
        });
        return data;
      },
    },
  },
  // 监听复选框选择事件
  onCheckboxChange: ({ row, checked }: any) => {
    if (!checked) {
      // 取消选择时不需要验证
      return;
    }

    // 获取当前所有已选中的设备
    let currentSelected: any[] = [];
    if (gridApi?.grid) {
      const gridInstance = gridApi.grid as any;
      if (typeof gridInstance.getCheckboxRecords === 'function') {
        currentSelected = gridInstance.getCheckboxRecords();
      } else if (gridInstance.checkboxRecords) {
        currentSelected = gridInstance.checkboxRecords;
      }
    }

    // 如果已经有选中的设备，检查产品ID是否一致
    if (currentSelected.length > 0) {
      const firstProductId = currentSelected[0]?.ioTPlatformProductId;
      const currentProductId = row?.ioTPlatformProductId;

      // 检查产品ID是否一致
      if (
        firstProductId &&
        currentProductId &&
        firstProductId !== currentProductId
      ) {
        // 产品ID不一致，阻止选择并提示
        Message.warning(
          '只能选择同一种产品的设备进行批量绑定，当前设备的产品与已选设备的产品不一致',
        );

        // 延迟取消选择，确保提示显示
        setTimeout(() => {
          if (gridApi?.grid) {
            const gridInstance = gridApi.grid as any;
            if (typeof gridInstance.setCheckboxRow === 'function') {
              gridInstance.setCheckboxRow(row, false);
            } else if (typeof gridInstance.clearCheckboxRow === 'function') {
              gridInstance.clearCheckboxRow(row);
            }
          }
        }, 100);
      }
    }
  },
  // 监听全选事件
  onCheckboxAll: ({ checked, records }: any) => {
    if (!checked || records.length <= 1) {
      // 取消全选或只有一条记录时不需要验证
      return;
    }

    // 检查所有记录的产品ID是否一致
    const productIds = records
      .map((r: any) => r.ioTPlatformProductId)
      .filter(Boolean);

    if (productIds.length > 0) {
      const uniqueProductIds = [...new Set(productIds)];
      if (uniqueProductIds.length > 1) {
        // 产品ID不一致，阻止全选并提示
        Message.warning(
          '只能选择同一种产品的设备进行批量绑定，当前页面包含多种产品',
        );

        // 延迟取消全选
        setTimeout(() => {
          if (gridApi?.grid) {
            const gridInstance = gridApi.grid as any;
            if (typeof gridInstance.clearCheckboxAll === 'function') {
              gridInstance.clearCheckboxAll();
            } else if (typeof gridInstance.setAllCheckboxRow === 'function') {
              gridInstance.setAllCheckboxRow(false);
            }
          }
        }, 100);
      }
    }
  },
};

const [Grid, gridApi] = useVbenVxeGrid({ formOptions, gridOptions });

const editRow: Record<string, any> = ref({});

// 监听路由参数变化，自动设置筛选条件
watch(
  () => route.query,
  async (newQuery) => {
    if (newQuery.ioTPlatform || newQuery.ioTPlatformProductId) {
      // 等待组件初始化的辅助函数
      const waitForFormApi = async (maxRetries = 10, delay = 100) => {
        for (let i = 0; i < maxRetries; i++) {
          if (gridApi?.formApi) {
            return true;
          }
          await new Promise((resolve) => setTimeout(resolve, delay));
        }
        return false;
      };

      // 延迟执行，确保组件完全初始化
      setTimeout(async () => {
        try {
          // 设置标志，防止 handleValuesChange 触发搜索
          isSettingFromRoute.value = true;

          // 等待表单API初始化
          const formApiReady = await waitForFormApi();
          if (!formApiReady) {
            console.warn('表单API初始化超时，无法设置筛选条件');
            isSettingFromRoute.value = false;
            return;
          }

          // 设置表单筛选条件
          const filterValues: any = {};

          if (newQuery.ioTPlatform) {
            filterValues.ioTPlatform = String(newQuery.ioTPlatform);
          }

          if (newQuery.ioTPlatformProductId) {
            // 设置产品ID字段
            filterValues.ioTPlatformProductId = String(
              newQuery.ioTPlatformProductId,
            );
          }

          // 先设置平台类型，等待其选项加载完成
          if (filterValues.ioTPlatform) {
            await gridApi.formApi.setFieldValue(
              'ioTPlatform',
              filterValues.ioTPlatform,
            );
            await nextTick();
            // 等待平台选择器的选项加载完成（ApiSelect组件需要时间加载选项）
            await new Promise((resolve) => setTimeout(resolve, 600));
          }

          // 再设置产品ID（如果有），需要等待产品选择器的选项加载完成
          if (filterValues.ioTPlatformProductId) {
            await gridApi.formApi.setFieldValue(
              'ioTPlatformProductId',
              filterValues.ioTPlatformProductId,
            );
            await nextTick();
            // 等待产品选择器的选项加载完成
            await new Promise((resolve) => setTimeout(resolve, 600));
          }

          // 等待所有表单值更新完成后再重新加载数据
          await nextTick();
          await new Promise((resolve) => setTimeout(resolve, 200));

          // 重置标志
          isSettingFromRoute.value = false;

          // 触发搜索（只触发一次）
          if (gridApi?.reload) {
            gridApi.reload();
          }
        } catch (error) {
          console.error('设置表单筛选条件时出错:', error);
          isSettingFromRoute.value = false;
        }
      }, 500); // 初始延迟，确保组件开始初始化
    }
  },
  { immediate: true },
);

const cacheRefreshLoading = ref(false);
const batchSyncLoading = ref(false);
const pageLoading = ref(false);
const loadingTip = ref('缓存刷新中...');
const commandRow: Record<string, any> = ref({});

/** 设备生命周期：8 表示停用模式 */
const DEVICE_STOPPED_LIFE_INFO = 8;

function isDeviceInStoppedState(row: Record<string, any>): boolean {
  return Number(row?.lifeInfo) === DEVICE_STOPPED_LIFE_INFO;
}

function setDeviceLifeInfo(row: Record<string, any>, lifeInfo: number) {
  row.lifeInfo = lifeInfo;
}

function getEncryptionTypeTagColor(encryptionType: unknown): string {
  const type = Number(encryptionType);
  if (type === 1) return 'blue';
  if (type === 2) return 'green';
  if (encryptionType === 'PerProductTypeSecret') return 'blue';
  if (encryptionType === 'PerDeviceSecret') return 'green';
  return 'default';
}

// 设备升级相关
const upgradeRow: Record<string, any> = ref({});

// 表格列定义
const commandTableColumns = computed(() => [
  {
    title: '',
    key: 'selected',
    width: 50,
    fixed: 'left' as const,
    customRender: ({ record }: any) => {
      return h(Checkbox, {
        checked: record.selected,
        'onUpdate:checked': (checked: boolean) => {
          record.selected = checked;
        },
      });
    },
  },
  {
    title: '标准物模型属性标识符',
    dataIndex: 'standardFieldName',
    key: 'standardFieldName',
    width: 200,
  },
  {
    title: '标准物模型属性名称',
    dataIndex: 'standardFieldDisplayName',
    key: 'standardFieldDisplayName',
    width: 200,
  },
  {
    title: '指令内容',
    dataIndex: 'commandValue',
    key: 'commandValue',
    width: 200,
    customRender: ({ record }: any) => {
      // 只读模式（r）时隐藏指令内容输入框
      if (record.accessMode === 'r') {
        return h(
          'span',
          { style: { color: '#999' } },
          '只读属性，无法设置指令',
        );
      }

      const dataType = record.ioTPlatformRawFieldDataType;
      const isNumeric = isNumericType(dataType);

      return h(Input, {
        value: record.commandValue,
        placeholder: isNumeric ? '请输入数字' : '请输入指令内容',
        type: 'text',
        'onUpdate:value': (val: string) => {
          // 如果是数字类型，只允许输入数字（包括小数点和负号）
          if (isNumeric && val !== '') {
            // 允许数字、小数点、负号
            // 正则表达式：允许负号开头，然后是数字，可以有小数点和小数部分
            const numericRegex = /^-?\d*(?:\.\d*)?$/;
            if (!numericRegex.test(val)) {
              return; // 不符合格式，不更新
            }
          }
          record.commandValue = val;
        },
      });
    },
  },
  {
    title: '返回结果',
    dataIndex: 'result',
    key: 'result',
    width: 250,
    customRender: ({ record }: any) => {
      // 确保 result 是字符串类型
      const resultText = record.result
        ? (typeof record.result === 'string'
          ? record.result
          : String(record.result))
        : '-';
      const resultStr = String(resultText);
      const isError = resultStr.includes('失败') || resultStr.includes('失败:');

      return h(
        'div',
        {
          style: {
            color:
              resultText === '-' ? '#666' : isError ? '#ff4d4f' : '#52c41a',
            wordBreak: 'break-all',
          },
        },
        resultStr,
      );
    },
  },
  {
    title: '操作',
    key: 'action',
    width: 200,
    fixed: 'right' as const,
    customRender: ({ record }: any) => {
      const buttons = [];

      // 读写模式（rw）和只写模式（w）都可以发送指令
      if (record.accessMode === 'rw' || record.accessMode === 'w') {
        buttons.push(
          h(
            Button,
            {
              type: 'primary',
              size: 'small',
              loading: record.loading,
              onClick: () => sendCommand(record),
            },
            { default: () => '发送指令' },
          ),
        );
      }

      // 只读模式（r）和读写模式（rw）可以抄读数据，只写模式（w）不能抄读
      if (record.accessMode === 'r' || record.accessMode === 'rw') {
        buttons.push(
          h(
            Button,
            {
              size: 'small',
              loading: record.loading,
              onClick: () => readData(record),
            },
            { default: () => '抄读数据' },
          ),
        );
      }

      return h(
        'div',
        {
          style: {
            display: 'flex',
            gap: '8px',
          },
        },
        buttons,
      );
    },
  },
]);

// 绑定设备端物模型相关
const bindRows: Array<Record<string, any>> = ref([]);

// 平台物模型属性列表（用于表格展示）
interface ThingModelProperty {
  id: string; // 物模型属性ID
  ioTPlatformRawFieldName: string; // 平台原始字段名
  standardFieldName: string; // 标准物模型字段名（标识符）
  standardFieldDisplayName: string; // 标准字段显示名
  commandValue: string; // 指令内容
  result: string; // 返回结果
  loading?: boolean; // 是否正在加载
  selected?: boolean; // 是否选中
  accessMode?: string; // 访问模式：rw-读写，r-只读
  ioTPlatformRawFieldDataType?: string; // 平台原始字段数据类型，如：INT32, STRING 等
}

const thingModelProperties = ref<ThingModelProperty[]>([]);
const thingModelLoading = ref(false);
// 筛选关键词
const filterKeyword = ref('');

// 设备操作统一使用标准物模型标识符作为参数键
const getPropertyIdentifier = (property: ThingModelProperty) =>
  (property.standardFieldName || '').trim();

// 判断是否为数字类型
const isNumericType = (dataType: string | undefined): boolean => {
  if (!dataType) return false;
  const upperType = dataType.toUpperCase();
  return (
    upperType.includes('INT') ||
    upperType.includes('FLOAT') ||
    upperType.includes('DOUBLE') ||
    upperType === 'NUMBER' ||
    upperType === 'DECIMAL'
  );
};

// 根据数据类型转换值
const convertValueByType = (
  value: string,
  dataType: string | undefined,
): any => {
  if (!value || value.trim() === '') {
    return value;
  }

  if (!dataType) {
    return value;
  }

  const upperType = dataType.toUpperCase();

  // 数字类型
  if (isNumericType(dataType)) {
    // 尝试转换为数字
    const numValue = Number(value);
    if (isNaN(numValue)) {
      return value; // 如果转换失败，返回原值
    }

    // 根据具体类型进行转换
    if (upperType.includes('INT')) {
      return Number.parseInt(value, 10);
    } else if (
      upperType.includes('FLOAT') ||
      upperType.includes('DOUBLE') ||
      upperType === 'DECIMAL'
    ) {
      return Number.parseFloat(value);
    }
    return numValue;
  }

  // 布尔类型
  if (upperType === 'BOOL' || upperType === 'BOOLEAN') {
    if (value.toLowerCase() === 'true' || value === '1') {
      return true;
    }
    if (value.toLowerCase() === 'false' || value === '0') {
      return false;
    }
    return value; // 无法转换时返回原值
  }

  // 字符串类型或其他类型，直接返回
  return value;
};
// 过滤后的物模型属性列表
const filteredThingModelProperties = computed(() => {
  if (!filterKeyword.value.trim()) {
    return thingModelProperties.value;
  }
  const keyword = filterKeyword.value.trim().toLowerCase();
  return thingModelProperties.value.filter((prop) => {
    return (
      prop.standardFieldDisplayName?.toLowerCase().includes(keyword) ||
      prop.standardFieldName?.toLowerCase().includes(keyword)
    );
  });
});

// 获取平台物模型属性列表
const fetchThingModelProperties = async (row: Record<string, any>) => {
  if (!row.ioTPlatform || !row.ioTPlatformProductId) {
    Message.warning('设备缺少平台或产品信息，无法获取物模型属性');
    return;
  }

  thingModelLoading.value = true;
  try {
    const requestBody: any = {
      pageIndex: 1,
      pageSize: 1000, // 获取所有数据
      IsPage: false, // 不分页
      IsOperableIdentifier: true, // 不分页
      ioTPlatform:
        typeof row.ioTPlatform === 'string'
          ? Number.parseInt(row.ioTPlatform)
          : row.ioTPlatform,
      ioTPlatformProductId: String(row.ioTPlatformProductId),
    };

    const { data } = await postIoTplatformThingModelInfoPageAsync({
      body: requestBody,
    });

    const items = Array.isArray(data?.items) ? data!.items : [];
    thingModelProperties.value = items.map((item: any) => ({
      id: item.id || '',
      ioTPlatformRawFieldName: item.ioTPlatformRawFieldName || '',
      standardFieldName: item.standardFieldName || '',
      standardFieldDisplayName: item.standardFieldDisplayName || '',
      commandValue: '',
      result: '',
      loading: false,
      selected: false,
      accessMode: item.accessMode || '', // 保存访问模式
      ioTPlatformRawFieldDataType: item.ioTPlatformRawFieldDataType || '', // 保存数据类型
    }));
  } catch (error) {
    console.error('获取平台物模型属性失败:', error);
    Message.error('获取平台物模型属性失败');
    thingModelProperties.value = [];
  } finally {
    thingModelLoading.value = false;
  }
};

const [UserModal, userModalApi] = useVbenModal({
  draggable: true,
  onConfirm: submit,
  onBeforeClose: () => {
    editRow.value = {};
    return true;
  },
});

// 批量发送指令
const submitBatchCommand = async () => {
  // 获取所有选中的行，并过滤掉值为空的，同时排除只读属性（accessMode === 'r'）
  // 允许读写模式（rw）和只写模式（w）
  const selectedProperties = thingModelProperties.value.filter(
    (prop) =>
      prop.selected &&
      getPropertyIdentifier(prop) &&
      (prop.accessMode === 'rw' || prop.accessMode === 'w') && // 允许读写模式和只写模式
      prop.commandValue &&
      prop.commandValue.trim() !== '',
  );

  if (selectedProperties.length === 0) {
    Message.warning(
      '请至少选择一个已填写指令内容的可写属性（只读属性无法发送指令）',
    );
    return;
  }

  // 将选中的属性组成键值对（键为标识符，值为指令内容）
  const commandContent: Record<string, any> = {};
  selectedProperties.forEach((prop) => {
    // 根据数据类型转换值
    const convertedValue = convertValueByType(
      prop.commandValue.trim(),
      prop.ioTPlatformRawFieldDataType,
    );
    const identifier = getPropertyIdentifier(prop);
    if (identifier) {
      commandContent[identifier] = convertedValue;
    }
  });

  try {
    commandModalApi.setState({ loading: true, confirmLoading: true });

    // 调用指令接口
    const result = await postAggregationDeviceDeviceCommandForApiAsync({
      body: {
        id: commandRow.value.id,
        commandContent,
      },
    });

    if (result.data) {
      // 更新所有选中行的返回结果
      selectedProperties.forEach((prop) => {
        prop.result =
          typeof result.data === 'string'
            ? result.data
            : (result.data
              ? JSON.stringify(result.data)
              : '指令下发成功');
      });
      Message.success(`成功发送 ${selectedProperties.length} 个指令`);
    } else {
      selectedProperties.forEach((prop) => {
        prop.result = '指令下发失败';
      });
      Message.error('指令下发失败');
    }
  } catch (error) {
    console.error('批量指令发送失败:', error);
    selectedProperties.forEach((prop) => {
      prop.result = `指令下发失败: ${(error as Error).message}`;
    });
    Message.error('指令下发失败');
  } finally {
    commandModalApi.setState({ loading: false, confirmLoading: false });
  }
};

const [CommandModal, commandModalApi] = useVbenModal({
  draggable: true,
  showCancelButton: false,
  showOkButton: false,
  onBeforeClose: () => {
    commandRow.value = {};
    thingModelProperties.value = [];
    filterKeyword.value = ''; // 清空筛选关键词
    return true;
  },
  onOpenChange: async (isOpen: boolean) => {
    if (isOpen && commandRow.value) {
      // 打开模态框时，获取平台物模型属性列表
      await fetchThingModelProperties(commandRow.value);
      filterKeyword.value = ''; // 重置筛选关键词
    }
  },
});

// 获取命令弹窗的状态
const commandModalState = commandModalApi.useStore();

const deviceEnableLoading = ref(false);

// 设备启停
const handleDeviceEnable = () => {
  if (!commandRow.value.id) {
    Message.warning('设备信息无效');
    return;
  }

  const isCurrentlyStopped = isDeviceInStoppedState(commandRow.value);
  const nextEnable = isCurrentlyStopped;
  const actionText = nextEnable ? '启用' : '停用';
  const deviceLabel =
    commandRow.value.deviceName || commandRow.value.deviceAddress || '';

  Modal.confirm({
    title: `确认${actionText}设备？`,
    content: `设备：${deviceLabel}`,
    okText: actionText,
    cancelText: $t('common.cancel'),
    onOk: async () => {
      try {
        deviceEnableLoading.value = true;
        commandModalApi.setState({ loading: true });
        const result = await postAggregationDeviceEnableDeviceForApiAsync({
          body: {
            id: commandRow.value.id,
            enable: nextEnable,
          },
        });
        if (result.data) {
          setDeviceLifeInfo(
            commandRow.value,
            nextEnable ? 5 : DEVICE_STOPPED_LIFE_INFO,
          );
          Message.success(`设备已${actionText}`);
          gridApi.reload();
        } else {
          Message.error(`${actionText}失败`);
        }
      } catch (error) {
        console.error('设备启停失败:', error);
        Message.error(`${actionText}失败`);
      } finally {
        deviceEnableLoading.value = false;
        commandModalApi.setState({ loading: false });
      }
    },
  });
};

// 更新设备密码
const updatePasswordSecKey = ref('');

const submitUpdateDevicePassword = async () => {
  if (!commandRow.value.id) {
    Message.warning('设备信息无效');
    return false;
  }

  const secKey = updatePasswordSecKey.value.trim();
  if (secKey.length > 32) {
    Message.warning('密码长度不能超过32个字符');
    return false;
  }

  try {
    updatePasswordModalApi.setState({ loading: true, confirmLoading: true });
    const result = await postAggregationDeviceUpdateDeviceSecKeyForApiAsync({
      body: {
        id: commandRow.value.id,
        secKey: secKey || undefined,
      },
    });
    if (result.data) {
      if (result.data.platformPassword) {
        commandRow.value.platformPassword = result.data.platformPassword;
      }
      Message.success('密码更新成功');
      updatePasswordModalApi.close();
      gridApi.reload();
      return true;
    }
    Message.error('密码更新失败');
    return false;
  } catch (error) {
    console.error('更新设备密码失败:', error);
    Message.error('密码更新失败');
    return false;
  } finally {
    updatePasswordModalApi.setState({ loading: false, confirmLoading: false });
  }
};

const [UpdatePasswordModal, updatePasswordModalApi] = useVbenModal({
  draggable: true,
  title: '更新设备密码',
  onConfirm: submitUpdateDevicePassword,
  onBeforeClose: () => {
    updatePasswordSecKey.value = '';
    return true;
  },
});

const openUpdatePasswordModal = () => {
  if (!commandRow.value.id) {
    Message.warning('设备信息无效');
    return;
  }
  updatePasswordSecKey.value = '';
  updatePasswordModalApi.open();
};

// 切换鉴权方式（一型一密 <-> 一机一密）
const switchEncryptionTypeLoading = ref(false);
// 鉴权方式取数值：1 一型一密，2 一机一密
const getEncryptionTypeLabel = (type: number): string =>
  type === 2 ? '一机一密' : '一型一密';
const handleSwitchEncryptionType = () => {
  if (!commandRow.value.id) {
    Message.warning('设备信息无效');
    return;
  }
  // 当前值兼容数值与字符串字面量，非一机一密一律按一型一密处理
  const current =
    Number(commandRow.value.encryptionType) === 2 ||
    commandRow.value.encryptionType === 'PerDeviceSecret'
      ? 2
      : 1;
  const target = current === 1 ? 2 : 1;
  const deviceLabel =
    commandRow.value.deviceName || commandRow.value.deviceAddress || '';
  Modal.confirm({
    title: '确认切换鉴权方式？',
    content: `设备：${deviceLabel}，将从「${getEncryptionTypeLabel(current)}」切换为「${getEncryptionTypeLabel(target)}」。切换会向设备下发一次主站切换指令，设备确认后生效。`,
    okText: '切换',
    cancelText: $t('common.cancel'),
    onOk: async () => {
      try {
        switchEncryptionTypeLoading.value = true;
        commandModalApi.setState({ loading: true });
        const result =
          await postAggregationDeviceUpdateDeviceEncryptionTypeForApiAsync({
            body: {
              id: commandRow.value.id,
              encryptionType: target,
            },
          });
        if (result.data) {
          commandRow.value.encryptionType = target;
          Message.success(
            `已切换为${getEncryptionTypeLabel(target)}，指令已下发，设备确认后生效`,
          );
          gridApi.reload();
        } else {
          Message.error('切换鉴权方式失败');
        }
      } catch (error) {
        console.error('切换鉴权方式失败:', error);
        Message.error('切换鉴权方式失败');
      } finally {
        switchEncryptionTypeLoading.value = false;
        commandModalApi.setState({ loading: false });
      }
    },
  });
};

// ---------- 物模型服务调用 ----------
type ServiceParamField = { name: string; label: string };

const serviceCallRow = ref<Record<string, any>>({});
const serviceCallLoading = ref(false);
const serviceCallOptions = ref<
  Array<{
    key?: null | string;
    secondValue?: null | string;
    thirdValue?: unknown;
    value?: null | string;
  }>
>([]);
const selectedServiceIndex = ref<number | undefined>(undefined);
const serviceCallFormValues = ref<Record<string, string>>({});

/** 服务调用接口返回，展示在弹窗内；不自动关闭弹窗 */
const serviceCallResultText = ref('');
const serviceCallResultOk = ref(true);

function formatServiceCallResultPayload(data: unknown): string {
  if (data === undefined || data === null) {
    return '（接口无返回体）';
  }
  if (typeof data === 'string') {
    return data.trim() === '' ? '（空字符串）' : data;
  }
  try {
    return JSON.stringify(data, null, 2);
  } catch {
    return String(data);
  }
}

function clearServiceCallResult() {
  serviceCallResultText.value = '';
  serviceCallResultOk.value = true;
}

/** 需用日期时间选择「起始时标」的冻结/补抄类服务（与物模型服务标识 key 一致） */
const FREEZE_START_TIME_MARKER_SERVICES = new Set([
  'DayFreezeService',
  'MonthFreezeService',
  'SupplementaryService',
  'PowerFreezeService',
]);

const currentSelectedServiceKey = computed(() => {
  const i = selectedServiceIndex.value;
  if (i == null) {
    return '';
  }
  return String(serviceCallOptions.value[i]?.key ?? '').trim();
});

function isStartTimeMarkerField(f: ServiceParamField): boolean {
  const n = f.name.trim().toLowerCase();
  if (n === 'starttime' || n === 'start_time') {
    return true;
  }
  return f.label.includes('起始时标');
}

function shouldUseStartTimePickerForField(f: ServiceParamField): boolean {
  const key = currentSelectedServiceKey.value;
  if (!key || !FREEZE_START_TIME_MARKER_SERVICES.has(key)) {
    return false;
  }
  return isStartTimeMarkerField(f);
}

function isValveControlServiceItem(item: {
  key?: null | string;
  value?: null | string;
} | null): boolean {
  if (!item) {
    return false;
  }
  const key = String(item.key ?? '').trim();
  const display = String(item.value ?? '');
  if (display.includes('阀控')) {
    return true;
  }
  return /ValveControl/i.test(key);
}

function isValveCommandTypeField(f: ServiceParamField): boolean {
  const n = f.name.trim().toLowerCase();
  if (f.label.includes('阀控类型')) {
    return true;
  }
  return (
    n.includes('commandtype') ||
    n.includes('valvecontroltype') ||
    n === 'commandtype'
  );
}

/** 拉合闸服务（物模型服务 key） */
const OPERATE_BREAKER_SERVICE_KEY = 'OperateBreakerService';

function isOperateBreakerServiceItem(item: {
  key?: null | string;
  value?: null | string;
} | null): boolean {
  if (!item) {
    return false;
  }
  const key = String(item.key ?? '').trim();
  const display = String(item.value ?? '');
  if (display.includes('拉合闸')) {
    return true;
  }
  return key === OPERATE_BREAKER_SERVICE_KEY;
}

/** 拉合闸类型参数：与物模型 InputData 标识符 / 展示名对应 */
function isBreakerOperateTypeField(f: ServiceParamField): boolean {
  const n = f.name.trim().toLowerCase();
  if (f.label.includes('拉合闸类型') || f.label.includes('操作类型')) {
    return true;
  }
  if (n === 'operatetype' || n === 'operate_type') {
    return true;
  }
  if (n === 'operatecommandtype') {
    return true;
  }
  if (n.includes('breaker') && (n.includes('type') || n.includes('operate'))) {
    return true;
  }
  return false;
}

/** GetSelectList 入参 typeName：设备物模型指令类型枚举（阀控类型、拉合闸类型等共用） */
const DEVICE_THING_MODE_COMMAND_TYPE_ENUM = 'DeviceThingModeCommandTypeEnum';

function parseServiceCallStartTimeValue(
  raw: string | undefined,
): Dayjs | undefined {
  if (!raw?.trim()) {
    return undefined;
  }
  const d = dayjs(raw.trim());
  return d.isValid() ? d : undefined;
}

function onServiceCallStartTimeChange(fieldName: string, v: Dayjs | null) {
  const d = v as Dayjs | null;
  const next =
    d && typeof d.isValid === 'function' && d.isValid()
      ? d.format('YYYY-MM-DD HH:mm:ss')
      : '';
  serviceCallFormValues.value = {
    ...serviceCallFormValues.value,
    [fieldName]: next,
  };
}

const valveCommandTypeOptions = ref<Array<{ label: string; value: string }>>(
  [],
);
const valveCommandTypeLoading = ref(false);

function normalizeServiceThirdValue(raw: unknown): ServiceParamField[] {
  if (raw === null || raw === undefined || raw === '') {
    return [];
  }
  let data: unknown = raw;
  if (typeof data === 'string') {
    const s = data.trim();
    if (!s) {
      return [];
    }
    try {
      data = JSON.parse(s) as unknown;
    } catch {
      return [];
    }
  }
  if (Array.isArray(data)) {
    return data
      .map((item: any) => {
        if (!item || typeof item !== 'object') {
          return null;
        }
        // 后端常见：Identifier + Name（PascalCase），或小写 camelCase
        const name = String(
          item.Identifier ??
            item.identifier ??
            item.name ??
            item.field ??
            item.key ??
            item.prop ??
            '',
        ).trim();
        const label = String(
          item.Name ??
            item.name ??
            item.label ??
            item.title ??
            item.displayName ??
            item.standardFieldDisplayName ??
            name,
        ).trim();
        if (!name) {
          return null;
        }
        return { label: label || name, name };
      })
      .filter(Boolean) as ServiceParamField[];
  }
  if (typeof data === 'object') {
    return Object.entries(data as Record<string, unknown>).map(([k, v]) => ({
      name: k,
      label:
        v !== null && typeof v === 'object'
          ? String(
              (v as Record<string, unknown>).label ??
                (v as Record<string, unknown>).title ??
                (v as Record<string, unknown>).displayName ??
                (v as Record<string, unknown>).Name ??
                k,
            )
          : String(v ?? k),
    }));
  }
  return [];
}

/** thirdValue 为空时，用 secondValue 中的 JSON 字符串兜底（与接口返回一致） */
function getServiceParamSource(item: {
  secondValue?: null | string;
  thirdValue?: unknown;
}): unknown {
  const tv = item.thirdValue;
  if (tv != null && tv !== '') {
    if (Array.isArray(tv) && tv.length > 0) {
      return tv;
    }
    if (typeof tv === 'object' && !Array.isArray(tv) && Object.keys(tv).length > 0) {
      return tv;
    }
    if (typeof tv === 'string' && String(tv).trim()) {
      return tv;
    }
  }
  const sv = item.secondValue;
  if (typeof sv === 'string' && sv.trim()) {
    try {
      return JSON.parse(sv) as unknown;
    } catch {
      return null;
    }
  }
  return null;
}

const currentServiceItem = computed(() => {
  const i = selectedServiceIndex.value;
  if (i == null || i < 0) {
    return null;
  }
  return serviceCallOptions.value[i] ?? null;
});

const serviceParamFields = computed(() => {
  if (!currentServiceItem.value) {
    return [];
  }
  return normalizeServiceThirdValue(
    getServiceParamSource(currentServiceItem.value),
  );
});

watch(
  serviceParamFields,
  (fields) => {
    const next: Record<string, string> = {};
    for (const f of fields) {
      next[f.name] = '';
    }
    serviceCallFormValues.value = next;
  },
  { immediate: true },
);

const serviceCallSelectOptions = computed(() =>
  serviceCallOptions.value.map((opt, index) => ({
    label: opt.value || opt.key || `服务 ${index + 1}`,
    value: index,
  })),
);

const breakerOperateTypeOptions = computed(() =>
  extractBreakerOperateTypeOptionsFromSecondValue(currentServiceItem.value),
);

function shouldUseValveCommandTypeSelectForField(f: ServiceParamField): boolean {
  if (!isValveControlServiceItem(currentServiceItem.value)) {
    return false;
  }
  return isValveCommandTypeField(f);
}

function shouldUseBreakerOperateTypeSelectForField(
  f: ServiceParamField,
): boolean {
  if (!isOperateBreakerServiceItem(currentServiceItem.value)) {
    return false;
  }
  return isBreakerOperateTypeField(f);
}

function parseJsonMaybe(raw: unknown): unknown {
  if (typeof raw !== 'string') {
    return raw;
  }
  const s = raw.trim();
  if (!s) {
    return null;
  }
  try {
    return JSON.parse(s) as unknown;
  } catch {
    return null;
  }
}

function toSecondValueSelectOptions(
  list: unknown[],
): Array<{ label: string; value: string }> {
  return list
    .map((x: any) => {
      if (!x || typeof x !== 'object') {
        return null;
      }
      const value = String(
        x.key ?? x.id ?? x.value ?? x.Identifier ?? x.identifier ?? '',
      ).trim();
      const label = String(
        x.label ?? x.name ?? x.text ?? x.value ?? x.Name ?? x.name ?? value,
      ).trim();
      if (!value) {
        return null;
      }
      return { label: label || value, value };
    })
    .filter(Boolean) as Array<{ label: string; value: string }>;
}

function extractBreakerOperateTypeOptionsFromSecondValue(item: {
  secondValue?: null | string;
} | null): Array<{ label: string; value: string }> {
  if (!item) {
    return [];
  }
  const parsed = parseJsonMaybe(item.secondValue);
  if (!parsed) {
    return [];
  }

  if (Array.isArray(parsed)) {
    return toSecondValueSelectOptions(parsed);
  }

  if (typeof parsed === 'object') {
    const obj = parsed as Record<string, unknown>;
    const candidateLists = [
      obj.operateType,
      obj.OperateType,
      obj.operateTypeList,
      obj.operateTypes,
      obj.OperateTypeList,
      obj.OperateTypes,
      obj.options,
      obj.list,
      obj.items,
      obj.data,
    ];
    for (const c of candidateLists) {
      if (Array.isArray(c)) {
        const options = toSecondValueSelectOptions(c);
        if (options.length > 0) {
          return options;
        }
      }
      // secondValue 也可能是键值映射：{ "1":"拉闸", "2":"合闸" }
      if (c && typeof c === 'object' && !Array.isArray(c)) {
        const mapped = Object.entries(c as Record<string, unknown>).map(
          ([k, v]) => ({
            label: String(v ?? k),
            value: String(k),
          }),
        );
        if (mapped.length > 0) {
          return mapped;
        }
      }
    }
  }
  return [];
}

function mapSelectListToOptions(list: any[]): Array<{ label: string; value: string }> {
  return list
    .map((x: any) => ({
      label: String(x.value ?? x.label ?? ''),
      value: String(x.key ?? ''),
    }))
    .filter((o) => o.value);
}

function unwrapSelectListResponse(data: unknown): any[] {
  if (Array.isArray(data)) {
    return data;
  }
  if (data && Array.isArray((data as any).items)) {
    return (data as any).items;
  }
  if (data && Array.isArray((data as any).data)) {
    return (data as any).data;
  }
  return [];
}

watch(
  () => currentServiceItem.value,
  async (item) => {
    valveCommandTypeOptions.value = [];
    if (!item) {
      return;
    }

    if (isValveControlServiceItem(item)) {
      valveCommandTypeLoading.value = true;
      try {
        const { data } = await getCommonGetSelectList({
          query: { typeName: DEVICE_THING_MODE_COMMAND_TYPE_ENUM },
        } as any);
        valveCommandTypeOptions.value = mapSelectListToOptions(
          unwrapSelectListResponse(data),
        );
      } catch {
        valveCommandTypeOptions.value = [];
      } finally {
        valveCommandTypeLoading.value = false;
      }
    }

  },
);

async function fetchThingModelServiceOptions(productId: string) {
  serviceCallLoading.value = true;
  try {
    const { data } =
      await postIoTplatformThingModelInfoGetIoTplatformThingModelService({
        body: { id: productId },
      } as any);
    serviceCallOptions.value = Array.isArray(data) ? data : [];
    selectedServiceIndex.value = undefined;
    if (serviceCallOptions.value.length === 0) {
      Message.warning('当前产品下未配置可调用的物模型服务');
    }
  } catch (error) {
    console.error('获取物模型服务列表失败:', error);
    Message.error('获取物模型服务列表失败');
    serviceCallOptions.value = [];
    selectedServiceIndex.value = undefined;
  } finally {
    serviceCallLoading.value = false;
  }
}

const [ServiceCallModal, serviceCallModalApi] = useVbenModal({
  draggable: true,
  showCancelButton: false,
  showOkButton: false,
  onBeforeClose: () => {
    serviceCallRow.value = {};
    serviceCallOptions.value = [];
    selectedServiceIndex.value = undefined;
    serviceCallFormValues.value = {};
    valveCommandTypeOptions.value = [];
    valveCommandTypeLoading.value = false;
    clearServiceCallResult();
    return true;
  },
  onOpenChange: async (isOpen: boolean) => {
    if (!isOpen) {
      return;
    }
    clearServiceCallResult();
    const pid = serviceCallRow.value?.ioTPlatformProductId;
    if (!pid) {
      Message.warning('设备缺少平台产品信息，无法获取服务列表');
      return;
    }
    await fetchThingModelServiceOptions(String(pid));
  },
});

const serviceCallModalState = serviceCallModalApi.useStore();

/** 服务入参：部分字段需为数值，避免以字符串提交 */
function coerceServiceCallParamValue(
  fieldName: string,
  raw: string,
  ctx: { isOperateBreakerService: boolean },
): { ok: true; value: unknown } | { ok: false; message: string } {
  const key = fieldName.trim().toLowerCase();
  if (key === 'quantity') {
    const n = Number(raw);
    if (Number.isNaN(n)) {
      return { ok: false, message: '数量（Quantity）需填写有效数字' };
    }
    return { ok: true, value: n };
  }
  // 拉合闸服务：操作类型（OperateType）须为数值，不能传字符串
  if (ctx.isOperateBreakerService && key === 'operatetype') {
    const n = Number(raw);
    if (Number.isNaN(n)) {
      return {
        ok: false,
        message: '操作类型（OperateType）需为有效数字',
      };
    }
    return { ok: true, value: n };
  }
  return { ok: true, value: raw };
}

async function submitDeviceServiceCall() {
  const row = serviceCallRow.value;
  if (!row?.id) {
    return;
  }
  if (selectedServiceIndex.value == null) {
    Message.warning('请选择要调用的服务');
    return;
  }
  const item = serviceCallOptions.value[selectedServiceIndex.value];
  if (!item) {
    return;
  }
  // 服务标识用 key（如 DayFreezeService）；secondValue 为参数 JSON，不能当作服务名
  const serviceName = String(item.key ?? '').trim();
  if (!serviceName) {
    Message.warning('当前选项缺少服务标识，无法调用');
    return;
  }
  const serviceParams: Record<string, unknown> = {};
  const coerceCtx = {
    isOperateBreakerService: isOperateBreakerServiceItem(item),
  };
  for (const f of serviceParamFields.value) {
    const v = (serviceCallFormValues.value[f.name] ?? '').trim();
    if (v !== '') {
      const coerced = coerceServiceCallParamValue(f.name, v, coerceCtx);
      if (!coerced.ok) {
        Message.warning(coerced.message);
        return;
      }
      serviceParams[f.name] = coerced.value;
    }
  }
  try {
    serviceCallModalApi.setState({ loading: true, confirmLoading: true });
    const { data } = await postAggregationDeviceCallDeviceServiceForApiAsync({
      body: {
        id: row.id,
        serviceName,
        serviceParams,
      },
    } as any);
    serviceCallResultOk.value = true;
    serviceCallResultText.value = formatServiceCallResultPayload(data);
    Message.success('服务调用成功');
  } catch (error: any) {
    console.error('调用设备服务失败:', error);
    const backendMessage =
      error?.response?.data?.error?.message ??
      error?.data?.error?.message ??
      error?.error?.message;
    const msg =
      backendMessage && typeof backendMessage === 'string'
        ? backendMessage
        : (error as Error)?.message || '调用设备服务失败';
    serviceCallResultOk.value = false;
    serviceCallResultText.value = msg;
  } finally {
    serviceCallModalApi.setState({ loading: false, confirmLoading: false });
  }
}

function openServiceCallModal(row: Record<string, any>) {
  if (!row.ioTPlatformProductId) {
    Message.warning('设备缺少平台产品信息，无法获取服务列表');
    return;
  }
  clearServiceCallResult();
  serviceCallRow.value = row;
  serviceCallModalApi.open();
}

// 设备绑定设备端物模型弹窗
const [BindModal, bindModalApi] = useVbenModal({
  draggable: true,
  onConfirm: submitBindDeviceThingModel,
  onBeforeClose: () => {
    bindRows.value = [];
    return true;
  },
  onOpenChange: async (isOpen: boolean) => {
    if (isOpen) {
      console.log('绑定弹窗打开，选中的设备:', bindRows.value);
      await nextTick();
      // 确保表单值已设置
      const formValues = await bindFormApi.getValues();
      console.log('弹窗打开时的表单值:', formValues);
    }
  },
});

const [AddForm, addFormApi] = useVbenForm({
  // 默认展开
  collapsed: false,
  // 所有表单项共用，可单独在表单内覆盖
  commonConfig: {
    labelWidth: 110,
    componentProps: {
      class: 'w-4/5',
    },
  },
  layout: 'horizontal',
  schema: addDeviceFormSchema.value,
  showCollapseButton: false,
  showDefaultActions: false,
  wrapperClass: 'grid-cols-2',
  handleValuesChange: async (values, changedFields) => {
    // 当账号切换时，清空产品名称字段
    if (changedFields.includes('ioTPlatformAccountId')) {
      await addFormApi.setFieldValue('ioTPlatformProductId', undefined);
    }
  },
});

const [EditForm, editFormApi] = useVbenForm({
  // 默认展开
  collapsed: false,
  // 所有表单项共用，可单独在表单内覆盖
  commonConfig: {
    labelWidth: 110,
    componentProps: {
      class: 'w-4/5',
    },
  },
  // 提交函数
  layout: 'horizontal',
  schema: editDeviceFormSchemaEdit.value,
  showCollapseButton: false,
  showDefaultActions: false,
  wrapperClass: 'grid-cols-2',
  handleValuesChange: async (values, changedFields) => {
    // 当账号切换时，清空产品名称字段
    if (changedFields.includes('ioTPlatformAccountId')) {
      await editFormApi.setFieldValue('ioTPlatformProductId', undefined);
    }
  },
});

const [CommandForm, commandFormApi] = useVbenForm({
  // 默认展开
  collapsed: false,
  // 所有表单项共用，可单独在表单内覆盖
  commonConfig: {
    labelWidth: 110,
    componentProps: {
      class: 'w-full',
    },
  },
  layout: 'horizontal',
  schema: commandFormSchema.value,
  showCollapseButton: false,
  showDefaultActions: false,
});

const [BindForm, bindFormApi] = useVbenForm({
  collapsed: false,
  commonConfig: {
    labelWidth: 160,
    componentProps: {
      class: 'w-full',
    },
  },
  layout: 'horizontal',
  schema: bindDeviceThingModelFormSchema.value,
  showCollapseButton: false,
  showDefaultActions: false,
  wrapperClass: 'grid-cols-1',
  handleValuesChange: async (values, changedFields) => {
    // 当开关打开时，手动触发设备物模型下拉框的重新加载
    if (
      changedFields.includes('isNeedConfigDeviceModel') &&
      values.isNeedConfigDeviceModel
    ) {
      console.log('开关打开，准备触发设备物模型下拉框重新加载');
      await nextTick();
      setTimeout(async () => {
        try {
          const productId =
            values._ioTPlatformProductId || values.ioTPlatformProductId;
          console.log('手动触发下拉框重新加载，产品ID:', productId);
          if (productId) {
            const fieldRef = bindFormApi.getFieldComponentRef(
              'deviceThingModelDataId',
            );
            if (fieldRef && typeof fieldRef.updateParam === 'function') {
              fieldRef.updateParam({
                query: {
                  input: {
                    id: String(productId),
                  },
                },
              });
              console.log('已手动触发下拉框重新加载');
            } else {
              console.warn('无法获取设备物模型下拉框的引用');
            }
          }
        } catch (error) {
          console.error('手动触发下拉框重新加载失败:', error);
        }
      }, 100);
    }
  },
});

const [BatchAddForm, batchAddFormApi] = useVbenForm({
  // 默认展开
  collapsed: false,
  // 所有表单项共用，可单独在表单内覆盖
  commonConfig: {
    labelWidth: 110,
    componentProps: {
      class: 'w-4/5',
    },
  },
  layout: 'horizontal',
  schema: batchAddDeviceFormSchema.value,
  showCollapseButton: false,
  showDefaultActions: false,
  wrapperClass: 'grid-cols-2',
  // 添加响应式监听
  autoSubmitOnEnter: false,
  handleValuesChange: async (values, changedFields) => {
    // 当账号切换时，清空产品名称字段
    if (changedFields.includes('ioTPlatformAccountId')) {
      await batchAddFormApi.setFieldValue('ioTPlatformProductId', undefined);
    }
  },
});

const [BatchAddModal, batchAddModalApi] = useVbenModal({
  draggable: true,
  onConfirm: submitBatchAdd,
  onBeforeClose: () => {
    batchAddFormApi.resetForm();
    stopLineCheck();
    return true;
  },
  onOpenChange: (isOpen) => {
    if (isOpen) {
      // 模态框打开时启动行数检查
      setTimeout(() => {
        startLineCheck();
      }, 100);
    } else {
      // 模态框关闭时停止行数检查
      stopLineCheck();
    }
  },
});

// 批量同步OneNET设备表单
// 批量设备升级表单
const [BatchUpgradeForm, batchUpgradeFormApi] = useVbenForm({
  collapsed: false,
  commonConfig: {
    labelWidth: 120,
    componentProps: {
      class: 'w-full',
    },
  },
  layout: 'horizontal',
  schema: batchUpgradeDeviceFormSchema.value,
  showCollapseButton: false,
  showDefaultActions: false,
  wrapperClass: 'grid-cols-1',
  handleValuesChange: async (values, changedFields) => {
    // 当账号切换时，清空产品名称字段
    if (changedFields.includes('ioTPlatformAccountId')) {
      await batchUpgradeFormApi.setFieldValue(
        'ioTPlatformProductId',
        undefined,
      );
      await batchUpgradeFormApi.setFieldValue('targetVersionId', undefined);
    }
    // 当产品ID变化时，触发目标版本字段重新加载
    if (changedFields.includes('ioTPlatformProductId')) {
      await nextTick();
      await batchUpgradeFormApi.setFieldValue('targetVersionId', undefined);
    }
  },
});

// 批量设备升级提交逻辑
const submitBatchUpgrade = async () => {
  const { valid } = await batchUpgradeFormApi.validate();
  if (!valid) return;

  const formValues = await batchUpgradeFormApi.getValues();

  // 处理设备地址列表
  const addressList = formValues.addressList
    .split('\n')
    .map((address: string) => address.trim())
    .filter((address: string) => address.length > 0);

  if (addressList.length === 0) {
    Message.error('请输入至少一个设备地址');
    return;
  }

  // 验证必要字段
  if (!formValues.ioTPlatform) {
    Message.error('请选择平台类型');
    return;
  }

  if (!formValues.ioTPlatformProductId) {
    Message.error('请选择产品');
    return;
  }

  // 验证目标版本
  if (!formValues.targetVersionId) {
    Message.error('请选择目标版本');
    return;
  }

  try {
    batchUpgradeModalApi.setState({ loading: true, confirmLoading: true });

    // 调用批量升级接口
    const result = await postAggregationDeviceDeviceBatchUpgradeForApiAsync({
      body: {
        addressList,
        ioTPlatform:
          typeof formValues.ioTPlatform === 'string'
            ? Number.parseInt(formValues.ioTPlatform)
            : formValues.ioTPlatform,
        ioTPlatformProductId: String(formValues.ioTPlatformProductId),
        nowFirmwareVersionDataId: formValues.targetVersionId,
        upgradeDescription: formValues.upgradeDescription || null,
      },
    });

    if (result.data) {
      Message.success(`批量升级任务已提交，共 ${addressList.length} 个设备`);
      batchUpgradeModalApi.close();
      batchUpgradeFormApi.resetForm();
      gridApi.reload();
    } else {
      Message.error('批量升级失败');
    }
  } catch (error) {
    console.error('批量升级失败:', error);
    Message.error('批量升级失败');
  } finally {
    batchUpgradeModalApi.setState({ loading: false, confirmLoading: false });
  }
};

// 批量升级地址行数
const batchUpgradeAddressLines = ref(0);

// 监听批量升级表单值变化，实时更新行数
watch(
  () => batchUpgradeFormApi.getValues()?.addressList,
  (newValue) => {
    try {
      if (!newValue || typeof newValue !== 'string') {
        batchUpgradeAddressLines.value = 0;
        return;
      }
      const lines = newValue.split('\n').filter((line: string) => line.trim());
      batchUpgradeAddressLines.value = lines.length;
    } catch {
      batchUpgradeAddressLines.value = 0;
    }
  },
  { immediate: true },
);

// 监听表单值变化，实时更新行数（备用方案）
watch(
  () => batchUpgradeFormApi.getValues(),
  (formValues) => {
    try {
      const addressList = formValues?.addressList;
      if (!addressList || typeof addressList !== 'string') {
        batchUpgradeAddressLines.value = 0;
        return;
      }
      const lines = addressList
        .split('\n')
        .filter((line: string) => line.trim());
      batchUpgradeAddressLines.value = lines.length;
    } catch {
      batchUpgradeAddressLines.value = 0;
    }
  },
  { deep: true, immediate: true },
);

// 使用定时器定期检查批量升级行数（第三种方案）
let batchUpgradeLineCheckInterval: NodeJS.Timeout | null = null;

// 在批量升级模态框打开时启动定时器
const startBatchUpgradeLineCheck = () => {
  if (batchUpgradeLineCheckInterval) {
    clearInterval(batchUpgradeLineCheckInterval);
  }
  batchUpgradeLineCheckInterval = setInterval(() => {
    try {
      // 尝试多种方式获取表单值
      let addressList = '';

      // 方式1：直接获取
      const formValues = batchUpgradeFormApi.getValues();
      addressList = formValues?.addressList || '';

      // 方式2：如果方式1失败，尝试其他方法
      if (!addressList) {
        try {
          const rawValues = batchUpgradeFormApi.getValues();
          addressList = rawValues?.addressList || '';
        } catch (error) {
          console.log('方式2失败:', error);
        }
      }

      // 方式3：直接从DOM获取
      if (!addressList) {
        const textarea = document.querySelector(
          'textarea[name="addressList"]',
        ) as HTMLTextAreaElement;
        if (textarea) {
          addressList = textarea.value;
        }
      }

      if (addressList && typeof addressList === 'string') {
        const lines = addressList
          .split('\n')
          .filter((line: string) => line.trim());
        const newLineCount = lines.length;
        console.log(
          '批量升级计算出的行数:',
          newLineCount,
          '当前行数:',
          batchUpgradeAddressLines.value,
        );
        // 无论是否相同都更新，确保响应式触发
        if (newLineCount !== batchUpgradeAddressLines.value) {
          console.log('批量升级更新行数:', newLineCount);
          batchUpgradeAddressLines.value = newLineCount;
          // 使用 nextTick 确保 DOM 更新
          nextTick(() => {
            console.log(
              '批量升级行数已更新到:',
              batchUpgradeAddressLines.value,
            );
          });
        }
      } else {
        // 如果地址列表为空，重置为0
        if (batchUpgradeAddressLines.value !== 0) {
          batchUpgradeAddressLines.value = 0;
        }
      }
    } catch (error) {
      console.error('定时器检查批量升级行数失败:', error);
    }
  }, 300);
};

// 在批量升级模态框关闭时清除定时器
const stopBatchUpgradeLineCheck = () => {
  if (batchUpgradeLineCheckInterval) {
    clearInterval(batchUpgradeLineCheckInterval);
    batchUpgradeLineCheckInterval = null;
  }
};

// 打开批量升级弹窗
const openBatchUpgradeModal = () => {
  batchUpgradeFormApi.resetForm();
  batchUpgradeAddressLines.value = 0; // 重置行数
  batchUpgradeModalApi.open();
};

const [BatchUpgradeModal, batchUpgradeModalApi] = useVbenModal({
  draggable: true,
  onConfirm: submitBatchUpgrade,
  onBeforeClose: () => {
    batchUpgradeFormApi.resetForm();
    batchUpgradeAddressLines.value = 0; // 重置行数
    stopBatchUpgradeLineCheck();
    return true;
  },
  onOpenChange: (isOpen) => {
    if (isOpen) {
      // 模态框打开时启动行数检查
      setTimeout(() => {
        startBatchUpgradeLineCheck();
      }, 100);
    } else {
      // 模态框关闭时停止行数检查
      stopBatchUpgradeLineCheck();
    }
  },
});

// 获取批量升级模态框的状态
const batchUpgradeModalState = batchUpgradeModalApi.useStore();

// 设备升级表单
const [UpgradeForm, upgradeFormApi] = useVbenForm({
  collapsed: false,
  commonConfig: {
    labelWidth: 120,
    componentProps: {
      class: 'w-full',
    },
  },
  layout: 'horizontal',
  schema: deviceUpgradeFormSchema.value,
  showCollapseButton: false,
  showDefaultActions: false,
  wrapperClass: 'grid-cols-1',
  handleValuesChange: async (values, changedFields) => {
    // 当产品ID变化时，触发目标版本字段重新加载
    if (changedFields.includes('ioTPlatformProductId')) {
      await nextTick();
      // 触发目标版本字段的依赖重新计算
      if (upgradeFormApi) {
        await upgradeFormApi.setFieldValue('targetVersionId', undefined);
      }
    }
  },
});

// 设备升级提交逻辑
const submitDeviceUpgrade = async () => {
  const { valid } = await upgradeFormApi.validate();
  if (!valid) return;

  const formValues = await upgradeFormApi.getValues();

  // 验证必要字段
  if (!upgradeRow.value.id) {
    Message.error('设备ID不存在');
    return;
  }

  if (!upgradeRow.value.ioTPlatform) {
    Message.error('设备平台类型不存在');
    return;
  }

  if (!upgradeRow.value.ioTPlatformProductId) {
    Message.error('设备产品ID不存在');
    return;
  }

  // 验证目标版本（在提交时验证）
  if (!formValues.targetVersionId) {
    Message.error('请选择目标版本');
    return;
  }

  try {
    upgradeModalApi.setState({ loading: true, confirmLoading: true });

    // 调用实际的升级接口，使用body参数（JSON格式）
    const result = await postAggregationDeviceDeviceUpgradeForApiAsync({
      body: {
        id: upgradeRow.value.id,
        ioTPlatform:
          typeof upgradeRow.value.ioTPlatform === 'string'
            ? Number.parseInt(upgradeRow.value.ioTPlatform)
            : upgradeRow.value.ioTPlatform,
        ioTPlatformProductId: String(upgradeRow.value.ioTPlatformProductId),
        nowFirmwareVersionDataId: formValues.targetVersionId,
        upgradeDescription: formValues.upgradeDescription || null,
      },
    });

    if (result.data) {
      Message.success('设备升级任务已提交');
      upgradeModalApi.close();
      upgradeRow.value = {};
      upgradeFormApi.resetForm();
      gridApi.reload();
    } else {
      Message.error('设备升级失败');
    }
  } catch (error) {
    console.error('设备升级失败:', error);
    Message.error('设备升级失败');
  } finally {
    upgradeModalApi.setState({ loading: false, confirmLoading: false });
  }
};

// 打开设备升级弹窗
const openUpgradeModal = (row: Record<string, any>) => {
  upgradeRow.value = row;
  upgradeModalApi.open();
  // 设置表单初始值
  nextTick(() => {
    upgradeFormApi.setValues({
      deviceAddress: row.deviceAddress,
      deviceName: row.deviceName || row.deviceAddress,
      currentVersion: row.firmwareVersion || '未知',
      ioTPlatform: row.ioTPlatform,
      ioTPlatformProductId: row.ioTPlatformProductId,
      targetVersionId: undefined,
      upgradeDescription: '',
    });
  });
};

const [UpgradeModal, upgradeModalApi] = useVbenModal({
  draggable: true,
  onConfirm: submitDeviceUpgrade,
  onBeforeClose: () => {
    upgradeRow.value = {};
    upgradeFormApi.resetForm();
    return true;
  },
});

// 获取批量添加模态框的状态
const batchAddModalState = batchAddModalApi.useStore();

// 新增和编辑提交的逻辑
async function submit() {
  const isEdit = !!editRow.value.id;
  const formApi = isEdit ? editFormApi : addFormApi;
  const api = postAggregationDeviceCreateAsync; // 目前只有创建接口，编辑也使用创建接口
  const { valid } = await formApi.validate();
  if (!valid) return;

  const formValues = await formApi.getValues();

  const fetchParams: any = isEdit
    ? {
      id: editRow.value.id,
      ...formValues,
    }
    : {
      ...formValues,
    };

  try {
    userModalApi.setState({ loading: true, confirmLoading: true });
    const resp = await api({ body: fetchParams });
    if (resp.data) {
      Message.success(
        editRow.value.id ? $t('common.editSuccess') : $t('common.addSuccess'),
      );
      userModalApi.close();
      editRow.value = {};
      gridApi.reload();
    } else {
      Message.error(
        editRow.value.id ? $t('common.editFail') : $t('common.addFail'),
      );
    }
  } catch (error) {
    console.error('设备操作失败:', error);
    Message.error(
      editRow.value.id ? $t('common.editFail') : $t('common.addFail'),
    );
  } finally {
    userModalApi.setState({ loading: false, confirmLoading: false });
  }
}

async function onEdit(record: any) {
  editRow.value = record;
  userModalApi.open();

  // 设置表单值
  const formValues = { ...record };

  // 确保ioTPlatform是字符串格式，因为ApiSelect组件的valueField是'key'
  if (formValues.ioTPlatform !== undefined && formValues.ioTPlatform !== null) {
    formValues.ioTPlatform = String(formValues.ioTPlatform);
  }

  editFormApi.setValues(formValues);
}

function onDel(row: any) {
  Modal.confirm({
    title: `${$t('common.confirmDelete')}${row.deviceName || row.deviceAddress} ?`,
    onOk: async () => {
      try {
        const result = await postAggregationDeviceDeleteAsync({
          body: { id: row.id },
        });
        if (result.data) {
          gridApi.reload();
          Message.success($t('common.deleteSuccess'));
        } else {
          Message.error($t('common.deleteFail'));
        }
      } catch (error) {
        console.error('删除设备失败:', error);
        Message.error($t('common.deleteFail'));
      }
    },
  });
}
const toDeviceInfoData = (row: Record<string, any>) => {
  // 或者使用编程式导航
  router.push({
    path: '/iotdbdatamanagement/deviceData',
    query: {
      DeviceAddress: row.deviceAddress,
      IoTDataType: 'Data',
    },
  });
};

const toDeviceLog = (row: Record<string, any>) => {
  // 根据平台类型跳转到对应的日志页面
  if (row.ioTPlatform === 1 || row.ioTPlatform === '1') {
    // CTWing平台
    router.push({
      path: '/iotdbdatamanagement/ctwingLog',
      query: {
        DeviceAddress: row.deviceAddress,
      },
    });
  } else if (row.ioTPlatform === 2 || row.ioTPlatform === '2') {
    // OneNET平台
    router.push({
      path: '/iotdbdatamanagement/onenetLog',
      query: {
        DeviceAddress: row.deviceAddress,
      },
    });
  }
};

const toTelemetryLog = (row: Record<string, any>) => {
  // 跳转到遥测日志页面
  router.push({
    path: '/iotdbdatamanagement/telemetryLog',
    query: {
      DeviceAddress: row.deviceAddress,
    },
  });
};

// 重推设备信息到IoT平台
const repushDeviceInfo = async (row: Record<string, any>) => {
  try {
    pageLoading.value = true;
    loadingTip.value = '重推设备信息中...';
    const result = await postAggregationDeviceRepushDeviceInfoToIoTplatform({
      body: { id: row.id },
    });
    if (result.data) {
      Message.success('重推设备信息成功');
      gridApi.reload();
    } else {
      Message.error('重推设备信息失败');
    }
  } catch {
    Message.error('重推设备信息失败');
  } finally {
    pageLoading.value = false;
    loadingTip.value = '缓存刷新中...';
  }
};

/** 物联网平台枚举：2=移动OneNET，3=EMQX */
const IOT_PLATFORM_ONENET = 2;
const IOT_PLATFORM_EMQX = 3;

/**
 * 是否允许"改投 OneNET"（与后端闸门一致）：
 * 已是 OneNET 不显示；EMQX 恒放开（仅本地建档、无外部注册）；
 * 其余平台仅在"未真实推送成功"时放开。
 */
function canRecoverToOneNET(row: Record<string, any>): boolean {
  const platform = Number(row?.ioTPlatform);
  if (platform === IOT_PLATFORM_ONENET) return false;
  if (platform === IOT_PLATFORM_EMQX) return true;
  return !row?.isPlatformPushSuccess;
}

// 改投设备平台到 OneNET（平台纠错：主要用于误选 EMQX 的设备）
const recoverDeviceToOneNET = (row: Record<string, any>) => {
  const deviceLabel = row.deviceName || row.deviceAddress || '';
  Modal.confirm({
    title: '确认改投 OneNET？',
    content: `设备：${deviceLabel}。将把平台切换为 OneNET 并向 OneNET 注册（平台若已存在该设备则回填密钥），请确认无误。`,
    okText: '改投 OneNET',
    cancelText: $t('common.cancel'),
    onOk: async () => {
      try {
        pageLoading.value = true;
        loadingTip.value = '改投 OneNET 中...';
        const result = await postAggregationDeviceRecoverDeviceToOneNet({
          body: { id: row.id },
        });
        if (result.data) {
          Message.success('改投 OneNET 成功');
          gridApi.reload();
        } else {
          Message.error('改投 OneNET 失败');
        }
      } catch (error) {
        console.error('改投 OneNET 失败:', error);
        Message.error('改投 OneNET 失败');
      } finally {
        pageLoading.value = false;
        loadingTip.value = '缓存刷新中...';
      }
    },
  });
};

const openAddModal = async () => {
  editRow.value = {};
  userModalApi.open();
};

const openCommandModal = async (row: Record<string, any>) => {
  commandRow.value = row;
  thingModelProperties.value = [];
  commandModalApi.open();
};

// 打开绑定设备端物模型弹窗（支持单个设备或多个设备）
const openBindModal = async (
  rowOrRows?: Array<Record<string, any>> | Record<string, any>,
) => {
  // 如果没有传入参数，尝试从表格获取选中的设备
  let selectedRows: Array<Record<string, any>> = [];

  if (rowOrRows) {
    // 如果传入的是数组，直接使用
    if (Array.isArray(rowOrRows)) {
      selectedRows = rowOrRows;
    } else {
      // 如果传入的是单个设备，转为数组
      selectedRows = [rowOrRows];
    }
  } else {
    // 从表格获取选中的设备
    try {
      let checkboxRecords: any[] = [];

      // 尝试通过 grid 获取
      if (gridApi?.grid) {
        const gridInstance = gridApi.grid as any;
        if (typeof gridInstance.getCheckboxRecords === 'function') {
          checkboxRecords = gridInstance.getCheckboxRecords();
        } else if (gridInstance.checkboxRecords) {
          checkboxRecords = gridInstance.checkboxRecords;
        }
      }

      // 尝试通过 store 获取
      if (checkboxRecords.length === 0 && gridApi?.store) {
        const store = gridApi.store as any;
        if (store.checkboxRecords) {
          checkboxRecords = store.checkboxRecords;
        }
      }

      selectedRows = checkboxRecords;
    } catch (error) {
      console.warn('无法获取表格选中的设备:', error);
    }
  }

  if (selectedRows.length === 0) {
    Message.warning('请先选择要绑定的设备');
    return;
  }

  // 验证所有选中的设备必须是同一种产品（批量选择时）
  if (selectedRows.length > 1) {
    const productIds = selectedRows
      .map((row) => row.ioTPlatformProductId)
      .filter(Boolean); // 过滤掉空值

    if (productIds.length === 0) {
      Message.error('选中的设备中没有有效的产品ID，无法进行绑定');
      return;
    }

    // 检查产品ID是否一致
    const uniqueProductIds = [...new Set(productIds)];
    if (uniqueProductIds.length > 1) {
      Message.error(
        `选中的设备包含 ${uniqueProductIds.length} 种不同的产品，请只选择同一种产品的设备进行批量绑定`,
      );
      return;
    }
  }

  console.log('打开绑定弹窗，选中的设备:', selectedRows);
  bindRows.value = selectedRows;

  // 获取第一个设备的产品ID（用于下拉框）
  const firstDevice = selectedRows[0];
  const productId = firstDevice.ioTPlatformProductId;
  console.log('设置表单值，产品ID:', productId, '类型:', typeof productId);

  if (!productId) {
    console.warn(
      '警告：设备行数据中没有 ioTPlatformProductId 字段，请检查表格数据是否包含该字段',
    );
  }

  bindModalApi.open();
  await nextTick();

  // 根据已选设备的当前绑定状态，预填表单
  // 单选设备时，直接使用该设备当前的绑定配置
  let isNeedConfigDeviceModel = false;
  let deviceThingModelDataId: string | undefined;

  if (selectedRows.length === 1) {
    const row = selectedRows[0] || {};
    isNeedConfigDeviceModel = !!row.isNeedConfigDeviceModel;
    if (row.deviceThingModelDataId) {
      deviceThingModelDataId = String(row.deviceThingModelDataId);
    }
  } else {
    // 多选时：如果所有设备都已开启配置并且绑定的是同一个物模型，则进行统一预填
    const allNeedConfig = selectedRows.every(
      (row) => !!row.isNeedConfigDeviceModel,
    );
    const firstModelId = selectedRows[0]?.deviceThingModelDataId;
    const allSameModel =
      allNeedConfig &&
      !!firstModelId &&
      selectedRows.every((row) => row.deviceThingModelDataId === firstModelId);

    isNeedConfigDeviceModel = allNeedConfig;
    deviceThingModelDataId = allSameModel ? String(firstModelId) : undefined;
  }

  await bindFormApi.setValues({
    isNeedConfigDeviceModel,
    deviceThingModelDataId,
    _ioTPlatformProductId: productId ? String(productId) : undefined,
  });

  const formValues = await bindFormApi.getValues();
  console.log('表单值设置完成，当前表单值:', formValues);
  console.log(
    '表单值中的 _ioTPlatformProductId:',
    formValues._ioTPlatformProductId,
  );
};

// 发送指令
const sendCommand = async (property: ThingModelProperty) => {
  // 检查是否为只读属性
  if (property.accessMode === 'r') {
    Message.warning('该属性为只读模式，无法发送指令');
    return;
  }

  // 只写模式（w）和读写模式（rw）都可以发送指令，这里不需要额外检查

  if (!property.commandValue || property.commandValue.trim() === '') {
    Message.warning('请输入指令内容');
    return;
  }

  const identifier = getPropertyIdentifier(property);
  if (!identifier) {
    Message.warning('该属性缺少标准物模型标识符，无法发送指令');
    return;
  }

  property.loading = true;
  try {
    const commandContent: Record<string, any> = {};
    // 根据数据类型转换值
    const convertedValue = convertValueByType(
      property.commandValue.trim(),
      property.ioTPlatformRawFieldDataType,
    );
    commandContent[identifier] = convertedValue;

    const result = await postAggregationDeviceDeviceCommandForApiAsync({
      body: {
        id: commandRow.value.id,
        commandContent,
      },
    });

    if (result.data) {
      // 确保 result 始终是字符串类型
      property.result =
        typeof result.data === 'string'
          ? result.data
          : (result.data
            ? JSON.stringify(result.data)
            : '指令下发成功');
      Message.success('指令下发成功');
    } else {
      property.result = '指令下发失败';
      Message.error('指令下发失败');
    }
  } catch (error) {
    console.error('指令发送失败:', error);
    property.result = `指令下发失败: ${(error as Error).message}`;
    Message.error('指令下发失败');
  } finally {
    property.loading = false;
  }
};

/** EMQX 异步抄读轮询：间隔与最大等待时长。后端"待应答"标记存活 60s，这里略短于它即可 */
const EMQX_READ_POLL_INTERVAL_MS = 1000;
const EMQX_READ_POLL_TIMEOUT_MS = 50_000;

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

/**
 * 抄读一批属性，屏蔽平台差异，统一返回 { 标准物模型标识符: 值 }。
 *
 * OneNET 有平台侧属性快照，可同步查询直接返回；
 * EMQX 只是 broker、没有这种快照，只能下发 property/get 等设备 get_reply 应答，
 * 故走"发起拿 RequestId → 轮询取结果"两步。两者返回的键值对形状一致，调用方无需关心。
 */
const readPropertyValues = async (
  deviceId: string,
  platform: number,
  propertyList: Record<string, string>,
): Promise<Record<string, any> | null> => {
  if (platform !== IOT_PLATFORM_EMQX) {
    const result = await postAggregationDeviceGetDevicePropertyValueForApiAsync({
      body: { id: deviceId, propertyList },
    });
    return (result.data as any) ?? null;
  }

  const issued = await postAggregationDeviceIssueDevicePropertyReadForApiAsync({
    body: { id: deviceId, propertyList },
  });
  // requestId 是雪花 Id，后端刻意包在对象里以 JSON 字符串返回：
  // 裸 string 返回会走 text/plain，axios 会把纯数字体 JSON.parse 成 number，
  // 而雪花远超 Number.MAX_SAFE_INTEGER，精度会当场丢失
  const requestId = (issued.data as any)?.requestId;
  if (!requestId) {
    throw new Error('发起抄读失败，未获取到抄读关联 Id');
  }

  // 设备应答要经过 MQTT 往返 + EMQX 规则推送回服务端，必然有延迟，故轮询而非等待单次返回
  const deadline = Date.now() + EMQX_READ_POLL_TIMEOUT_MS;
  while (Date.now() < deadline) {
    await sleep(EMQX_READ_POLL_INTERVAL_MS);

    const polled =
      await postAggregationDeviceGetDevicePropertyReadResultForApiAsync({
        body: { id: deviceId, requestId },
      });

    // 设备尚未应答时后端返回 null，继续轮询
    if (polled.data !== undefined && polled.data !== null) {
      return polled.data as any;
    }
  }

  throw new Error('抄读超时，设备未在规定时间内应答');
};

// 抄读数据
const readData = async (property: ThingModelProperty) => {
  // 检查是否为只写属性
  if (property.accessMode === 'w') {
    Message.warning('该属性为只写模式，无法抄读数据');
    return;
  }

  const identifier = getPropertyIdentifier(property);
  if (!identifier) {
    Message.warning('该属性缺少标准物模型标识符，无法抄读数据');
    return;
  }

  if (!commandRow.value.id) {
    Message.warning('设备ID不存在，无法抄读数据');
    return;
  }

  property.loading = true;
  try {
    const result = await readPropertyValues(
      String(commandRow.value.id),
      Number(commandRow.value.ioTPlatform),
      { [identifier]: '' },
    );

    if (result !== undefined && result !== null) {
      // 返回结果是一个对象，需要根据属性名获取对应的值
      const propertyValue = result[identifier];
      if (propertyValue !== undefined && propertyValue !== null) {
        property.result =
          typeof propertyValue === 'string'
            ? propertyValue
            : JSON.stringify(propertyValue);
        Message.success('抄读数据成功');
      } else {
        property.result = '未获取到数据';
        Message.warning('未获取到该属性的数据');
      }
    } else {
      property.result = '抄读数据失败';
    }
  } catch (error: any) {
    console.error('抄读数据失败:', error);

    // 优先从后端返回的标准错误格式中获取具体错误信息
    const backendMessage =
      error?.response?.data?.error?.message ??
      error?.data?.error?.message ??
      error?.error?.message;

    const finalMessage =
      backendMessage && typeof backendMessage === 'string'
        ? backendMessage
        : (error as Error).message || '抄读数据失败';

    // 在表格中展示详细错误（保留一处，不额外弹第二个气泡）
    property.result = `抄读数据失败: ${finalMessage}`;
    // 不再调用 Message.error，避免与全局请求拦截器重复弹窗
  } finally {
    property.loading = false;
  }
};

// 批量抄读数据
const submitBatchRead = async () => {
  // 获取所有选中的行，排除只写模式（w）的属性
  const selectedProperties = thingModelProperties.value.filter(
    (prop) =>
      prop.selected && getPropertyIdentifier(prop) && prop.accessMode !== 'w', // 只写模式不能抄读
  );

  if (selectedProperties.length === 0) {
    Message.warning('请至少选择一个可读属性进行抄读（只写属性无法抄读）');
    return;
  }

  if (!commandRow.value.id) {
    Message.warning('设备ID不存在，无法抄读数据');
    return;
  }

  // 获取所有选中属性的字段名列表
  const propertyList = selectedProperties.reduce<Record<string, string>>(
    (acc, prop) => {
      const identifier = getPropertyIdentifier(prop);
      if (identifier) {
        acc[identifier] = '';
      }
      return acc;
    },
    {},
  );

  try {
    commandModalApi.setState({ loading: true, confirmLoading: false });

    // 设置所有选中属性为加载状态
    selectedProperties.forEach((prop) => {
      prop.loading = true;
    });

    // 调用批量抄读接口（EMQX 走异步抄读，内部自行发起 + 轮询）
    const result = await readPropertyValues(
      String(commandRow.value.id),
      Number(commandRow.value.ioTPlatform),
      propertyList,
    );

    if (result !== undefined && result !== null) {
      // 更新所有选中行的返回结果
      selectedProperties.forEach((prop) => {
        const identifier = getPropertyIdentifier(prop);
        const propertyValue = identifier ? result[identifier] : undefined;
        if (propertyValue !== undefined && propertyValue !== null) {
          prop.result =
            typeof propertyValue === 'string'
              ? propertyValue
              : JSON.stringify(propertyValue);
        } else {
          prop.result = '未获取到数据';
        }
        prop.loading = false;
      });
      Message.success(`成功抄读 ${selectedProperties.length} 个属性`);
    } else {
      selectedProperties.forEach((prop) => {
        prop.result = '抄读数据失败';
        prop.loading = false;
      });
      Message.error('抄读数据失败');
    }
  } catch (error) {
    console.error('批量抄读失败:', error);
    selectedProperties.forEach((prop) => {
      prop.result = `抄读数据失败: ${(error as Error).message}`;
      prop.loading = false;
    });
    Message.error('批量抄读失败');
  } finally {
    commandModalApi.setState({ loading: false, confirmLoading: false });
  }
};

// 绑定设备端物模型提交逻辑
async function submitBindDeviceThingModel() {
  const formValues = await bindFormApi.getValues();

  // 开关关闭时，直接提交无需选择物模型
  const isNeedConfig = !!formValues.isNeedConfigDeviceModel;
  const deviceThingModelDataId = formValues.deviceThingModelDataId as
    | string
    | undefined;

  if (isNeedConfig && !deviceThingModelDataId) {
    Message.error('请先选择设备端物模型');
    return;
  }

  if (!bindRows.value || bindRows.value.length === 0) {
    Message.error('请选择要绑定的设备');
    return;
  }

  // 获取所有选中设备的ID
  const deviceIds = bindRows.value.map((row) => row.id).filter(Boolean); // 过滤掉空值

  if (deviceIds.length === 0) {
    Message.error('选中的设备中没有有效的设备ID');
    return;
  }

  try {
    bindModalApi.setState({ loading: true, confirmLoading: true });
    const result = await postAggregationDeviceBindingDeviceThingModel({
      body: {
        devieDataIdList: deviceIds,
        isNeedConfigDeviceModel: isNeedConfig,
        deviceThingModelDataId: isNeedConfig ? deviceThingModelDataId : null,
      },
    });

    if (result.data) {
      Message.success(`成功绑定 ${deviceIds.length} 个设备的设备端物模型`);
      bindModalApi.close();
      bindRows.value = [];
      gridApi.reload();
    } else {
      Message.error('绑定设备端物模型失败');
    }
  } catch (error) {
    console.error('绑定设备端物模型失败:', error);
  } finally {
    bindModalApi.setState({ loading: false, confirmLoading: false });
  }
}

// 批量添加设备提交逻辑
async function submitBatchAdd() {
  const { valid } = await batchAddFormApi.validate();
  if (!valid) return;

  const formValues = await batchAddFormApi.getValues();

  // 处理设备地址列表
  const addressList = formValues.addressList
    .split('\n')
    .map((address: string) => address.trim())
    .filter((address: string) => address.length > 0);

  if (addressList.length === 0) {
    Message.error('请输入至少一个设备地址');
    return;
  }

  // 验证行数限制
  if (addressList.length > 1000) {
    Message.error('设备地址不能超过1000行');
    return;
  }

  // 验证设备地址格式（简单验证）
  const invalidAddresses = addressList.filter(
    (address: string) => !address || address.length < 3,
  );
  if (invalidAddresses.length > 0) {
    Message.error(`以下设备地址格式不正确: ${invalidAddresses.join(', ')}`);
    return;
  }

  // 获取产品ID（兼容新旧字段名）
  const ioTPlatformProductId =
    formValues.ioTPlatformProductId ||
    formValues.oneNETProductId ||
    formValues.ctWingProductId;

  if (!ioTPlatformProductId) {
    Message.error('请选择产品');
    return;
  }

  const batchCreateParams = {
    addressList,
    ioTPlatform: formValues.ioTPlatform,
    ioTPlatformProductId,
    EncryptionType: formValues.EncryptionType,
  };

  try {
    batchAddModalApi.setState({ loading: true, confirmLoading: true });
    const resp = await postAggregationDeviceBatchCreateAsync({
      body: batchCreateParams,
    });
    if (resp.data) {
      Message.success(`批量添加设备成功，共添加 ${addressList.length} 个设备`);
      batchAddModalApi.close();
      batchAddFormApi.resetForm();
      gridApi.reload();
    } else {
      Message.error('批量添加设备失败');
    }
  } catch (error) {
    console.error('批量添加设备失败:', error);
    Message.error('批量添加设备失败');
  } finally {
    batchAddModalApi.setState({ loading: false, confirmLoading: false });
  }
}

// 批量同步OneNET/CTWing 设备（优先筛选条件，其次取表格数据）
async function handleBatchSync() {
  const EMQX = 3;

  // 读取复选框选中的设备地址
  const gridInstance = gridApi.grid as any;
  const selectedRows: any[] = gridInstance?.getCheckboxRecords?.() ?? [];
  const selectedAddresses: string[] = selectedRows
    .map((row: any) => row.deviceAddress)
    .filter(Boolean);

  // 优先读取筛选表单中的值
  const filterValues = await gridApi.formApi.getValues();
  let ioTPlatform: number | undefined = undefined;
  let ioTPlatformProductId: string | undefined = undefined;

  // 筛选条件中的平台
  const filterPlatform = filterValues.ioTPlatform;

  // 判断平台：优先筛选条件
  if (filterPlatform !== undefined && filterPlatform !== null && filterPlatform !== '') {
    const fp = Number(filterPlatform);
    if (fp === EMQX) {
      Message.warning('批量同步不支持 EMQX 平台，仅支持 OneNET 和 CTWing');
      return;
    }
    ioTPlatform = fp;
    ioTPlatformProductId = filterValues.ioTPlatformProductId;
  }

  // 平台或产品未从筛选获取到，从选中设备或表格数据补全
  if (!ioTPlatform || !ioTPlatformProductId) {
    // 优先用选中设备推导
    const sourceRows: any[] = selectedRows.length > 0
      ? selectedRows
      : (() => {
          const tableData = gridInstance?.getTableData?.();
          return tableData?.fullData || tableData?.visibleData || [];
        })();

    // 收集源数据中所有有效平台（排除 EMQX）
    const platforms = new Set<number>();
    for (const row of sourceRows) {
      const rp = Number(row.ioTPlatform);
      if (rp && rp !== EMQX) {
        platforms.add(rp);
        if (!ioTPlatformProductId && row.ioTPlatformProductId) {
          ioTPlatformProductId = row.ioTPlatformProductId;
        }
      }
    }

    if (platforms.size === 0) {
      Message.warning('列表中无 OneNET 或 CTWing 设备，请先筛选产品');
      return;
    }
    if (platforms.size > 1) {
      Message.warning('列表中包含多个平台的设备，请先筛选为单一平台后再同步');
      return;
    }

    ioTPlatform = ioTPlatform || [...platforms][0];
  }

  if (!ioTPlatformProductId) {
    Message.warning('未找到产品信息，请先在筛选条件中选择产品');
    return;
  }

  try {
    // 整屏遮罩，避免用户重复点击或误操作；后端为异步丢弃，提交后即返回
    batchSyncLoading.value = true;
    pageLoading.value = true;
    loadingTip.value = '正在提交批量同步任务...';

    const body: any = { ioTPlatform: ioTPlatform!, ioTPlatformProductId };
    if (selectedAddresses.length > 0) {
      body.deviceAddresses = selectedAddresses;
    }
    const resp = await postAggregationDeviceBatchSyncDeviceFromOneNetAsync({ body });
    if (resp.data) {
      // 后端已在后台逐个处理，接口不等待执行完毕，友好提示稍后刷新查看结果
      Message.success(
        '批量同步任务已提交，正在后台执行，请稍后刷新列表查看同步结果',
      );
    } else {
      Message.error('批量同步任务提交失败');
    }
  } catch (error) {
    console.error('批量同步任务提交失败:', error);
    Message.error('批量同步任务提交失败');
  } finally {
    batchSyncLoading.value = false;
    pageLoading.value = false;
    loadingTip.value = '缓存刷新中...';
  }
}

// 打开批量添加模态框
const openBatchAddModal = () => {
  console.log('打开批量添加模态框');
  batchAddFormApi.resetForm();
  addressLines.value = 0; // 重置行数
  batchAddModalApi.open();
};

// 批量添加地址行数
const addressLines = ref(0);

// 检查行数是否超过限制
const isOverLineLimit = computed(() => {
  return addressLines.value > 1000;
});

// 监听表单值变化，实时更新行数
watch(
  () => batchAddFormApi.getValues()?.addressList,
  (newValue) => {
    try {
      console.log('监听地址列表变化:', newValue);
      if (!newValue || typeof newValue !== 'string') {
        addressLines.value = 0;
        return;
      }
      const lines = newValue.split('\n').filter((line: string) => line.trim());
      addressLines.value = lines.length;
      console.log('更新行数:', addressLines.value);
    } catch {
      addressLines.value = 0;
    }
  },
  { immediate: true },
);

// 监听表单值变化，实时更新行数（备用方案）
watch(
  () => batchAddFormApi.getValues(),
  (formValues) => {
    try {
      const addressList = formValues?.addressList;
      console.log('监听表单值变化:', addressList);
      if (!addressList || typeof addressList !== 'string') {
        addressLines.value = 0;
        return;
      }
      const lines = addressList
        .split('\n')
        .filter((line: string) => line.trim());
      addressLines.value = lines.length;
      console.log('备用方案更新行数:', addressLines.value);
    } catch {
      addressLines.value = 0;
    }
  },
  { deep: true, immediate: true },
);

// 使用定时器定期检查行数（第三种方案）
let lineCheckInterval: NodeJS.Timeout | null = null;

// 在模态框打开时启动定时器
const startLineCheck = () => {
  if (lineCheckInterval) {
    clearInterval(lineCheckInterval);
  }
  lineCheckInterval = setInterval(() => {
    try {
      // 尝试多种方式获取表单值
      let addressList = '';

      // 方式1：直接获取
      const formValues = batchAddFormApi.getValues();
      console.log('表单值:', formValues);
      addressList = formValues?.addressList || '';

      // 方式2：如果方式1失败，尝试其他方法
      if (!addressList) {
        try {
          const rawValues = batchAddFormApi.getValues();
          console.log('原始表单值:', rawValues);
          addressList = rawValues?.addressList || '';
        } catch (error) {
          console.log('方式2失败:', error);
        }
      }

      // 方式3：直接从DOM获取
      if (!addressList) {
        const textarea = document.querySelector(
          'textarea[name="addressList"]',
        ) as HTMLTextAreaElement;
        if (textarea) {
          addressList = textarea.value;
          console.log('从DOM获取的地址列表:', addressList);
        }
      }

      console.log('获取到的地址列表:', addressList);

      if (addressList && typeof addressList === 'string') {
        const lines = addressList
          .split('\n')
          .filter((line: string) => line.trim());
        const newLineCount = lines.length;
        console.log(
          '计算出的行数:',
          newLineCount,
          '当前行数:',
          addressLines.value,
        );
        if (newLineCount !== addressLines.value) {
          console.log('定时器更新行数:', newLineCount);
          addressLines.value = newLineCount;
        }
      } else {
        console.log('地址列表为空或不是字符串');
      }
    } catch (error) {
      console.error('定时器检查行数失败:', error);
    }
  }, 300);
};

// 在模态框关闭时清除定时器
const stopLineCheck = () => {
  if (lineCheckInterval) {
    clearInterval(lineCheckInterval);
    lineCheckInterval = null;
  }
};

// 缓存刷新按钮处理函数
const handleCacheRefresh = async () => {
  try {
    cacheRefreshLoading.value = true;
    pageLoading.value = true;
    loadingTip.value = '缓存刷新中...';
    const result = await postDeviceInfoCacheDeviceDataToRedis();
    if (result.data) {
      Message.success('缓存刷新成功');
      gridApi.reload();
    } else {
      Message.error('缓存刷新失败');
    }
  } catch (error) {
    console.error('缓存刷新失败:', error);
    Message.error('缓存刷新失败');
  } finally {
    cacheRefreshLoading.value = false;
    pageLoading.value = false;
    loadingTip.value = '缓存刷新中...';
  }
};

// 批量绑定设备端物模型
const openBatchBindModal = async () => {
  // 从表格获取选中的设备
  try {
    console.log('gridApi:', gridApi);
    console.log('gridApi.grid:', gridApi?.grid);

    // 尝试多种方式获取选中的行
    let checkboxRecords: any[] = [];

    // 方式1：通过 grid 获取
    if (gridApi?.grid) {
      console.log('grid 可用，尝试获取选中行');
      const gridInstance = gridApi.grid as any;

      // 尝试不同的方法名
      if (typeof gridInstance.getCheckboxRecords === 'function') {
        checkboxRecords = gridInstance.getCheckboxRecords();
        console.log('通过 grid.getCheckboxRecords 获取:', checkboxRecords);
      } else if (typeof gridInstance.getCheckboxReserveRecords === 'function') {
        checkboxRecords = gridInstance.getCheckboxReserveRecords();
        console.log(
          '通过 grid.getCheckboxReserveRecords 获取:',
          checkboxRecords,
        );
      }

      // 尝试直接访问内部属性
      if (checkboxRecords.length === 0 && gridInstance.checkboxRecords) {
        checkboxRecords = gridInstance.checkboxRecords;
        console.log('通过 grid.checkboxRecords 属性获取:', checkboxRecords);
      }

      // 尝试通过 store 获取
      if (checkboxRecords.length === 0 && gridInstance.$store) {
        const store = gridInstance.$store;
        if (store.checkboxRecords) {
          checkboxRecords = store.checkboxRecords;
          console.log(
            '通过 grid.$store.checkboxRecords 获取:',
            checkboxRecords,
          );
        }
      }
    }

    // 方式2：通过 gridApi.store 获取
    if (checkboxRecords.length === 0 && gridApi?.store) {
      const store = gridApi.store as any;
      if (store.checkboxRecords) {
        checkboxRecords = store.checkboxRecords;
        console.log(
          '通过 gridApi.store.checkboxRecords 获取:',
          checkboxRecords,
        );
      }
    }

    // 方式3：直接调用 gridApi 的方法
    if (
      checkboxRecords.length === 0 &&
      typeof (gridApi as any).getCheckboxRecords === 'function'
    ) {
      checkboxRecords = (gridApi as any).getCheckboxRecords();
      console.log('通过 gridApi.getCheckboxRecords 获取:', checkboxRecords);
    }

    console.log('最终获取到的选中设备:', checkboxRecords);
    console.log('选中设备数量:', checkboxRecords.length);

    if (!checkboxRecords || checkboxRecords.length === 0) {
      Message.warning('请先在表格中勾选要绑定的设备（点击每行前面的复选框）');
      return;
    }

    // 验证所有选中的设备必须是同一种产品
    const productIds = checkboxRecords
      .map((row) => row.ioTPlatformProductId)
      .filter(Boolean); // 过滤掉空值

    if (productIds.length === 0) {
      Message.error('选中的设备中没有有效的产品ID，无法进行绑定');
      return;
    }

    // 检查产品ID是否一致
    const uniqueProductIds = [...new Set(productIds)];
    if (uniqueProductIds.length > 1) {
      Message.error(
        `选中的设备包含 ${uniqueProductIds.length} 种不同的产品，请只选择同一种产品的设备进行批量绑定`,
      );
      return;
    }

    await openBindModal(checkboxRecords);
  } catch (error) {
    console.error('获取表格选中的设备失败:', error);
    console.error('错误详情:', error);
    Message.error('获取表格选中的设备失败，请确保已勾选设备');
  }
};

// ============ 批量转主站 ============
// 转主站仅支持 OneNET / EMQX（CTWing=1，OneNET=2，EMQX=3）
const MASTER_SWITCH_SUPPORTED_PLATFORMS = new Set([2, 3]);

const masterSwitchRows = ref<Array<Record<string, any>>>([]);
const masterStationOptions = ref<
  Array<{ ioTPlatform?: number; label: string; value: string }>
>([]);
const masterStationLoading = ref(false);
// 主用 / 备用主站一次性下发，设备侧主备表整体刷新；现场只有一个主站时允许两者选同一个
const selectedMasterAddress = ref<string | undefined>(undefined);
const selectedBackupMasterAddress = ref<string | undefined>(undefined);

// 统一获取表格勾选的设备（与批量绑定一致的多重兜底方式）
function getGridCheckboxRecords(): any[] {
  let checkboxRecords: any[] = [];
  if (gridApi?.grid) {
    const gridInstance = gridApi.grid as any;
    if (typeof gridInstance.getCheckboxRecords === 'function') {
      checkboxRecords = gridInstance.getCheckboxRecords();
    } else if (gridInstance.checkboxRecords) {
      checkboxRecords = gridInstance.checkboxRecords;
    }
  }
  if (checkboxRecords.length === 0 && gridApi?.store) {
    const store = gridApi.store as any;
    if (store.checkboxRecords) {
      checkboxRecords = store.checkboxRecords;
    }
  }
  return checkboxRecords || [];
}

// 将行的平台字段统一转为数值
function toPlatformNumber(raw: unknown): number | undefined {
  if (raw === undefined || raw === null || raw === '') {
    return undefined;
  }
  const n = typeof raw === 'string' ? Number.parseInt(raw, 10) : Number(raw);
  return Number.isNaN(n) ? undefined : n;
}

// 拉取主站下拉列表（固定两项：OneNET 静态地址、EMQX 配置地址）
async function fetchMasterStationOptions() {
  masterStationLoading.value = true;
  try {
    const { data } = await getAggregationDeviceGetMasterStationListForApiAsync();
    const list = Array.isArray(data) ? data : [];
    masterStationOptions.value = list
      .filter((m) => m?.masterStationAddress)
      .map((m) => ({
        label: `${m.masterName ?? ''}（${m.masterStationAddress}）`,
        value: String(m.masterStationAddress),
        ioTPlatform: toPlatformNumber(m.ioTPlatform),
      }));
  } catch (error) {
    console.error('获取主站下拉列表失败:', error);
    Message.error('获取主站下拉列表失败');
    masterStationOptions.value = [];
  } finally {
    masterStationLoading.value = false;
  }
}

// 按地址还原成后端要的主站对象（地址 + 平台类型），平台类型取自下拉项，不由前端猜
function resolveMasterStation(address: string | undefined) {
  if (!address) {
    return undefined;
  }
  const option = masterStationOptions.value.find((m) => m.value === address);
  if (!option || option.ioTPlatform === undefined) {
    return undefined;
  }
  return {
    masterStationAddress: address,
    ioTPlatform: option.ioTPlatform,
  };
}

const [MasterSwitchModal, masterSwitchModalApi] = useVbenModal({
  draggable: true,
  onConfirm: submitBatchMasterSwitch,
  onBeforeClose: () => {
    masterSwitchRows.value = [];
    selectedMasterAddress.value = undefined;
    selectedBackupMasterAddress.value = undefined;
    return true;
  },
  onOpenChange: async (isOpen: boolean) => {
    if (isOpen) {
      await fetchMasterStationOptions();
    }
  },
});

const masterSwitchModalState = masterSwitchModalApi.useStore();

// 打开批量转主站弹窗：校验同一平台、同一产品，且平台受支持
function openBatchMasterSwitchModal() {
  const checkboxRecords = getGridCheckboxRecords();
  if (!checkboxRecords || checkboxRecords.length === 0) {
    Message.warning(
      '请先在表格中勾选要切换主站的设备（点击每行前面的复选框）',
    );
    return;
  }

  // 校验：同一产品
  const productIds = checkboxRecords
    .map((r) => r.ioTPlatformProductId)
    .filter(Boolean);
  if (productIds.length === 0) {
    Message.error('选中的设备中没有有效的产品ID，无法切换主站');
    return;
  }
  if (new Set(productIds).size > 1) {
    Message.error('选中的设备包含多种产品，请只选择同一产品的设备切换主站');
    return;
  }

  // 校验：同一平台，且平台受支持（仅 OneNET / EMQX）
  const platforms = checkboxRecords
    .map((r) => toPlatformNumber(r.ioTPlatform))
    .filter((p): p is number => p !== undefined);
  const uniquePlatforms = [...new Set(platforms)];
  if (uniquePlatforms.length > 1) {
    Message.error('选中的设备包含多个物联网平台，请只选择同一平台的设备');
    return;
  }
  if (
    uniquePlatforms.length === 0 ||
    !MASTER_SWITCH_SUPPORTED_PLATFORMS.has(uniquePlatforms[0]!)
  ) {
    Message.warning('批量切换主站仅支持 OneNET / EMQX 平台的设备');
    return;
  }

  masterSwitchRows.value = checkboxRecords;
  selectedMasterAddress.value = undefined;
  selectedBackupMasterAddress.value = undefined;
  masterSwitchModalApi.open();
}

// 提交批量转主站：下发后返回失败地址集合，做汇总提示
async function submitBatchMasterSwitch() {
  const rows = masterSwitchRows.value;
  if (!rows || rows.length === 0) {
    Message.warning('没有选中的设备');
    return;
  }
  const masterStation = resolveMasterStation(selectedMasterAddress.value);
  if (!masterStation) {
    Message.warning('请选择主用主站');
    return;
  }

  const backupMasterStation = resolveMasterStation(
    selectedBackupMasterAddress.value,
  );
  if (!backupMasterStation) {
    Message.warning('请选择备用主站');
    return;
  }

  const addressList = rows.map((r) => r.deviceAddress).filter(Boolean);
  if (addressList.length === 0) {
    Message.error('选中的设备缺少设备地址，无法切换主站');
    return;
  }

  const firstRow = rows[0]!;
  const ioTPlatform = toPlatformNumber(firstRow.ioTPlatform);

  try {
    masterSwitchModalApi.setState({ loading: true, confirmLoading: true });
    const { data } =
      await postAggregationDeviceDeviceBatchMasterSwitchForApiAsync({
        body: {
          addressList,
          ioTPlatform,
          ioTPlatformProductId: String(firstRow.ioTPlatformProductId),
          masterStation,
          backupMasterStation,
        },
      });

    const failedList = Array.isArray(data) ? data : [];
    if (failedList.length === 0) {
      Message.success(`批量切换主站指令已下发，共 ${addressList.length} 个设备`);
      masterSwitchModalApi.close();
      gridApi.reload();
    } else {
      Modal.warning({
        title: '部分设备切换主站下发失败',
        content: `成功 ${addressList.length - failedList.length} 个，失败 ${failedList.length} 个。失败设备地址：${failedList.join('、')}`,
      });
    }
  } catch (error) {
    console.error('批量切换主站失败:', error);
    Message.error('批量切换主站失败');
  } finally {
    masterSwitchModalApi.setState({ loading: false, confirmLoading: false });
  }
}

// ---------- 批量删除 ----------
// 批量删除仅支持 OneNET（后端 CTWing 未实现、EMQX 无平台注册信息，与单删行为一致）
const BATCH_DELETE_SUPPORTED_PLATFORMS = new Set([IOT_PLATFORM_ONENET]);
const BATCH_DELETE_MAX = 1000;
// 后端逐台删除并限流（并发5、每台间隔300ms），实测满额1000台约96秒（≈10.4台/秒，
// 每个并发槽周期≈300ms间隔+180ms平台响应与本地删除），已远超全局默认的60秒超时，
// 故本接口单独放宽到120秒（仅覆盖此请求，不影响其他接口）。
// 后续若调整后端限流参数或平台响应变慢，需同步复核此值。
const BATCH_DELETE_TIMEOUT_MS = 120_000;

// 批量删除弹窗实时行数（与批量添加一致，界面显示"共 N 行设备地址"）
const batchDeleteAddressLines = ref(0);
const isBatchDeleteOverLimit = computed(
  () => batchDeleteAddressLines.value > BATCH_DELETE_MAX,
);
function countAddressLines(raw: unknown): number {
  if (!raw || typeof raw !== 'string') return 0;
  return raw.split('\n').filter((line: string) => line.trim()).length;
}

const [BatchDeleteForm, batchDeleteFormApi] = useVbenForm({
  collapsed: false,
  commonConfig: {
    labelWidth: 110,
    componentProps: {
      class: 'w-4/5',
    },
  },
  layout: 'horizontal',
  schema: batchDeleteDeviceFormSchema.value,
  showCollapseButton: false,
  showDefaultActions: false,
  wrapperClass: 'grid-cols-1',
  handleValuesChange: async (values, changedFields) => {
    // 账号切换时清空产品，避免残留跨账号产品Id
    if (changedFields.includes('ioTPlatformAccountId')) {
      await batchDeleteFormApi.setFieldValue('ioTPlatformProductId', undefined);
    }
    // 地址文本变化时实时刷新行数
    if (changedFields.includes('addressList')) {
      batchDeleteAddressLines.value = countAddressLines(values.addressList);
    }
  },
});

const [BatchDeleteModal, batchDeleteModalApi] = useVbenModal({
  draggable: true,
  onConfirm: submitBatchDeleteFromForm,
  onBeforeClose: () => {
    batchDeleteFormApi.resetForm();
    batchDeleteAddressLines.value = 0;
    return true;
  },
});

const batchDeleteModalState = batchDeleteModalApi.useStore();

// 统一的批量删除执行：调接口 → 按返回失败地址集合汇总提示 → reload
async function executeBatchDelete(
  addressList: string[],
  ioTPlatform: number,
  ioTPlatformProductId: string,
  setLoading: (loading: boolean) => void,
): Promise<boolean> {
  try {
    setLoading(true);
    const { data } = await postAggregationDeviceBatchDeleteAsync({
      body: {
        addressList,
        ioTPlatform,
        ioTPlatformProductId,
      },
      // 批量删除耗时随台数线性增长，单独放宽超时，避免满额删除被全局60秒超时打断
      timeout: BATCH_DELETE_TIMEOUT_MS,
    } as any);

    const failedList = Array.isArray(data) ? data : [];
    const total = addressList.length;
    if (failedList.length === 0) {
      Message.success(`批量删除成功，共 ${total} 个设备`);
    } else {
      Modal.warning({
        title: '部分设备删除失败',
        content: `成功 ${total - failedList.length} 个，失败 ${failedList.length} 个。失败设备地址：${failedList.join('、')}`,
      });
    }
    gridApi.reload();
    return failedList.length === 0;
  } catch (error) {
    console.error('批量删除设备失败:', error);
    Message.error('批量删除设备失败');
    return false;
  } finally {
    setLoading(false);
  }
}

// 工具栏「批量删除」入口：有勾选走选中行；无勾选打开地址输入弹窗
function handleBatchDelete() {
  const checkboxRecords = getGridCheckboxRecords();
  if (!checkboxRecords || checkboxRecords.length === 0) {
    // 未勾选任何设备 → 打开手动输入地址弹窗（同批量新增交互）
    batchDeleteFormApi.resetForm();
    batchDeleteAddressLines.value = 0;
    batchDeleteModalApi.open();
    return;
  }

  // 勾选删除：校验同一产品
  const productIds = checkboxRecords
    .map((r) => r.ioTPlatformProductId)
    .filter(Boolean);
  if (productIds.length === 0) {
    Message.error('选中的设备中没有有效的产品ID，无法删除');
    return;
  }
  if (new Set(productIds).size > 1) {
    Message.error('选中的设备包含多种产品，请只选择同一产品的设备删除');
    return;
  }

  // 校验：同一平台，且平台受支持（仅 OneNET）
  const platforms = checkboxRecords
    .map((r) => toPlatformNumber(r.ioTPlatform))
    .filter((p): p is number => p !== undefined);
  const uniquePlatforms = [...new Set(platforms)];
  if (uniquePlatforms.length > 1) {
    Message.error('选中的设备包含多个物联网平台，请只选择同一平台的设备');
    return;
  }
  if (
    uniquePlatforms.length === 0 ||
    !BATCH_DELETE_SUPPORTED_PLATFORMS.has(uniquePlatforms[0]!)
  ) {
    Message.warning('批量删除仅支持 OneNET 平台的设备');
    return;
  }

  const addressList = checkboxRecords
    .map((r) => r.deviceAddress)
    .filter(Boolean);
  if (addressList.length === 0) {
    Message.error('选中的设备缺少设备地址，无法删除');
    return;
  }

  const ioTPlatform = uniquePlatforms[0]!;
  const ioTPlatformProductId = String(productIds[0]);

  Modal.confirm({
    title: '确认批量删除选中设备？',
    content: `将删除选中的 ${addressList.length} 个设备，删除后不可恢复。`,
    okText: '删除',
    okType: 'danger',
    cancelText: $t('common.cancel'),
    onOk: async () => {
      await executeBatchDelete(
        addressList,
        ioTPlatform,
        ioTPlatformProductId,
        (loading) => {
          pageLoading.value = loading;
          loadingTip.value = '批量删除中...';
        },
      );
    },
  });
}

// 弹窗手动输入地址删除：切分地址 → 校验 ≤1000 → 取平台/产品 → 执行删除
async function submitBatchDeleteFromForm() {
  const { valid } = await batchDeleteFormApi.validate();
  if (!valid) return;

  const formValues = await batchDeleteFormApi.getValues();

  const addressList = String(formValues.addressList ?? '')
    .split('\n')
    .map((address: string) => address.trim())
    .filter((address: string) => address.length > 0);

  if (addressList.length === 0) {
    Message.error('请输入至少一个设备地址');
    return;
  }
  if (addressList.length > BATCH_DELETE_MAX) {
    Message.error(`设备地址不能超过${BATCH_DELETE_MAX}行`);
    return;
  }

  const ioTPlatform = toPlatformNumber(formValues.ioTPlatform);
  if (ioTPlatform === undefined) {
    Message.error('请选择物联网平台');
    return;
  }
  if (!BATCH_DELETE_SUPPORTED_PLATFORMS.has(ioTPlatform)) {
    Message.warning('批量删除仅支持 OneNET 平台的设备');
    return;
  }

  const ioTPlatformProductId = formValues.ioTPlatformProductId;
  if (!ioTPlatformProductId) {
    Message.error('请选择产品');
    return;
  }

  const ok = await executeBatchDelete(
    addressList,
    ioTPlatform,
    String(ioTPlatformProductId),
    (loading) =>
      batchDeleteModalApi.setState({ loading, confirmLoading: loading }),
  );
  if (ok) {
    batchDeleteModalApi.close();
  }
}

// 工具栏按钮配置
const toolbarActions = computed(() => [
  {
    label: $t('common.add'),
    type: 'primary',
    icon: 'ant-design:plus-outlined',
    onClick: openAddModal.bind(null),
    auth: ['AbpIdentity.Users.Create'],
  },
  {
    label: '批量添加',
    type: 'primary',
    icon: 'ant-design:plus-outlined',
    onClick: openBatchAddModal.bind(null),
    auth: ['AbpIdentity.Users.Create'],
  },
  {
    label: '批量同步',
    type: 'primary',
    icon: batchSyncLoading.value
      ? 'ant-design:loading-outlined'
      : 'ant-design:sync-outlined',
    onClick: handleBatchSync,
    disabled: batchSyncLoading.value,
    auth: ['AbpIdentity.Users.Create'],
  },
  {
    label: '批量绑定设备端物模型',
    type: 'default',
    icon: 'ant-design:link-outlined',
    onClick: openBatchBindModal,
    auth: ['AbpIdentity.Users.Create'],
  },
  {
    label: '批量升级',
    type: 'default',
    icon: 'ant-design:link-outlined',
    onClick: openBatchUpgradeModal,
    auth: ['AbpIdentity.Users.Create'],
  },
  {
    label: '批量转主站',
    type: 'default',
    icon: 'ant-design:swap-outlined',
    onClick: openBatchMasterSwitchModal,
    auth: ['AbpIdentity.Users.Create'],
  },
  {
    label: '批量删除',
    type: 'primary',
    danger: true,
    icon: 'ant-design:delete-outlined',
    onClick: handleBatchDelete,
    auth: ['AbpIdentity.Users.Delete'],
  },
  {
    label: cacheRefreshLoading.value
      ? $t('common.loading')
      : $t('abp.IoTDBBase.CacheRefresh'),
    type: 'default',
    icon: cacheRefreshLoading.value
      ? 'ant-design:loading-outlined'
      : 'ant-design:reload-outlined',
    onClick: handleCacheRefresh,
    disabled: cacheRefreshLoading.value,
    style: {
      backgroundColor: '#52c41a',
      borderColor: '#52c41a',
    },
  },
]);

// ============ 设备数据流转配置（设备级覆盖产品配置） ============
const dataFlowRow = ref<Record<string, any>>({});
const dataFlowOverride = ref(false);
const dataFlowNeedPush = ref(false);
const dataFlowRules = ref<DataPushRule[]>([]);

// 打开设备数据流转配置弹窗
function openDataFlowModal(record: any) {
  if (!record.id) {
    Message.error('设备数据ID不存在，无法进行数据流转配置');
    return;
  }
  dataFlowRow.value = record;
  dataFlowOverride.value = record.dataPushOverride ?? false;
  dataFlowNeedPush.value = record.isNeedPushData ?? false;
  dataFlowRules.value = Array.isArray(record.dataPushRules)
    ? record.dataPushRules
    : [];
  deviceDataFlowModalApi.open();
}

// 提交设备数据流转配置
async function submitDeviceDataFlowConfig() {
  if (!dataFlowRow.value.id) {
    Message.error('设备数据ID不存在，无法进行数据流转配置');
    return;
  }
  const override = dataFlowOverride.value;
  const needPush = dataFlowNeedPush.value;
  const rules = dataFlowRules.value ?? [];

  // 仅在开启覆盖且需要推送时校验规则
  if (override && needPush) {
    if (rules.length === 0) {
      Message.error('请至少选择要推送的数据类型');
      return;
    }
    const invalid = rules.some(
      (r) => r.enabled && (!r.pushServer || !r.pushInfo),
    );
    if (invalid) {
      Message.error('启用的推送规则必须填写服务地址与推送路径/KEY');
      return;
    }
  }

  try {
    const resp = await postDeviceInfoDeviceDataFlowConfig({
      body: {
        id: dataFlowRow.value.id,
        dataPushOverride: override,
        isNeedPushData: needPush,
        // 不覆盖时清空设备级规则，运行时将继承所属产品配置
        dataPushRules: override ? rules : [],
      },
    });
    if (resp.data) {
      Message.success('数据流转配置成功');
      deviceDataFlowModalApi.close();
      gridApi.reload();
    } else {
      Message.error('数据流转配置失败');
    }
  } catch {
    Message.error('数据流转配置失败');
  }
}

const [DeviceDataFlowModal, deviceDataFlowModalApi] = useVbenModal({
  draggable: true,
  footer: true,
  showCancelButton: true,
  showConfirmButton: true,
  onConfirm: submitDeviceDataFlowConfig,
});
</script>

<template>
  <Page auto-content-height>
    <Loading :loading="pageLoading" :tip="loadingTip" />
    <Grid>
      <template #toolbar-actions>
        <TableAction :actions="toolbarActions" />
      </template>

      <template #isArchiveStatus="{ row }">
        {{
          row.archiveStatus ? $t('common.Issued') : $t('common.Undistributed')
        }}
      </template>
      <template #isTripState="{ row }">
        {{ row.tripState ? $t('common.SwitchOff') : $t('common.Closing') }}
      </template>
      <template #isHaveValve="{ row }">
        <component :is="h(Tag, { color: row.haveValve ? 'green' : 'red' }, () =>
          row.haveValve ? $t('common.yes') : $t('common.no'),
        )
          " />
      </template>
      <template #isSelfDevelop="{ row }">
        <component :is="h(Tag, { color: row.selfDevelop ? 'green' : 'red' }, () =>
          row.selfDevelop ? $t('common.yes') : $t('common.no'),
        )
          " />
      </template>
      <template #isDynamicPassword="{ row }">
        <component :is="h(Tag, { color: row.dynamicPassword ? 'green' : 'red' }, () =>
          row.dynamicPassword ? $t('common.yes') : $t('common.no'),
        )
          " />
      </template>
      <template #isEnable="{ row }">
        <component :is="h(Tag, { color: row.enabled ? 'green' : 'red' }, () =>
          row.enabled ? $t('common.yes') : $t('common.no'),
        )
          " />
      </template>
      <template #ioTPlatformName="{ row }">
        <span :style="{
          color:
            row.ioTPlatform === 2 || row.ioTPlatform === '2'
              ? '#0B34BE'
              : '#048CD1',
          fontWeight: 'bold',
        }">
          {{ row.ioTPlatformName }}
        </span>
      </template>
      <template #readingMode="{ row }">
        <component :is="h(Tag, { color: row.readingMode === 1 ? 'green' : 'orange' }, () =>
          row.readingMode === 1 ? '标准模式' : '特殊模式',
        )
          " />
      </template>
      <template #deviceOnlineStatusName="{ row }">
        <component :is="h(Tag, { color: row.deviceOnlineStatus === 1 ? 'green' : row.deviceOnlineStatus === 2 ? 'red' : 'default' }, () =>
          row.deviceOnlineStatusName || '-',
        )
          " />
      </template>
      <template #lifeInfoName="{ row }">
        <component :is="h(Tag, { color: Number(row.lifeInfo) === DEVICE_STOPPED_LIFE_INFO ? 'red' : 'green' }, () =>
          row.lifeInfoName || '-',
        )
          " />
      </template>
      <template #encryptionTypeName="{ row }">
        <component :is="h(Tag, { color: getEncryptionTypeTagColor(row.encryptionType) }, () =>
          row.encryptionTypeName || '-',
        )
          " />
      </template>

      <template #action="{ row }">
        <div style="display: flex; gap: 8px; align-items: center">
          <!-- 编辑按钮已注释，因为后端没有更新接口 -->
          <!-- <Button size="small" type="link" @click="onEdit.bind(null, row)()">
            {{ $t('common.edit') }}
          </Button> -->
          <Button size="small" type="link" @click="toDeviceInfoData.bind(null, row)()">
            {{ $t('abp.deviceInfos.viewData') }}
          </Button>
          <Button size="small" type="link" @click="openCommandModal.bind(null, row)()">
            设备操作
          </Button>
          <Button size="small" type="link" @click="openBindModal.bind(null, row)()">
            绑定物模型
          </Button>
          <Popover trigger="hover" placement="bottomRight" :overlay-style="{ minWidth: '132px' }">
            <template #content>
              <div style="display: flex; flex-direction: column; gap: 4px">
                <Button type="text" size="small" @click="openServiceCallModal.bind(null, row)()"
                  style="padding: 4px 8px; text-align: left">
                  服务调用
                </Button>
                <Button type="text" size="small" @click="openUpgradeModal.bind(null, row)()"
                  style="padding: 4px 8px; text-align: left">
                  设备升级
                </Button>
                <Button type="text" size="small" @click="toDeviceLog.bind(null, row)()"
                  style="padding: 4px 8px; text-align: left">
                  {{ $t('abp.IoTDBBase.PlatformLog') }}
                </Button>
                <Button type="text" size="small" @click="toTelemetryLog.bind(null, row)()"
                  style="padding: 4px 8px; text-align: left">
                  {{ $t('abp.IoTDBBase.TelemetryLog') }}
                </Button>
                <Button type="text" size="small" @click="repushDeviceInfo.bind(null, row)()"
                  style="padding: 4px 8px; text-align: left">
                  重推设备信息
                </Button>
                <Button v-if="canRecoverToOneNET(row)" type="text" size="small"
                  @click="recoverDeviceToOneNET.bind(null, row)()"
                  style="padding: 4px 8px; color: #1677ff; text-align: left">
                  改投 OneNET
                </Button>
                <Button type="text" size="small" @click="openDataFlowModal.bind(null, row)()"
                  style="padding: 4px 8px; text-align: left">
                  数据流转配置
                </Button>
                <Button type="text" size="small" @click="onDel.bind(null, row)()"
                  style="padding: 4px 8px; color: #ff4d4f; text-align: left">
                  {{ $t('common.delete') }}
                </Button>
              </div>
            </template>
            <Button size="small" type="link">
              <template #icon>
                <Icon icon="ant-design:more-outlined" />
              </template>
            </Button>
          </Popover>
        </div>
      </template>
    </Grid>
    <UserModal :title="editRow.id ? $t('common.edit') : $t('common.add')" class="w-[800px]">
      <component :is="editRow.id ? EditForm : AddForm" />
    </UserModal>
    <DeviceDataFlowModal
      :title="`数据流转配置 - ${dataFlowRow.deviceName || dataFlowRow.deviceAddress || ''}`"
      class="w-[900px]"
    >
      <div class="space-y-3">
        <div class="flex items-center gap-2">
          <span class="w-24 flex-shrink-0 text-sm text-gray-600">覆盖产品配置</span>
          <Switch
            v-model:checked="dataFlowOverride"
            checked-children="是"
            un-checked-children="否"
          />
          <span class="text-xs text-gray-400">
            开启后使用本设备的推送配置，关闭则继承所属产品配置
          </span>
        </div>
        <div class="flex items-center gap-2">
          <span class="w-24 flex-shrink-0 text-sm text-gray-600">是否推送数据</span>
          <Switch
            v-model:checked="dataFlowNeedPush"
            :disabled="!dataFlowOverride"
            checked-children="是"
            un-checked-children="否"
          />
        </div>
        <DataPushRuleEditor
          v-model:rules="dataFlowRules"
          :disabled="!dataFlowOverride"
        />
      </div>
    </DeviceDataFlowModal>
    <CommandModal
      :title="`设备操作 - ${commandRow.deviceName || commandRow.deviceAddress || ''}`"
      class="w-[1200px]"
    >
      <div v-if="thingModelLoading" style="padding: 40px; text-align: center">
        <Loading :loading="true" tip="加载物模型属性中..." />
      </div>
      <div v-else>
        <div style="margin-bottom: 16px">
          <Input v-model:value="filterKeyword" placeholder="请输入标准物模型属性名称或平台物模型属性标识符进行筛选" allow-clear
            style="width: 100%">
            <template #prefix>
              <Icon icon="ant-design:search-outlined" />
            </template>
          </Input>
        </div>
        <Table :columns="commandTableColumns" :data-source="filteredThingModelProperties" :pagination="false"
          :scroll="{ x: 1000, y: 500 }" row-key="id" size="small" />
      </div>
      <template #footer>
        <div class="flex w-full items-center justify-end gap-2">
          <Button @click="commandModalApi.close()">
            {{ $t('common.cancel') }}
          </Button>
          <Button danger :loading="deviceEnableLoading" @click="handleDeviceEnable">
            启停
          </Button>
          <Button danger @click="openUpdatePasswordModal">
            更新密码
          </Button>
          <Button
            :loading="switchEncryptionTypeLoading"
            @click="handleSwitchEncryptionType"
          >
            切换鉴权方式
          </Button>
          <Button type="default" :loading="commandModalState?.loading" @click="submitBatchRead">
            批量抄读
          </Button>
          <Button type="primary" :loading="commandModalState?.loading" @click="submitBatchCommand">
            批量发送指令
          </Button>
        </div>
      </template>
    </CommandModal>
    <UpdatePasswordModal class="w-[480px]">
      <div class="space-y-3">
        <div class="text-sm text-gray-500">
          设备：{{ commandRow.deviceName || commandRow.deviceAddress || '-' }}
        </div>
        <div class="flex items-center gap-2">
          <span class="w-20 flex-shrink-0 text-right text-sm text-gray-600">新密码</span>
          <Input.Password
            v-model:value="updatePasswordSecKey"
            allow-clear
            class="min-w-0 flex-1"
            maxlength="32"
            placeholder="留空则由服务端自动生成"
          />
        </div>
        <div class="text-xs text-gray-400">
          设备接入鉴权密码明文，长度不超过32个字符；留空时由服务端自动生成。
        </div>
      </div>
    </UpdatePasswordModal>
    <ServiceCallModal
      :title="`服务调用 - ${serviceCallRow.deviceName || serviceCallRow.deviceAddress || ''}`"
      class="w-[480px] max-w-[92vw]"
    >
      <div v-if="serviceCallLoading" class="py-10 text-center">
        <Loading :loading="true" tip="加载物模型服务中..." />
      </div>
      <div v-else class="flex max-h-[min(72vh,520px)] flex-col gap-2">
        <div
          class="truncate text-xs text-gray-500"
          :title="String(serviceCallRow.ioTPlatformProductId || '')"
        >
          产品 Id：{{ serviceCallRow.ioTPlatformProductId || '-' }}
        </div>
        <div class="flex items-center gap-2">
          <span class="w-14 flex-shrink-0 text-right text-sm text-gray-600">服务</span>
          <Select
            v-model:value="selectedServiceIndex"
            :options="serviceCallSelectOptions"
            allow-clear
            class="min-w-0 flex-1"
            placeholder="请选择"
            size="small"
          />
        </div>
        <template v-if="serviceParamFields.length > 0">
          <div class="text-sm font-medium text-gray-700">参数</div>
          <div
            class="max-h-[min(42vh,280px)] space-y-2 overflow-y-auto pr-0.5"
          >
            <div
              v-for="f in serviceParamFields"
              :key="f.name"
              class="flex items-center gap-2"
            >
              <span
                class="w-[5.5rem] flex-shrink-0 truncate text-right text-sm text-gray-600"
                :title="f.label"
              >{{ f.label }}</span>
              <DatePicker
                v-if="shouldUseStartTimePickerForField(f)"
                show-time
                format="YYYY-MM-DD HH:mm:ss"
                class="min-w-0 flex-1"
                size="small"
                allow-clear
                placeholder="选择起始时标"
                :value="parseServiceCallStartTimeValue(serviceCallFormValues[f.name])"
                @update:value="onServiceCallStartTimeChange(f.name, $event)"
              />
              <Select
                v-else-if="shouldUseBreakerOperateTypeSelectForField(f)"
                v-model:value="serviceCallFormValues[f.name]"
                :options="breakerOperateTypeOptions"
                allow-clear
                class="min-w-0 flex-1"
                size="small"
                placeholder="请选择拉合闸类型"
              />
              <Select
                v-else-if="shouldUseValveCommandTypeSelectForField(f)"
                v-model:value="serviceCallFormValues[f.name]"
                :options="valveCommandTypeOptions"
                :loading="valveCommandTypeLoading"
                allow-clear
                class="min-w-0 flex-1"
                size="small"
                placeholder="请选择阀控类型"
              />
              <Input
                v-else
                v-model:value="serviceCallFormValues[f.name]"
                allow-clear
                class="min-w-0 flex-1"
                size="small"
                :placeholder="f.name"
              />
            </div>
          </div>
        </template>
        <div
          v-else-if="selectedServiceIndex != null && serviceCallOptions.length > 0"
          class="text-xs text-gray-500"
        >
          当前服务无参数（thirdValue / secondValue 为空或无法解析）
        </div>
        <div v-if="serviceCallResultText" class="mt-2 flex min-h-0 flex-shrink-0 flex-col gap-1">
          <div class="text-sm font-medium text-gray-700">调用结果</div>
          <div
            class="max-h-[min(28vh,200px)] overflow-auto rounded border px-2 py-2 text-xs leading-relaxed"
            :class="
              serviceCallResultOk
                ? 'border-green-200 bg-green-50 text-gray-800'
                : 'border-red-200 bg-red-50 text-red-800'
            "
          >
            <pre class="m-0 whitespace-pre-wrap break-words font-sans">{{ serviceCallResultText }}</pre>
          </div>
        </div>
      </div>
      <template #footer>
        <div class="flex w-full items-center justify-end gap-2">
          <Button @click="serviceCallModalApi.close()">
            {{ $t('common.cancel') }}
          </Button>
          <Button
            type="primary"
            :loading="serviceCallModalState?.loading"
            :disabled="serviceCallLoading"
            @click="submitDeviceServiceCall"
          >
            调用服务
          </Button>
        </div>
      </template>
    </ServiceCallModal>
    <UpgradeModal title="设备升级" class="w-[600px]">
      <UpgradeForm />
    </UpgradeModal>
    <BindModal :title="`绑定设备端物模型${bindRows.length > 0 ? ` (已选择 ${bindRows.length} 个设备)` : ''}`" class="w-[600px]">
      <div v-if="bindRows.length > 0" class="mb-4 rounded border border-blue-200 bg-blue-50 p-3">
        <div class="mb-2 text-sm font-medium text-blue-800">
          已选择 {{ bindRows.length }} 个设备：
        </div>
        <div class="max-h-32 overflow-y-auto text-xs text-blue-600">
          <div v-for="(row, index) in bindRows" :key="row.id || index" class="mb-1">
            <div>
              {{ index + 1 }}.
              {{ row.deviceName || row.deviceAddress || row.id }}
            </div>
            <div class="ml-4 text-[11px] text-blue-700">
              当前是否需要配置设备模型：
              {{ row.isNeedConfigDeviceModel ? '是' : '否' }}
              <span class="mx-1">|</span>
              当前已绑定设备端物模型：
              {{ row.deviceThingModelName || '未绑定' }}
            </div>
          </div>
        </div>
      </div>
      <BindForm />
    </BindModal>
    <BatchAddModal title="批量添加设备" class="w-[800px]">
      <BatchAddForm />
      <template #footer>
        <div class="flex w-full items-center justify-between">
          <div class="text-sm text-gray-500">
            <span v-if="addressLines > 0">
              共 {{ addressLines }} 行设备地址
              <span v-if="isOverLineLimit" style="color: #ff4d4f">
                (超过1000行限制)
              </span>
            </span>
            <span v-else> 当前行数: {{ addressLines }} (调试信息) </span>
          </div>
          <div class="flex gap-2">
            <Button @click="batchAddModalApi.close()">
              {{ $t('common.cancel') }}
            </Button>
            <Button type="primary" :loading="batchAddModalState?.confirmLoading" @click="submitBatchAdd"
              :disabled="isOverLineLimit">
              {{ $t('common.confirm') }}
            </Button>
          </div>
        </div>
      </template>
    </BatchAddModal>
    <BatchDeleteModal title="批量删除设备" class="w-[800px]">
      <div class="mb-2 text-sm" style="color: #ff4d4f">
        未勾选设备时按此处输入的地址删除；仅支持 OneNET 平台，最多 1000
        个，删除后不可恢复。
      </div>
      <BatchDeleteForm />
      <template #footer>
        <div class="flex w-full items-center justify-between">
          <div class="text-sm text-gray-500">
            <span v-if="batchDeleteAddressLines > 0">
              共 {{ batchDeleteAddressLines }} 行设备地址
              <span v-if="isBatchDeleteOverLimit" style="color: #ff4d4f">
                (超过{{ BATCH_DELETE_MAX }}行限制)
              </span>
            </span>
          </div>
          <div class="flex gap-2">
            <Button @click="batchDeleteModalApi.close()">
              {{ $t('common.cancel') }}
            </Button>
            <Button
              danger
              type="primary"
              :disabled="isBatchDeleteOverLimit"
              :loading="batchDeleteModalState?.confirmLoading"
              @click="submitBatchDeleteFromForm"
            >
              {{ $t('common.confirm') }}
            </Button>
          </div>
        </div>
      </template>
    </BatchDeleteModal>
    <BatchUpgradeModal title="批量升级设备" class="w-[800px]">
      <BatchUpgradeForm />
      <template #footer>
        <div class="flex w-full items-center justify-between">
          <div class="text-sm text-gray-500">
            <span v-if="batchUpgradeAddressLines > 0">
              共 {{ batchUpgradeAddressLines }} 个设备地址
            </span>
            <span v-else> 当前设备地址数: {{ batchUpgradeAddressLines }} </span>
          </div>
          <div class="flex gap-2">
            <Button @click="batchUpgradeModalApi.close()">
              {{ $t('common.cancel') }}
            </Button>
            <Button type="primary" :loading="batchUpgradeModalState?.confirmLoading" @click="submitBatchUpgrade">
              {{ $t('common.confirm') }}
            </Button>
          </div>
        </div>
      </template>
    </BatchUpgradeModal>
    <MasterSwitchModal title="批量切换主站" class="w-[560px]">
      <div class="space-y-4 p-2">
        <div class="text-sm text-gray-500">
          已选设备：<span class="font-medium text-gray-700">{{
            masterSwitchRows.length
          }}</span>
          个
        </div>
        <div>
          <div class="mb-2 text-sm">
            主用主站 <span class="text-red-500">*</span>
          </div>
          <Select
            v-model:value="selectedMasterAddress"
            class="w-full"
            placeholder="请选择设备优先接入的主站"
            :options="masterStationOptions"
            :loading="masterStationLoading"
            allow-clear
          />
        </div>
        <div>
          <div class="mb-2 text-sm">
            备用主站 <span class="text-red-500">*</span>
          </div>
          <Select
            v-model:value="selectedBackupMasterAddress"
            class="w-full"
            placeholder="请选择主用主站不可用时回落的主站"
            :options="masterStationOptions"
            :loading="masterStationLoading"
            allow-clear
          />
        </div>
        <div class="text-xs text-gray-400">
          主用与备用主站一次性下发，设备侧主备主站表整体刷新；现场只有一个主站时，两者可选同一个。
          指令下发成功不代表设备已完成切换，最终以设备上报的主站切换事件为准。
        </div>
      </div>
      <template #footer>
        <div class="flex w-full items-center justify-end gap-2">
          <Button @click="masterSwitchModalApi.close()">
            {{ $t('common.cancel') }}
          </Button>
          <Button
            type="primary"
            :loading="masterSwitchModalState?.confirmLoading"
            :disabled="!selectedMasterAddress || !selectedBackupMasterAddress"
            @click="submitBatchMasterSwitch"
          >
            {{ $t('common.confirm') }}
          </Button>
        </div>
      </template>
    </MasterSwitchModal>
  </Page>
</template>
