import { TIMEOUTS } from '../../utils/timeouts';
import { Page, Locator } from '@playwright/test';
import { selectRadixOption } from '@d8d/e2e-test-utils';

/**
 * 订单状态常量
 */
export const ORDER_STATUS = {
  DRAFT: 'draft',
  CONFIRMED: 'confirmed',
  IN_PROGRESS: 'in_progress',
  COMPLETED: 'completed',
} as const;

/**
 * 订单状态类型
 */
export type OrderStatus = typeof ORDER_STATUS[keyof typeof ORDER_STATUS];

/**
 * 订单状态显示名称映射
 */
export const ORDER_STATUS_LABELS: Record<OrderStatus, string> = {
  draft: '草稿',
  confirmed: '已确认',
  in_progress: '进行中',
  completed: '已完成',
} as const;

/**
 * 工作状态常量
 */
export const WORK_STATUS = {
  NOT_WORKING: 'not_working',
  PRE_WORKING: 'pre_working',
  WORKING: 'working',
  RESIGNED: 'resigned',
} as const;

/**
 * 工作状态类型
 */
export type WorkStatus = typeof WORK_STATUS[keyof typeof WORK_STATUS];

/**
 * 工作状态显示名称映射
 */
export const WORK_STATUS_LABELS: Record<WorkStatus, string> = {
  not_working: '未入职',
  pre_working: '已入职',
  working: '工作中',
  resigned: '已离职',
} as const;

/**
 * 订单数据接口
 */
export interface OrderData {
  /** 订单名称 */
  name: string;
  /** 预计开始日期 */
  expectedStartDate?: string;
  /** 平台ID */
  platformId?: number;
  /** 平台名称 */
  platformName?: string;
  /** 公司ID */
  companyId?: number;
  /** 公司名称 */
  companyName?: string;
  /** 渠道ID */
  channelId?: number;
  /** 渠道名称 */
  channelName?: string;
  /** 订单状态 */
  status?: OrderStatus;
  /** 工作状态 */
  workStatus?: WorkStatus;
}

/**
 * 订单人员数据接口
 */
export interface OrderPersonData {
  /** 残疾人ID */
  disabledPersonId: number;
  /** 残疾人姓名 */
  disabledPersonName?: string;
  /** 入职日期 */
  hireDate?: string;
  /** 薪资 */
  salary?: number;
  /** 工作状态 */
  workStatus?: WorkStatus;
  /** 实际入职日期 */
  actualHireDate?: string;
  /** 离职日期 */
  resignDate?: string;
}

/**
 * 网络响应数据接口
 */
export interface NetworkResponse {
  /** 请求URL */
  url: string;
  /** 请求方法 */
  method: string;
  /** 响应状态码 */
  status: number;
  /** 是否成功 */
  ok: boolean;
  /** 响应头 */
  responseHeaders: Record<string, string>;
  /** 响应体 */
  responseBody: unknown;
}

/**
 * 表单提交结果接口
 */
export interface FormSubmitResult {
  /** 提交是否成功 */
  success: boolean;
  /** 是否有错误 */
  hasError: boolean;
  /** 是否有成功消息 */
  hasSuccess: boolean;
  /** 错误消息 */
  errorMessage?: string;
  /** 成功消息 */
  successMessage?: string;
  /** 网络响应列表 */
  responses?: NetworkResponse[];
}

/**
 * 订单管理 Page Object
 *
 * 用于订单管理功能的 E2E 测试
 * 页面路径: /admin/orders（待确认）
 *
 * @example
 * ```typescript
 * const orderPage = new OrderManagementPage(page);
 * await orderPage.goto();
 * await orderPage.createOrder({ name: '测试订单' });
 * ```
 */
export class OrderManagementPage {
  readonly page: Page;

  // ===== 页面级选择器 =====
  /** 页面标题 */
  readonly pageTitle: Locator;
  /** 新增订单按钮 */
  readonly addOrderButton: Locator;
  /** 订单列表表格 */
  readonly orderTable: Locator;
  /** 搜索输入框 */
  readonly searchInput: Locator;
  /** 搜索按钮 */
  readonly searchButton: Locator;

  constructor(page: Page) {
    this.page = page;

    // 初始化页面级选择器
    // 使用更精确的选择器来定位页面标题（避免与侧边栏按钮冲突）
    this.pageTitle = page.locator('[data-slot="card-title"]').getByText('订单管理', { exact: true });
    // 使用 data-testid 定位创建订单按钮（按钮文本是"创建订单"不是"新增订单"）
    this.addOrderButton = page.getByTestId('create-order-button');
    this.orderTable = page.locator('table');
    // 使用 data-testid 定位搜索输入框
    this.searchInput = page.getByTestId('search-order-name-input');
    // 使用 data-testid 定位搜索按钮
    this.searchButton = page.getByTestId('search-button');
  }

  // ===== 导航和基础验证 =====

  /**
   * 导航到订单管理页面
   */
  async goto() {
    await this.page.goto('/admin/orders');
    await this.page.waitForLoadState('domcontentloaded');
    // 等待页面标题出现
    await this.pageTitle.waitFor({ state: 'visible', timeout: TIMEOUTS.PAGE_LOAD });
    // 等待表格数据加载
    await this.page.waitForSelector('table tbody tr', { state: 'visible', timeout: TIMEOUTS.PAGE_LOAD_LONG });
    await this.expectToBeVisible();
  }

  /**
   * 验证页面关键元素可见
   */
  async expectToBeVisible() {
    await this.pageTitle.waitFor({ state: 'visible', timeout: TIMEOUTS.PAGE_LOAD });
    await this.addOrderButton.waitFor({ state: 'visible', timeout: TIMEOUTS.TABLE_LOAD });
  }

  // ===== 搜索和筛选功能 =====

  /**
   * 按订单名称搜索
   * @param name 订单名称
   */
  async searchByName(name: string) {
    await this.searchInput.fill(name);
    await this.searchButton.click();
    await this.page.waitForLoadState('networkidle');
    await this.page.waitForTimeout(TIMEOUTS.LONG);
  }

  /**
   * 打开高级筛选对话框
   */
  async openFilterDialog() {
    const filterButton = this.page.getByRole('button', { name: /筛选|高级筛选/ });
    await filterButton.click();
    await this.page.waitForSelector('[role="dialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG });
  }

  /**
   * 设置筛选条件
   * @param filters 筛选条件
   */
  async setFilters(filters: {
    status?: OrderStatus;
    workStatus?: WorkStatus;
    platformId?: number;
    platformName?: string;
    companyId?: number;
    companyName?: string;
    channelId?: number;
    channelName?: string;
    dateRange?: { start?: string; end?: string };
  }) {
    // 订单状态筛选
    if (filters.status) {
      const statusFilter = this.page.getByLabel(/订单状态/);
      await statusFilter.click();
      const statusLabel = ORDER_STATUS_LABELS[filters.status];
      await this.page.getByRole('option', { name: statusLabel }).click();
    }

    // 工作状态筛选
    if (filters.workStatus) {
      const workStatusFilter = this.page.getByLabel(/工作状态/);
      await workStatusFilter.click();
      const workStatusLabel = WORK_STATUS_LABELS[filters.workStatus];
      await this.page.getByRole('option', { name: workStatusLabel }).click();
    }

    // 平台筛选
    if (filters.platformName) {
      await selectRadixOption(this.page, '平台', filters.platformName);
    }

    // 公司筛选
    if (filters.companyName) {
      await selectRadixOption(this.page, '公司', filters.companyName);
    }

    // 渠道筛选
    if (filters.channelName) {
      await selectRadixOption(this.page, '渠道', filters.channelName);
    }

    // 日期范围筛选
    if (filters.dateRange) {
      if (filters.dateRange.start) {
        const startDateInput = this.page.getByLabel(/开始日期|起始日期/);
        await startDateInput.fill(filters.dateRange.start);
      }
      if (filters.dateRange.end) {
        const endDateInput = this.page.getByLabel(/结束日期|截止日期/);
        await endDateInput.fill(filters.dateRange.end);
      }
    }
  }

  /**
   * 应用筛选条件
   */
  async applyFilters() {
    const applyButton = this.page.getByRole('button', { name: /应用|确定|筛选/ });
    await applyButton.click();
    await this.page.waitForLoadState('networkidle');
    await this.page.waitForTimeout(TIMEOUTS.LONG);
  }

  /**
   * 清空筛选条件
   */
  async clearFilters() {
    const clearButton = this.page.getByRole('button', { name: /重置|清空/ });
    await clearButton.click();
    await this.page.waitForTimeout(TIMEOUTS.MEDIUM);
  }

  // ===== 订单 CRUD 操作 =====

  /**
   * 打开创建订单对话框
   */
  async openCreateDialog() {
    await this.addOrderButton.click();
    await this.page.waitForSelector('[role="dialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG });
  }

  /**
   * 打开编辑订单对话框
   * @param orderName 订单名称
   */
  async openEditDialog(orderName: string) {
    // 找到订单行并点击"打开菜单"按钮
    const orderRow = this.orderTable.locator('tbody tr').filter({ hasText: orderName }).first();
    const menuButton = orderRow.getByRole('button', { name: '打开菜单' });
    await menuButton.click();

    // 等待菜单出现并点击"编辑"选项
    // 使用 data-testid 或 role 定位编辑选项
    const editOption = this.page.getByRole('menuitem', { name: '编辑' });
    await editOption.waitFor({ state: 'visible', timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT });
    await editOption.click();

    // 等待编辑对话框出现
    await this.page.waitForSelector('[role="dialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG });
  }

  /**
   * 打开删除确认对话框
   * @param orderName 订单名称
   */
  async openDeleteDialog(orderName: string) {
    // 找到订单行并点击"打开菜单"按钮（与编辑操作相同的模式）
    const orderRow = this.orderTable.locator('tbody tr').filter({ hasText: orderName }).first();
    const menuButton = orderRow.getByRole('button', { name: '打开菜单' });
    await menuButton.click();

    // 等待菜单出现并点击"删除"选项
    const deleteOption = this.page.getByRole('menuitem', { name: '删除' });
    await deleteOption.waitFor({ state: 'visible', timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT });
    await deleteOption.click();

    // 等待删除确认对话框出现
    await this.page.waitForSelector('[role="alertdialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG });
  }

  /**
   * 填写订单表单
   * @param data 订单数据
   */
  async fillOrderForm(data: OrderData) {
    // 等待表单出现
    await this.page.waitForSelector('form', { state: 'visible', timeout: TIMEOUTS.DIALOG });

    // 填写订单名称
    if (data.name) {
      await this.page.getByLabel(/订单名称|名称/).fill(data.name);
    }

    // 选择平台（必须在公司之前选择，因为公司列表依赖平台）
    if (data.platformName) {
      await selectRadixOption(this.page, '平台', data.platformName);
    }

    // 选择公司
    if (data.companyName) {
      await selectRadixOption(this.page, '公司', data.companyName);
    }

    // 选择渠道
    if (data.channelName) {
      await selectRadixOption(this.page, '渠道', data.channelName);
    }

    // 填写预计开始日期
    if (data.expectedStartDate) {
      const dateInput = this.page.getByLabel(/预计开始日期|开始日期/);
      await dateInput.fill(data.expectedStartDate);
    }

    // 选择订单状态（如果是编辑模式）
    if (data.status) {
      const statusLabel = ORDER_STATUS_LABELS[data.status];
      await selectRadixOption(this.page, '订单状态', statusLabel);
    }

    // 选择工作状态（如果是编辑模式）
    if (data.workStatus) {
      const workStatusLabel = WORK_STATUS_LABELS[data.workStatus];
      await selectRadixOption(this.page, '工作状态', workStatusLabel);
    }

    // 创建订单时需要至少选择一名残疾人
    // 如果表单中有"选择残疾人"按钮，点击它并选择第一个可用的残疾人
    const selectPersonButton = this.page.getByRole('button', { name: '选择残疾人' });
    const hasSelectPersonButton = await selectPersonButton.count();
    if (hasSelectPersonButton > 0) {
      console.debug('[创建订单] 检测到需要选择残疾人，点击"选择残疾人"按钮');
      await selectPersonButton.click();
      await this.page.waitForTimeout(TIMEOUTS.MEDIUM);

      // 等待残疾人选择器对话框打开
      // 选择第一个可用的残疾人（通常是测试数据）
      // 尝试多种方式定位残疾人列表
      const firstCheckbox = this.page.locator('input[type="checkbox"]').first();
      const checkboxCount = await firstCheckbox.count();

      if (checkboxCount > 0) {
        // 使用第一个复选框
        await firstCheckbox.click();
        console.debug('[创建订单] 已选择第一个残疾人');

        // 查找确认按钮并点击（可能是"确定"、"确认"等）
        const confirmButton = this.page.getByRole('button', { name: /^(确定|确认|选择)$/ });
        const confirmCount = await confirmButton.count();
        if (confirmCount > 0) {
          await confirmButton.first().click();
          console.debug('[创建订单] 已确认选择残疾人');
        } else {
          // 如果没有确认按钮，尝试按 Enter 键
          await this.page.keyboard.press('Enter');
          console.debug('[创建订单] 按 Enter 键确认选择');
        }
      } else {
        console.debug('[创建订单] 未找到残疾人复选框，尝试其他方式');

        // 尝试查找残疾人列表项并点击第一个
        const firstPersonItem = this.page.locator('[role="option"], .option-item, .person-item').first();
        const itemCount = await firstPersonItem.count();
        if (itemCount > 0) {
          await firstPersonItem.click();
          console.debug('[创建订单] 已点击第一个残疾人选项');
        } else {
          // 如果还是没有，尝试关闭对话框并继续（有些实现可能有默认选择）
          console.debug('[创建订单] 未找到残疾人选项，尝试关闭对话框并继续');
          await this.page.keyboard.press('Escape');
        }
      }

      await this.page.waitForTimeout(TIMEOUTS.MEDIUM);
    } else {
      console.debug('[创建订单] 未检测到"选择残疾人"按钮，可能已有人选或不在创建模式');
    }
  }

  /**
   * 提交表单
   * @returns 表单提交结果
   */
  async submitForm(): Promise<FormSubmitResult> {
    // 收集网络响应
    const responses: NetworkResponse[] = [];

    // 监听所有网络请求
    const responseHandler = async (response: Response) => {
      const url = response.url();
      // 监听订单管理相关的 API 请求
      if (url.includes('/orders') || url.includes('order')) {
        const _requestBody = response.request()?.postData();
        const responseBody = await response.text().catch(() => '');
        let jsonBody = null;
        try {
          jsonBody = JSON.parse(responseBody);
        } catch {
          // 不是 JSON 响应
        }

        responses.push({
          url,
          method: response.request()?.method() ?? 'UNKNOWN',
          status: response.status(),
          ok: response.ok(),
          responseHeaders: await response.allHeaders().catch(() => ({})),
          responseBody: jsonBody || responseBody,
        });
      }
    };

    this.page.on('response', responseHandler);

    try {
      // 点击提交按钮（创建或更新）
      const submitButton = this.page.getByRole('button', { name: /^(创建|更新|保存)$/ });
      await submitButton.click();

      // 等待网络请求完成（使用较宽松的超时，因为有些操作可能不触发网络请求）
      try {
        await this.page.waitForLoadState('domcontentloaded', { timeout: TIMEOUTS.DIALOG });
      } catch {
        // domcontentloaded 超时不是致命错误，继续检查 Toast 消息
        console.debug('domcontentloaded 超时，继续检查 Toast 消息');
      }
    } finally {
      // 确保监听器总是被移除，防止内存泄漏
      this.page.off('response', responseHandler);
    }

    // 等待 Toast 消息显示
    await this.page.waitForTimeout(TIMEOUTS.VERY_LONG);

    // 检查 Toast 消息
    const errorToast = this.page.locator('[data-sonner-toast][data-type="error"]');
    const successToast = this.page.locator('[data-sonner-toast][data-type="success"]');

    const hasError = await errorToast.count() > 0;
    const hasSuccess = await successToast.count() > 0;

    let errorMessage: string | null = null;
    let successMessage: string | null = null;

    if (hasError) {
      errorMessage = await errorToast.first().textContent();
    }
    if (hasSuccess) {
      successMessage = await successToast.first().textContent();
    }

    return {
      success: hasSuccess || (!hasError && !hasSuccess),
      hasError,
      hasSuccess,
      errorMessage: errorMessage ?? undefined,
      successMessage: successMessage ?? undefined,
      responses,
    };
  }

  /**
   * 取消对话框
   */
  async cancelDialog() {
    const cancelButton = this.page.getByRole('button', { name: '取消' });
    await cancelButton.click();
    await this.waitForDialogClosed();
  }

  /**
   * 等待对话框关闭
   */
  async waitForDialogClosed() {
    // 先等待一段时间让对话框有机会关闭
    await this.page.waitForTimeout(TIMEOUTS.LONG);

    // 检查是否还有对话框可见
    const dialogs = this.page.locator('[role="dialog"]');
    const dialogCount = await dialogs.count();

    if (dialogCount === 0) {
      // 没有对话框了，已经关闭
      console.debug('对话框已关闭（无对话框元素）');
      return;
    }

    // 尝试等待对话框隐藏或从 DOM 中移除
    try {
      await dialogs.first().waitFor({ state: 'hidden', timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT });
      console.debug('对话框已关闭');
    } catch {
      // 超时不是致命错误，对话框可能已经以其他方式关闭
      console.debug('对话框关闭等待超时，继续执行');
    }

    await this.page.waitForTimeout(TIMEOUTS.MEDIUM);
  }

  /**
   * 确认删除操作
   */
  async confirmDelete() {
    // 尝试多种可能的按钮名称
    const confirmButton = this.page.locator('[role="alertdialog"]').getByRole('button', {
      name: /^(确认删除|删除|确定|确认)$/
    });
    await confirmButton.click();
    // 等待确认对话框关闭和网络请求完成
    await this.page.waitForSelector('[role="alertdialog"]', { state: 'hidden', timeout: TIMEOUTS.DIALOG })
      .catch(() => console.debug('删除确认对话框关闭超时'));
    await this.page.waitForLoadState('networkidle', { timeout: TIMEOUTS.TABLE_LOAD });
    await this.page.waitForTimeout(TIMEOUTS.LONG);
  }

  /**
   * 取消删除操作
   */
  async cancelDelete() {
    // 先定位到 alertdialog，然后在其中查找取消按钮
    const dialog = this.page.locator('[role="alertdialog"]');
    const cancelButton = dialog.getByRole('button', { name: '取消' });
    await cancelButton.click();
    await this.page.waitForSelector('[role="alertdialog"]', { state: 'hidden', timeout: TIMEOUTS.DIALOG })
      .catch(() => console.debug('删除确认对话框关闭超时（取消操作）'));
  }

  /**
   * 验证订单是否存在
   * @param orderName 订单名称
   * @returns 订单是否存在
   */
  async orderExists(orderName: string): Promise<boolean> {
    const orderRow = this.orderTable.locator('tbody tr').filter({ hasText: orderName }).first();
    return (await orderRow.count()) > 0;
  }

  // ===== 订单详情 =====

  /**
   * 打开订单详情对话框
   * @param orderName 订单名称
   */
  async openDetailDialog(orderName: string) {
    // 找到订单行
    const orderRow = this.orderTable.locator('tbody tr').filter({ hasText: orderName }).first();

    // 先点击操作菜单触发按钮（"打开菜单" 或 MoreHorizontal 图标）
    const menuTrigger = orderRow.getByRole('button', { name: /打开菜单/ });
    await menuTrigger.click();

    // 等待菜单显示
    await this.page.waitForTimeout(TIMEOUTS.VERY_SHORT);

    // 点击"查看详情"菜单项
    const detailButton = this.page.getByRole('menuitem', { name: /查看详情/ });
    await detailButton.click();

    await this.page.waitForSelector('[role="dialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG });
  }


  /**
   * 获取订单详情对话框中的基本信息
   * @returns 订单基本信息
   */
  async getOrderDetailInfo(): Promise<{
    name?: string;
    status?: string;
    workStatus?: string;
    expectedStartDate?: string;
    platform?: string;
    company?: string;
    channel?: string;
  }> {
    const dialog = this.page.locator('[role="dialog"]');
    const result: Record<string, string | undefined> = {};

    // 使用 data-testid 直接定位元素（更可靠）
    // DOM 结构: <div className="flex items-center justify-between">
    //             <span className="text-sm font-medium">标签:</span>
    //             <span data-testid="order-detail-xxx">值</span>
    //           </div>
    
    // 订单名称 - 使用 data-testid
    const nameElement = dialog.locator('[data-testid="order-detail-name"]');
    if (await nameElement.count() > 0) {
      result.name = (await nameElement.textContent())?.trim();
    }

    // 订单状态
    const statusElement = dialog.locator('[data-testid="order-detail-status"]');
    if (await statusElement.count() > 0) {
      result.status = (await statusElement.textContent())?.trim();
    }

    // 工作状态 - 查找包含"工作状态"标签的行
    const workStatusRow = dialog.locator('div').filter({ hasText: /工作状态:/ }).first();
    if (await workStatusRow.count() > 0) {
      const workStatusElement = workStatusRow.locator('span').nth(1);
      result.workStatus = (await workStatusElement.textContent())?.trim();
    }

    // 预计开始日期 - 使用 data-testid
    const expectedStartDateElement = dialog.locator('[data-testid="order-detail-expected-start"]');
    if (await expectedStartDateElement.count() > 0) {
      result.expectedStartDate = (await expectedStartDateElement.textContent())?.trim();
    }

    // 平台 - 使用 data-testid
    const platformElement = dialog.locator('[data-testid="order-detail-platform"]');
    if (await platformElement.count() > 0) {
      result.platform = (await platformElement.textContent())?.trim();
    }

    // 公司 - 使用 data-testid
    const companyElement = dialog.locator('[data-testid="order-detail-company"]');
    if (await companyElement.count() > 0) {
      result.company = (await companyElement.textContent())?.trim();
    }

    // 渠道 - 使用 data-testid
    const channelElement = dialog.locator('[data-testid="order-detail-channel"]');
    if (await channelElement.count() > 0) {
      result.channel = (await channelElement.textContent())?.trim();
    }

    return result;
  }

  /**
   * 从订单详情对话框中获取关联人员列表
   * @returns 人员信息列表
   */
  async getPersonListFromDetail(): Promise<Array<{
    name?: string;
    workStatus?: string;
    hireDate?: string;
    salary?: string;
    phone?: string;
  }>> {
    const dialog = this.page.locator('[role="dialog"]');
    const result: Array<{ name?: string; workStatus?: string; hireDate?: string; salary?: string; phone?: string }> = [];

    // 查找所有表格，对话框中可能有两个表格：
    // 1. "待添加人员列表" - 临时表格，包含未确认的人员
    // 2. "绑定人员列表" - 实际已绑定到订单的人员
    // 我们需要第二个"绑定人员列表"表格
    const allTables = dialog.locator('table');
    const tableCount = await allTables.count();

    // 查找"绑定人员列表"表格（通常是包含"工作状态"列的表格）
    let personTable;
    for (let i = 0; i < tableCount; i++) {
      const table = allTables.nth(i);
      const tableText = await table.textContent();
      // 绑定人员列表表格包含"工作状态"列，而待添加人员列表没有
      if (tableText && tableText.includes('工作状态')) {
        personTable = table;
        break;
      }
    }

    const personList = dialog.locator('[class*="person"], [class*="employee"], [data-testid*="person"]');

    // 优先使用表格形式
    if (personTable) {
      const rows = personTable.locator('tbody tr');
      const rowCount = await rows.count();

      for (let i = 0; i < rowCount; i++) {
        const row = rows.nth(i);
        const cells = row.locator('td');
        const cellCount = await cells.count();

        const personInfo: { name?: string; workStatus?: string; hireDate?: string; salary?: string; phone?: string } = {};

        // 根据列数量和数据类型提取信息
        // 表格列：ID 姓名 性别 残疾类型 联系电话 入职日期 离职日期 工作状态 薪资
        for (let j = 0; j < cellCount; j++) {
          const cellText = await cells.nth(j).textContent();
          if (!cellText) continue;

          const trimmedText = cellText.trim();

          // 尝试识别列内容
          // ID 在第一列（j === 0），姓名在第二列（j === 1）
          if (j === 1 && trimmedText) {
            personInfo.name = trimmedText;
          }
          // 联系电话在第 5 列（j === 4），是 11 位数字
          if (j === 4 && /^\d{11}$/.test(trimmedText)) {
            personInfo.phone = trimmedText;
          }
          // 工作状态检查
          for (const [_statusValue, statusLabel] of Object.entries(WORK_STATUS_LABELS)) {
            if (trimmedText.includes(statusLabel)) {
              personInfo.workStatus = statusLabel;
              break;
            }
          }
          // 日期检查（符合日期格式）
          if (/^\d{4}-\d{2}-\d{2}$/.test(trimmedText) || /^\d{4}\/\d{2}\/\d{2}$/.test(trimmedText)) {
            if (!personInfo.hireDate) {
              personInfo.hireDate = trimmedText;
            }
          }
          // 薪资检查（在最后一列，包含数字且可能是薪资）
          // 薪资通常是较大的数字，不应该是11位电话号码
          if (j === cellCount - 1 && /^\d+(\.\d+)?$/.test(trimmedText.replace(/,/g, ''))) {
            const numValue = trimmedText.replace(/,/g, '');
            // 排除11位电话号码（如13800019729）
            if (numValue.length < 11) {
              personInfo.salary = trimmedText;
            }
          }
        }

        if (personInfo.name || personInfo.workStatus) {
          result.push(personInfo);
        }
      }
    } else if (await personList.count() > 0) {
      // 如果是列表形式而非表格
      const listItems = personList.locator('[class*="item"], [class*="row"], li, div');
      const itemCount = await listItems.count();

      for (let i = 0; i < itemCount; i++) {
        const item = listItems.nth(i);
        const itemText = await item.textContent();
        if (itemText && itemText.trim()) {
          result.push({ name: itemText.trim() });
        }
      }
    }

    return result;
  }

  /**
   * 从订单详情对话框中获取附件列表
   * @returns 附件信息列表
   */
  async getAttachmentListFromDetail(): Promise<Array<{
    fileName?: string;
    uploadDate?: string;
    uploader?: string;
  }>> {
    const dialog = this.page.locator('[role="dialog"]');
    const result: Array<{ fileName?: string; uploadDate?: string; uploader?: string }> = [];

    // 查找附件列表区域
    // 尝试多种可能的定位策略
    const attachmentTable = dialog.locator('table').filter({ hasText: /附件|文件/ });
    const attachmentList = dialog.locator('[class*="attachment"], [class*="file"], [data-testid*="attachment"]');

    // 优先使用表格形式
    if (await attachmentTable.count() > 0) {
      const rows = attachmentTable.locator('tbody tr');
      const rowCount = await rows.count();

      for (let i = 0; i < rowCount; i++) {
        const row = rows.nth(i);
        const cells = row.locator('td');
        const cellCount = await cells.count();

        const attachmentInfo: { fileName?: string; uploadDate?: string; uploader?: string } = {};

        for (let j = 0; j < cellCount; j++) {
          const cellText = await cells.nth(j).textContent();
          if (!cellText) continue;

          const trimmedText = cellText.trim();

          // 文件名通常在第一列
          if (j === 0 && trimmedText) {
            attachmentInfo.fileName = trimmedText;
          }
          // 日期检查
          if (/^\d{4}-\d{2}-\d{2}/.test(trimmedText) || /^\d{4}\/\d{2}\/\d{2}/.test(trimmedText)) {
            if (!attachmentInfo.uploadDate) {
              attachmentInfo.uploadDate = trimmedText;
            }
          }
          // 上传者通常是文本用户名
          if (j > 0 && trimmedText && !attachmentInfo.uploader && !attachmentInfo.uploadDate && !/^\d{4}/.test(trimmedText)) {
            attachmentInfo.uploader = trimmedText;
          }
        }

        if (attachmentInfo.fileName) {
          result.push(attachmentInfo);
        }
      }
    } else if (await attachmentList.count() > 0) {
      // 如果是列表形式
      const listItems = attachmentList.locator('[class*="item"], [class*="row"], li, div');
      const itemCount = await listItems.count();

      for (let i = 0; i < itemCount; i++) {
        const item = listItems.nth(i);
        const itemText = await item.textContent();
        if (itemText && itemText.trim()) {
          result.push({ fileName: itemText.trim() });
        }
      }
    }

    return result;
  }

  /**
   * 关闭订单详情对话框
   */
  async closeDetailDialog(): Promise<void> {
    // 尝试多种关闭方式
    // 方式1: 点击右上角 X 按钮
    const closeButton = this.page.locator('[role="dialog"]').getByRole('button', { name: '关闭' }).first();
    const closeButtonCount = await closeButton.count();

    if (closeButtonCount > 0) {
      await closeButton.click();
    } else {
      // 方式2: 点击取消按钮
      const cancelButton = this.page.locator('[role="dialog"]').getByRole('button', { name: '取消' }).first();
      const cancelButtonCount = await cancelButton.count();

      if (cancelButtonCount > 0) {
        await cancelButton.click();
      } else {
        // 方式3: 按 Escape 键
        await this.page.keyboard.press('Escape');
      }
    }

    // 等待对话框关闭
    await this.waitForDialogClosed();
  }

  // ===== 人员关联管理 =====

  /**
   * 打开人员管理对话框
   *
   * **使用场景:**
   * - **从订单列表页打开**: 传入 `orderName` 参数，方法会先找到对应订单行，再点击人员管理按钮
   * - **从订单详情页打开**: 不传参数，方法会直接点击页面中的人员管理按钮
   *
   * @param orderName 订单名称（可选）。从列表页打开时需要传入，从详情页打开时不传
   *
   * @example
   * ```typescript
   * // 从订单列表页打开
   * await orderPage.openPersonManagementDialog('测试订单');
   *
   * // 从订单详情页打开
   * await orderPage.openDetailDialog('测试订单');
   * await orderPage.openPersonManagementDialog();
   * ```
   */
  async openPersonManagementDialog(orderName?: string) {
    // 人员管理功能直接集成在订单详情对话框中
    // 如果提供了订单名称，打开订单详情对话框
    if (orderName) {
      await this.openDetailDialog(orderName);
    }
    // 人员管理功能已在详情对话框中，无需额外操作
  }

  /**
   * 添加人员到订单
   * @param personData 人员数据
   */
  async addPersonToOrder(personData: OrderPersonData) {
    // 点击添加人员按钮
    const addButton = this.page.getByRole('button', { name: /添加人员|新增人员/ });
    await addButton.click();
    await this.page.waitForTimeout(TIMEOUTS.SHORT);

    // 选择残疾人（支持通过名称选择）
    if (personData.disabledPersonName) {
      await selectRadixOption(this.page, '残疾人|选择残疾人', personData.disabledPersonName);
    } else if (personData.disabledPersonId) {
      // 如果只提供了 ID，尝试在对话框中选择第一个残疾人
      const firstCheckbox = this.page.locator('[role="dialog"]').locator('table tbody tr').first().locator('input[type="checkbox"]').first();
      try {
        await firstCheckbox.waitFor({ state: 'visible', timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT });
        await firstCheckbox.check();
      } catch {
        console.debug('没有可用的残疾人数据');
      }
    }

    // 填写入职日期
    if (personData.hireDate) {
      const hireDateInput = this.page.getByLabel(/入职日期/);
      await hireDateInput.fill(personData.hireDate);
    }

    // 填写薪资
    if (personData.salary !== undefined) {
      const salaryInput = this.page.getByLabel(/薪资|工资/);
      await salaryInput.fill(String(personData.salary));
    }

    // 选择工作状态
    if (personData.workStatus) {
      const workStatusLabel = WORK_STATUS_LABELS[personData.workStatus];
      await selectRadixOption(this.page, '工作状态', workStatusLabel);
    }

    // 提交
    const submitButton = this.page.getByRole('button', { name: /^(添加|确定|保存)$/ });
    await submitButton.click();

    await this.page.waitForLoadState('networkidle');
    await this.page.waitForTimeout(TIMEOUTS.LONG);
  }

  /**
   * 修改人员工作状态
   * @param personName 人员姓名
   * @param newStatus 新的工作状态
   */
  async updatePersonWorkStatus(personName: string, newStatus: WorkStatus) {
    const dialog = this.page.locator('[role="dialog"]');

    // 等待对话框完全加载
    await dialog.waitFor({ state: 'visible', timeout: TIMEOUTS.DIALOG });

    // 从 error-context.md 可知：
    // 1. 对话框中有"绑定人员列表"表格
    // 2. 表格列：ID 姓名 性别 残疾类型 联系电话 入职日期 离职日期 工作状态 薪资
    // 3. 工作状态列直接是 combobox，不需要点击编辑按钮

    // 查找所有表格
    const allTables = dialog.locator('table');
    const allTableCount = await allTables.count();
    console.debug(`对话框中总共有 ${allTableCount} 个表格`);

    let personTable = allTables.first();
    // 找到包含"绑定人员"或"工作状态"列的表格（第二个表格是绑定人员列表）
    for (let i = 0; i < allTableCount; i++) {
      const table = allTables.nth(i);
      const tableText = await table.textContent();
      if (tableText && (tableText.includes('绑定人员') || tableText.includes('工作状态'))) {
        personTable = table;
        console.debug(`找到人员表格（索引 ${i}）`);
        break;
      }
    }

    // 在表格中查找包含指定人员名称的行
    const targetRow = personTable.locator('tbody tr').filter({ hasText: personName }).first();
    const rowCount = await targetRow.count();
    console.debug(`找到 ${rowCount} 个匹配的人员行`);

    if (rowCount === 0) {
      throw new Error(`未找到人员 ${personName}`);
    }

    // 等待行可见
    await targetRow.waitFor({ state: 'visible', timeout: TIMEOUTS.DIALOG });

    // 从 error-context.md 可知，工作状态在单元格中是一个 combobox
    // 表格列：ID 姓名 性别 残疾类型 联系电话 入职日期 离职日期 工作状态 薪资
    // 工作状态是倒数第二列（薪资是最后一列）

    const cells = targetRow.locator('td');
    const cellCount = await cells.count();
    console.debug(`人员行有 ${cellCount} 个单元格`);

    // 工作状态在倒数第二列
    const workStatusCell = cells.nth(cellCount - 2);
    const workStatusCombobox = workStatusCell.getByRole('combobox');
    const comboboxCount = await workStatusCombobox.count();
    console.debug(`工作状态 combobox 数量: ${comboboxCount}`);

    if (comboboxCount === 0) {
      throw new Error(`未找到人员 ${personName} 的工作状态选择器`);
    }

    await workStatusCombobox.click({ timeout: TIMEOUTS.DIALOG });
    console.debug('工作状态 combobox 已点击');

    // 等待下拉选项显示
    await this.page.waitForTimeout(TIMEOUTS.MEDIUM);

    // 使用中文标签选择选项
    // 注意：UI 中的工作状态选项与 WORK_STATUS_LABELS 不同
    // UI 选项：未入职、已入职、工作中、已离职
    // WORK_STATUS_LABELS：未就业、待就业、已就业、已离职
    const statusMapping: Record<WorkStatus, string> = {
      not_working: '未入职',
      pre_working: '已入职',
      working: '工作中',
      resigned: '已离职',
    };
    const newWorkStatusLabel = statusMapping[newStatus];
    console.debug(`尝试选择状态: ${newWorkStatusLabel}`);

    const optionLocator = this.page.getByRole('option', { name: newWorkStatusLabel });
    const optionCount = await optionLocator.count();
    console.debug(`找到 ${optionCount} 个选项`);

    if (optionCount === 0) {
      throw new Error(`未找到工作状态选项: ${newWorkStatusLabel}`);
    }

    await optionLocator.first().click({ timeout: TIMEOUTS.DIALOG });
    console.debug(`工作状态已更新为: ${newWorkStatusLabel}`);

    // 使用较短的超时时间等待网络空闲
    await this.page.waitForLoadState('domcontentloaded', { timeout: TIMEOUTS.DIALOG })
      .catch(() => console.debug('domcontentloaded 等待超时，继续'));
    await this.page.waitForTimeout(TIMEOUTS.MEDIUM);
  }

  // ===== 附件管理 =====

  /**
   * 打开资源上传对话框
   * "资源上传"按钮在订单详情对话框中
   */
  async openAddAttachmentDialog() {
    // 使用"资源上传"按钮
    const attachmentButton = this.page.getByRole('button', { name: /资源上传/ });
    await attachmentButton.click();
    // 等待第二个对话框（资源上传对话框）打开
    await this.page.waitForTimeout(TIMEOUTS.MEDIUM);
  }

  /**
   * 上传附件
   *
   * 实际的 UI 流程：
   * 1. 在资源上传对话框中，点击人员行中对应文件类型的"上传文件"按钮
   * 2. 打开上传弹窗（第三个对话框）- 选择文件类型
   * 3. 在上传弹窗中，点击 FileSelector 的触发按钮
   * 4. 打开 FileSelector 对话框（第四个对话框）
   * 5. 在 FileSelector 对话框中使用 uploadFileToField 上传文件
   * 6. 点击上传后的文件进行选择
   * 7. 点击"确认选择"按钮
   * 8. 在上传弹窗中点击"提交"按钮
   *
   * @param personIdentifier 人员标识（可以是 ID 或姓名，方法会自动匹配）
   * @param fileName 文件名（相对于 web/tests/fixtures 目录）
   * @param mimeType 文件类型（默认为 image/jpeg，未使用，保留用于未来扩展）
   * @param fileType 文件类型（税务文件、薪资单、工作成果、合同签署、残疾证明、其他），默认为"其他"
   */
  async uploadAttachment(
    personIdentifier: string,
    fileName: string,
    _mimeType: string = 'image/jpeg',
    fileType: string = '其他'
  ) {
    // 动态导入 uploadFileToField 工具
    const { uploadFileToField } = await import('@d8d/e2e-test-utils');

    // 找到资源上传对话框（第二个对话框）
    const dialogs = this.page.locator('[role="dialog"]');
    const uploadDialog = dialogs.nth(1);

    // 在对话框中找到对应残疾人的行
    // 使用 ID 或姓名匹配
    const personRow = uploadDialog.locator('tr').filter({ hasText: personIdentifier });
    const rowCount = await personRow.count();

    if (rowCount === 0) {
      console.debug(`未找到人员 ${personIdentifier} 的行`);
      // 尝试打印所有行内容用于调试
      const allRows = uploadDialog.locator('tbody tr');
      const allRowCount = await allRows.count();
      console.debug(`资源上传对话框共有 ${allRowCount} 行`);
      for (let i = 0; i < Math.min(allRowCount, 3); i++) {
        const rowText = await allRows.nth(i).textContent();
        console.debug(`行 ${i} 内容:`, rowText);
      }
      return;
    }

    console.debug(`找到人员 ${personIdentifier} 的行`);

    // 在该人员行中找到对应文件类型的"上传文件"按钮
    // 文件类型列顺序：税务文件、薪资单、工作成果、合同签署、残疾证明、其他
    const uploadButton = personRow.getByRole('button', { name: '上传文件' });
    const buttonCount = await uploadButton.count();

    if (buttonCount === 0) {
      console.debug(`未找到"上传文件"按钮`);
      return;
    }

    console.debug(`找到 ${buttonCount} 个"上传文件"按钮`);

    // 根据文件类型选择对应的上传按钮
    let buttonIndex = 5; // 默认为"其他"（最后一个）
    switch (fileType) {
      case '税务文件':
        buttonIndex = 0;
        break;
      case '薪资单':
        buttonIndex = 1;
        break;
      case '工作成果':
        buttonIndex = 2;
        break;
      case '合同签署':
        buttonIndex = 3;
        break;
      case '残疾证明':
        buttonIndex = 4;
        break;
      case '其他':
      default:
        buttonIndex = 5;
        break;
    }

    // 点击对应的上传文件按钮，这会打开上传弹窗（第三个对话框）
    const targetButton = uploadButton.nth(buttonIndex);

    // 调试信息：检查按钮是否可见
    const isVisible = await targetButton.isVisible().catch(() => false);
    console.debug(`目标上传文件按钮可见性: ${isVisible}`);

    if (!isVisible) {
      console.debug(`上传文件按钮不可见，尝试滚动到视图`);
      await personRow.scrollIntoViewIfNeeded();
      await this.page.waitForTimeout(TIMEOUTS.SHORT);
    }

    await targetButton.click();
    console.debug(`已点击第 ${buttonIndex} 个上传文件按钮`);

    // 等待上传弹窗打开（第三个对话框）
    await this.page.waitForTimeout(TIMEOUTS.MEDIUM);

    // 在上传弹窗中点击 FileSelector 的触发按钮
    // FileSelector 组件的触发按钮文本是"选择或上传文件"
    const fileSelectorTrigger = this.page.getByRole('button', { name: /选择或上传文件/ }).or(
      this.page.getByText('选择或上传文件')
    );

    const triggerCount = await fileSelectorTrigger.count();
    console.debug(`找到 ${triggerCount} 个 FileSelector 触发按钮`);

    if (triggerCount === 0) {
      console.debug('未找到 FileSelector 触发按钮');
      return;
    }

    // 点击最新的 FileSelector 触发按钮（如果有多于一个的话）
    await fileSelectorTrigger.nth(triggerCount - 1).click();
    console.debug('已点击 FileSelector 触发按钮');

    // FileSelector 对话框的 data-testid 是 "file-selector-dialog"
    const fileSelectorDialog = this.page.getByTestId('file-selector-dialog');

    // 等待 FileSelector 对话框打开（第四个对话框）
    try {
      await fileSelectorDialog.waitFor({ state: 'visible', timeout: TIMEOUTS.DIALOG });
      console.debug('FileSelector 对话框已打开');
    } catch (_error) {
      console.debug('FileSelector 对话框未打开（超时）');
      // 打印调试信息
      const allDialogs = await this.page.locator('[role="dialog"]').count();
      console.debug(`当前页面对话框数量: ${allDialogs}`);
      return;
    }

    // 使用 uploadFileToField 上传文件
    // MinioUploader 使用 data-testid="minio-uploader-input"（因为 testId 未被传递）
    try {
      await uploadFileToField(
        this.page,
        '[data-testid="minio-uploader-input"]',
        fileName,
        {
          fixturesDir: 'tests/fixtures',
          timeout: TIMEOUTS.DIALOG
        }
      );
      console.debug(`文件 ${fileName} 上传操作已完成`);
    } catch (uploadError) {
      console.debug('文件上传失败:', uploadError);
      // 即使上传失败，也尝试关闭对话框
      await fileSelectorDialog.getByRole('button', { name: '取消' }).click().catch(() => {});
      return;
    }

    // 等待上传处理完成，等待文件出现在对话框中
    await this.page.waitForTimeout(TIMEOUTS.LONG);

    // 点击上传后的文件进行选择（查找第一个可点击的文件）
    // 上传成功后，文件会被添加到文件列表中并显示为选中状态（border-primary）
    const uploadedFile = fileSelectorDialog.locator('.border-primary').or(
      fileSelectorDialog.locator('img').first()
    );
    const fileExists = await uploadedFile.count() > 0;

    if (fileExists) {
      await uploadedFile.first().click();
      console.debug('已点击上传后的文件');
    } else {
      // 如果没找到选中的文件，尝试查看对话框内容
      const allImages = fileSelectorDialog.locator('img');
      const imgCount = await allImages.count();
      console.debug(`对话框中图片数量: ${imgCount}`);

      // 尝试点击任何图片
      if (imgCount > 0) {
        await allImages.first().click();
        console.debug('已点击第一张图片');
      }
    }

    await this.page.waitForTimeout(TIMEOUTS.SHORT);

    // 点击"确认选择"按钮
    const confirmButton = fileSelectorDialog.getByRole('button', { name: '确认选择' });
    const confirmButtonCount = await confirmButton.count();

    if (confirmButtonCount > 0) {
      await confirmButton.click();
      console.debug('已点击确认选择按钮');
    } else {
      console.debug('未找到"确认选择"按钮');
    }

    // 等待 FileSelector 对话框关闭（回到上传弹窗）
    await this.page.waitForTimeout(TIMEOUTS.MEDIUM);

    // 在上传弹窗中点击"提交"按钮
    const submitButton = this.page.getByRole('button', { name: /^提交$/ });
    const submitButtonCount = await submitButton.count();

    if (submitButtonCount > 0) {
      await submitButton.click();
      console.debug('已点击提交按钮');
      // 等待提交完成（上传弹窗关闭）
      await this.page.waitForTimeout(TIMEOUTS.LONG);
    } else {
      console.debug('未找到提交按钮');
    }

    console.debug(`附件上传流程完成: ${fileName}`);
  }

  /**
   * 关闭资源上传对话框
   */
  async closeUploadDialog() {
    // 资源上传对话框有"关闭"按钮
    // 需要精确定位到第二个对话框（资源上传对话框）的关闭按钮
    const dialogs = this.page.locator('[role="dialog"]');
    const dialogCount = await dialogs.count();

    if (dialogCount >= 2) {
      // 资源上传对话框通常是第二个对话框
      const uploadDialog = dialogs.nth(1);
      const closeButton = uploadDialog.getByRole('button', { name: '关闭' });
      const buttonCount = await closeButton.count();

      if (buttonCount > 0) {
        await closeButton.first().click();
        console.debug('已关闭资源上传对话框');
      } else {
        console.debug('未找到资源上传对话框的关闭按钮，尝试按 Escape');
        await this.page.keyboard.press('Escape');
      }
    } else {
      // 如果只有一个对话框，尝试点击通用的关闭按钮
      const closeButton = this.page.getByRole('button', { name: '关闭' });
      const buttonCount = await closeButton.count();
      if (buttonCount > 0) {
        await closeButton.first().click();
        console.debug('已关闭对话框（使用通用关闭按钮）');
      }
    }

    await this.page.waitForTimeout(TIMEOUTS.MEDIUM);
  }

  // ===== 高级操作 =====

  /**
   * 创建订单（完整流程）
   * @param data 订单数据
   * @returns 表单提交结果
   */
  async createOrder(data: OrderData): Promise<FormSubmitResult> {
    await this.openCreateDialog();
    await this.fillOrderForm(data);
    const result = await this.submitForm();
    await this.waitForDialogClosed();
    return result;
  }

  /**
   * 编辑订单（完整流程）
   * @param orderName 订单名称
   * @param data 更新的订单数据
   * @returns 表单提交结果
   */
  async editOrder(orderName: string, data: OrderData): Promise<FormSubmitResult> {
    await this.openEditDialog(orderName);
    await this.fillOrderForm(data);
    const result = await this.submitForm();
    await this.waitForDialogClosed();
    return result;
  }

  /**
   * 删除订单（完整流程）
   * @param orderName 订单名称
   * @returns 是否成功删除
   */
  async deleteOrder(orderName: string): Promise<boolean> {
    await this.openDeleteDialog(orderName);
    await this.confirmDelete();

    // 等待并检查 Toast 消息
    await this.page.waitForTimeout(TIMEOUTS.LONG);
    const successToast = this.page.locator('[data-sonner-toast][data-type="success"]');
    const hasSuccess = await successToast.count() > 0;

    return hasSuccess;
  }

  // ===== 订单状态流转操作 =====

  /**
   * 打开激活订单确认对话框
   * @param orderName 订单名称
   */
  async openActivateDialog(orderName: string): Promise<void> {
    // 找到订单行并点击"打开菜单"按钮（与编辑/删除操作相同的模式）
    const orderRow = this.orderTable.locator('tbody tr').filter({ hasText: orderName }).first();
    const menuButton = orderRow.getByRole('button', { name: '打开菜单' });
    await menuButton.click();

    // 等待菜单出现并点击"激活"选项
    const activateOption = this.page.getByRole('menuitem', { name: /激活|激活订单/ });
    await activateOption.waitFor({ state: 'visible', timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT });
    await activateOption.click();

    // 等待确认对话框出现
    await this.page.waitForSelector('[role="alertdialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG });
  }

  /**
   * 确认激活订单
   */
  async confirmActivate(): Promise<void> {
    // 尝试多种可能的按钮名称
    const confirmButton = this.page.locator('[role="alertdialog"]').getByRole('button', {
      name: /^(确认激活|激活|确定|确认)$/
    });
    await confirmButton.click();
    // 等待确认对话框关闭和网络请求完成
    await this.page.waitForSelector('[role="alertdialog"]', { state: 'hidden', timeout: TIMEOUTS.DIALOG })
      .catch(() => console.debug('激活确认对话框关闭超时'));
    // networkidle 可能因为持续的网络活动而失败，使用更宽松的超时
    await this.page.waitForLoadState('networkidle', { timeout: TIMEOUTS.TABLE_LOAD })
      .catch(() => console.debug('networkidle 超时，继续执行'));
    await this.page.waitForTimeout(TIMEOUTS.LONG);
  }

  /**
   * 激活订单（完整流程）
   * @param orderName 订单名称
   * @returns 是否成功激活
   */
  async activateOrder(orderName: string): Promise<boolean> {
    await this.openActivateDialog(orderName);
    await this.confirmActivate();

    // 等待并检查 Toast 消息
    await this.page.waitForTimeout(TIMEOUTS.LONG);
    const successToast = this.page.locator('[data-sonner-toast][data-type="success"]');
    const hasSuccess = await successToast.count() > 0;

    return hasSuccess;
  }

  /**
   * 打开关闭订单确认对话框
   * @param orderName 订单名称
   */
  async openCloseDialog(orderName: string): Promise<void> {
    // 找到订单行并点击"打开菜单"按钮
    const orderRow = this.orderTable.locator('tbody tr').filter({ hasText: orderName }).first();
    const menuButton = orderRow.getByRole('button', { name: '打开菜单' });
    await menuButton.click();

    // 等待菜单出现并点击"关闭"选项
    const closeOption = this.page.getByRole('menuitem', { name: /关闭|关闭订单|完成/ });
    await closeOption.waitFor({ state: 'visible', timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT });
    await closeOption.click();

    // 等待确认对话框出现
    await this.page.waitForSelector('[role="alertdialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG });
  }

  /**
   * 确认关闭订单
   */
  async confirmClose(): Promise<void> {
    // 尝试多种可能的按钮名称
    const confirmButton = this.page.locator('[role="alertdialog"]').getByRole('button', {
      name: /^(确认关闭|关闭|确定|确认)$/
    });
    await confirmButton.click();
    // 等待确认对话框关闭和网络请求完成
    await this.page.waitForSelector('[role="alertdialog"]', { state: 'hidden', timeout: TIMEOUTS.DIALOG })
      .catch(() => console.debug('关闭确认对话框关闭超时'));
    await this.page.waitForLoadState('networkidle', { timeout: TIMEOUTS.TABLE_LOAD });
    await this.page.waitForTimeout(TIMEOUTS.LONG);
  }

  /**
   * 关闭订单（完整流程）
   * @param orderName 订单名称
   * @returns 是否成功关闭
   */
  async closeOrder(orderName: string): Promise<boolean> {
    await this.openCloseDialog(orderName);
    await this.confirmClose();

    // 等待并检查 Toast 消息
    await this.page.waitForTimeout(TIMEOUTS.LONG);
    const successToast = this.page.locator('[data-sonner-toast][data-type="success"]');
    const hasSuccess = await successToast.count() > 0;

    return hasSuccess;
  }

  /**
   * 获取订单的当前状态（从列表页面）
   * @param orderName 订单名称
   * @returns 订单状态值或 null
   */
  async getOrderStatus(orderName: string): Promise<OrderStatus | null> {
    const orderRow = this.orderTable.locator('tbody tr').filter({ hasText: orderName }).first();

    // 等待行可见
    await orderRow.waitFor({ state: 'visible', timeout: TIMEOUTS.ELEMENT_VISIBLE_SHORT }).catch(() => {
      console.debug(`订单 "${orderName}" 行不可见`);
    });

    const rowCount = await orderRow.count();
    if (rowCount === 0) {
      console.debug(`订单 "${orderName}" 不存在`);
      return null;
    }

    // 尝试多种策略定位状态列
    // 策略1: 查找包含状态文本的单元格（但排除订单名称列）
    const allCells = orderRow.locator('td');
    const cellCount = await allCells.count();

    for (let i = 1; i < cellCount; i++) {  // 跳过第一列（通常是订单名称）
      const cell = allCells.nth(i);
      const cellText = await cell.textContent();

      if (cellText) {
        // 检查是否包含完整的状态标签（避免部分匹配）
        for (const [statusValue, statusLabel] of Object.entries(ORDER_STATUS_LABELS)) {
          // 使用更严格的匹配：必须是状态标签本身或包含完整标签
          const trimmedText = cellText.trim();
          if (trimmedText === statusLabel || trimmedText.includes(`${statusLabel}`)) {
            // 验证不是订单名称列（额外检查）
            const firstCellText = await allCells.nth(0).textContent();
            if (firstCellText && !firstCellText.includes(orderName.substring(0, 3))) {
              // 第一列不包含订单名称开头，说明列结构可能不同
              return statusValue as OrderStatus;
            }
            // 跳过第一列后找到的状态标签才返回
            return statusValue as OrderStatus;
          }
        }
      }
    }

    // 策略2: 如果上述方法失败，尝试查找状态徽章/标签元素
    // 查找具有状态样式特征的元素
    const statusBadge = orderRow.locator('[class*="status"], [class*="badge"], span').filter({
      hasText: Object.values(ORDER_STATUS_LABELS)
    });

    if (await statusBadge.count() > 0) {
      const badgeText = await statusBadge.first().textContent();
      if (badgeText) {
        for (const [statusValue, statusLabel] of Object.entries(ORDER_STATUS_LABELS)) {
          if (badgeText.includes(statusLabel)) {
            return statusValue as OrderStatus;
          }
        }
      }
    }

    console.debug(`无法从订单 "${orderName}" 中解析状态`);
    return null;
  }

  /**
   * 验证订单状态
   * @param orderName 订单名称
   * @param expectedStatus 期望的状态
   */
  async expectOrderStatus(orderName: string, expectedStatus: OrderStatus): Promise<void> {
    const actualStatus = await this.getOrderStatus(orderName);
    if (actualStatus === null) {
      throw new Error(`订单 "${orderName}" 未找到或状态列无法识别`);
    }
    if (actualStatus !== expectedStatus) {
      throw new Error(
        `订单 "${orderName}" 状态不匹配: 期望 "${ORDER_STATUS_LABELS[expectedStatus]}", 实际 "${ORDER_STATUS_LABELS[actualStatus]}"`
      );
    }
  }

  /**
   * 检查激活按钮是否可用
   *
   * **注意**: 此方法会打开和关闭菜单，属于有副作用的操作
   *
   * @param orderName 订单名称
   * @returns 按钮是否可用
   */
  async checkActivateButtonEnabled(orderName: string): Promise<boolean> {
    // 找到订单行并打开菜单
    const orderRow = this.orderTable.locator('tbody tr').filter({ hasText: orderName }).first();

    // 检查订单是否存在
    const orderCount = await orderRow.count();
    if (orderCount === 0) {
      console.debug(`订单 "${orderName}" 不存在`);
      return false;
    }

    const menuButton = orderRow.getByRole('button', { name: '打开菜单' });

    try {
      await menuButton.click();
    } catch (error) {
      console.debug(`无法打开订单 "${orderName}" 的菜单:`, error);
      return false;
    }

    // 检查激活菜单项是否可点击
    const activateOption = this.page.getByRole('menuitem', { name: /激活|激活订单/ });
    const isVisible = await activateOption.isVisible().catch(() => false);

    let isEnabled = false;
    if (isVisible) {
      // 检查是否有禁用属性或样式
      const isDisabled = await activateOption.isDisabled().catch(() => false);
      isEnabled = !isDisabled;
    }

    // 关闭菜单以便后续操作
    await this.page.keyboard.press('Escape');
    await this.page.waitForTimeout(TIMEOUTS.SHORT);

    return isEnabled;
  }

  /**
   * 检查关闭按钮是否可用
   *
   * **注意**: 此方法会打开和关闭菜单，属于有副作用的操作
   *
   * @param orderName 订单名称
   * @returns 按钮是否可用
   */
  async checkCloseButtonEnabled(orderName: string): Promise<boolean> {
    // 找到订单行并打开菜单
    const orderRow = this.orderTable.locator('tbody tr').filter({ hasText: orderName }).first();

    // 检查订单是否存在
    const orderCount = await orderRow.count();
    if (orderCount === 0) {
      console.debug(`订单 "${orderName}" 不存在`);
      return false;
    }

    const menuButton = orderRow.getByRole('button', { name: '打开菜单' });

    try {
      await menuButton.click();
    } catch (error) {
      console.debug(`无法打开订单 "${orderName}" 的菜单:`, error);
      return false;
    }

    // 检查关闭菜单项是否可点击
    const closeOption = this.page.getByRole('menuitem', { name: /关闭|关闭订单|完成/ });
    const isVisible = await closeOption.isVisible().catch(() => false);

    let isEnabled = false;
    if (isVisible) {
      // 检查是否有禁用属性或样式
      const isDisabled = await closeOption.isDisabled().catch(() => false);
      isEnabled = !isDisabled;
    }

    // 关闭菜单以便后续操作
    await this.page.keyboard.press('Escape');
    await this.page.waitForTimeout(TIMEOUTS.SHORT);

    return isEnabled;
  }
}
