package com.test.test.service.impl;

import java.util.*;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.test.common.config.TestConfig;
import com.test.common.utils.DateUtils;
import com.test.common.utils.SecurityUtils;
import com.test.common.utils.SeleniumUtils;
import com.test.common.utils.StringUtils;
import com.test.test.domain.*;
import com.test.test.domain.qo.*;
import com.test.test.domain.vo.AssertionReportVO;
import com.test.test.domain.vo.UiHighSettingVO;
import com.test.test.mapper.UiAutomationMapper;
import com.test.test.mapper.UiSceneStepsMapper;
import com.test.test.mapper.UiSceneStepsReportMapper;
import com.test.test.service.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.C;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.UnexpectedTagNameException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;


@Service
@Slf4j
public class UiSceneStepsServiceImpl implements IUiSceneStepsService {

    @Resource
    private UiSceneStepsMapper uiSceneStepsMapper;
    @Resource
    private IUiReportService uiReportService;
    @Resource
    private UiSceneStepsReportMapper uiSceneStepsReportMapper;
    @Resource
    private IUiAutomationService uiAutomationService;
    @Resource
    private IUiElementService uiElementService;
    @Resource
    private UiAutomationMapper uiAutomationMapper;

    @Value("${test.selenium.chrome-driver-path}")
    private String chromeDriverPath;


    /**
     * 执行场景步骤
     *
     * @param automationId
     * @return
     */
    public Map<String, Object> executeStep(Long automationId, String triggerMode) {
        Map<String, Object> result = new HashMap<>();
        result.put("msg", "");
        result.put("id", 0L);
        AtomicInteger totalAssertions = new AtomicInteger(0);
        AtomicInteger successAssertions = new AtomicInteger(0);
        Gson gson = new Gson();
        // 1. 验证并获取步骤
        List<UiSceneSteps> steps = validateAndGetSteps(automationId);

        //修改ui_automation 状态为进行中 执行结果为running
        UiAutomation uiAutomation = new UiAutomation();
        uiAutomation.setId(automationId);
        uiAutomation.setExecutionResult("1");
        uiAutomation.setUpdateTime(DateUtils.getNowDate());
        uiAutomationMapper.updateUiAutomation(uiAutomation);


        Date startTime = DateUtils.getNowDate();
        // 2. 创建执行报告
        Long reportId = uiReportService.insertUiReport(startTime, automationId, triggerMode, steps.size());
        UiReport uiReport = new UiReport();
        result.put("id", reportId);
        uiReport.setId(reportId);
        Date endTime = null;
        Integer orderNumber = 0;
        for (UiSceneSteps step : steps) {
            orderNumber++;
            //创建报告步骤表
            createSceneStepsReport(orderNumber, step, reportId);
        }
        System.setProperty("webdriver.chrome.driver", chromeDriverPath);
        WebDriver driver = new ChromeDriver();
        SeleniumUtils seleniumUtils = new SeleniumUtils(driver);
        try {
            // 3. 执行所有步骤
            executeAllSteps(steps, reportId,seleniumUtils);
            log.info("场景执行成功: {}", automationId);
            uiReport.setConsoleStr("OK");
            uiReport.setStatus(3);
        } catch (IllegalArgumentException e) {
            result.put("msg", e.getMessage());
            log.error("场景执行失败: {}", e.getMessage());
            uiReport.setConsoleStr(e.toString());
            uiReport.setStatus(2);
            uiAutomation.setExecutionResult("2");
        } finally {
            endTime = DateUtils.getNowDate();
            uiReport.setTake(DateUtils.calculateTimeDifference(startTime, endTime));
            uiReport.setUpdateTime(endTime);
            //查询report
            UiSceneStepsReport uiStepsReport = new UiSceneStepsReport();
            uiStepsReport.setReportId(reportId);
            List<UiSceneStepsReport> uiSceneStepsReports = uiSceneStepsReportMapper.selectUiSceneStepsReportList(uiStepsReport);
            int scenesSucceedNumber = 0;
            int scenesErrorNumber = 0;
            int stepsSucceedNumber = 0;
            int stepsErrorNumber = 0;
            int stepsNotNumber = 0;
            if (uiSceneStepsReports != null && !uiSceneStepsReports.isEmpty()) {
                boolean allSuccess = true;
                for (UiSceneStepsReport report : uiSceneStepsReports) {
                    if (report.getExecutionFlag().equals("1")) {
                        stepsSucceedNumber++;
                    } else if (report.getExecutionFlag().equals("2")) {
                        stepsErrorNumber++;
                        allSuccess = false;
                    } else if (report.getExecutionFlag().equals("0")) {
                        stepsNotNumber++;
                    }
                    //成功断言数
                    String assertionJson = report.getAssertionJson();
                    if (assertionJson != null && !assertionJson.trim().isEmpty()) {
                        try {
                            List<AssertionReportVO> assertions = gson.fromJson(
                                    assertionJson,
                                    new TypeToken<List<AssertionReportVO>>() {
                                    }.getType()
                            );
                            int stepAssertionCount = assertions.size();
                            int stepSuccessCount = (int) assertions.stream()
                                    .filter(a -> "1".equals(a.getIsSuccess()))
                                    .count();
                            totalAssertions.addAndGet(stepAssertionCount);
                            successAssertions.addAndGet(stepSuccessCount);
                        } catch (Exception e) {
                            log.error("步骤[{}]断言数据解析失败: {}", report.getId(), assertionJson, e);
                        }
                    }
                }
                if (allSuccess) {
                    scenesSucceedNumber = 1;
                } else {
                    scenesErrorNumber = 1;
                }
            }
            uiReport.setSuccessAssertion("0/0");
            String globalAssertionResult = successAssertions.get() + "/" + totalAssertions.get();
            uiReport.setSuccessAssertion(globalAssertionResult);

            uiReport.setScenesSucceedNumber(scenesSucceedNumber);
            uiReport.setScenesErrorNumber(scenesErrorNumber);
            uiReport.setScenesNotNumber(0);
            uiReport.setStepsSucceedNumber(stepsSucceedNumber);
            uiReport.setStepsErrorNumber(stepsErrorNumber);
            uiReport.setStepsNotNumber(stepsNotNumber);
            uiReport.setFaiiRate(
                    stepsErrorNumber == 0 ? "0%" :
                            String.format("%.2f%%", (double) stepsErrorNumber / steps.size() * 100)


            );
            if (1 == uiReport.getStatus()){
                uiReport.setStatus(4);
            }
            uiReportService.updateUiReport(uiReport);

            //修改ui_automation
            UiAutomation uiAutomation1 = new UiAutomation();
            uiAutomation1.setId(automationId);
            uiAutomation1.setExecutionResult("2");
            if (!uiAutomation.getExecutionResult().equals("2")) {
                uiAutomation1.setExecutionResult("3");
            }
            uiAutomation1.setUpdateTime(DateUtils.getNowDate());
            uiAutomation1.setPassRate(stepsSucceedNumber == 0 ? "0%" :
                    String.format("%.2f%%", (double) stepsSucceedNumber / steps.size() * 100));
            uiAutomationMapper.updateUiAutomation(uiAutomation1);

            //关闭浏览器
            seleniumUtils.quit();
        }
        return result;
    }

    /**
     * 判断步骤是否为空
     *
     * @param automationId
     * @return
     */
    private List<UiSceneSteps> validateAndGetSteps(Long automationId) {
        List<UiSceneSteps> steps = uiSceneStepsMapper.selectUiSceneStepsById(automationId);
        steps = steps.stream().filter(e -> 0 == e.getIsDisabled() ).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(steps)) {
            throw new IllegalArgumentException("步骤不能为空");
        }
        //判断第一步是不是打开网页
        if (!"1".equals(steps.get(0).getOperateType()) || !"1".equals(steps.get(0).getStepType())) {
            throw new IllegalArgumentException("第一步必须是打开网页");
        }
        return steps;
    }

    /**
     * 创建报告步骤
     *
     * @param steps
     * @param reportId
     */
    private Long createSceneStepsReport(Integer orderNumber, UiSceneSteps steps, Long reportId) {
        UiSceneStepsReport uiSceneStepsReport = new UiSceneStepsReport();
        uiSceneStepsReport.setReportId(reportId);
        uiSceneStepsReport.setName(steps.getName());
        uiSceneStepsReport.setCreateBy(SecurityUtils.getUsername());
        uiSceneStepsReport.setExecutionFlag("0");//未执行
        uiSceneStepsReport.setOrderNumber(orderNumber);
        uiSceneStepsReportMapper.insertUiSceneStepsReport(uiSceneStepsReport);
        return uiSceneStepsReport.getId();
    }


    /**
     * 场景步骤执行
     *
     * @param steps
     */
    private void executeAllSteps(List<UiSceneSteps> steps, Long reportId,SeleniumUtils seleniumUtils) {
        Integer orderNumber = 0;
        UiSceneStepsReport uiSceneStepsReport = new UiSceneStepsReport();
        uiSceneStepsReport.setReportId(reportId);
        for (UiSceneSteps step : steps) {
            orderNumber++;
            uiSceneStepsReport.setOrderNumber(orderNumber);
            List<UiSceneStepsReport> uiSceneStepsReports = uiSceneStepsReportMapper.selectUiSceneStepsReportList(uiSceneStepsReport);
            if (CollectionUtils.isEmpty(uiSceneStepsReports)) {
                continue;
            }
            //查看高级设置
            List<UiHighSettingVO> uiHighSettingVOList = uiAutomationService.getUiHighSettingVOList(step.getId());
            executeSingleStep(step, seleniumUtils, uiSceneStepsReports.get(0).getId(), uiHighSettingVOList);
        }
        //关闭浏览器
//        seleniumUtils.quit();
    }

    private void executeSingleStep(UiSceneSteps step, SeleniumUtils seleniumUtils,
                                   Long sceneStepsReportId, List<UiHighSettingVO> uiHighSettingVOList) {
        // 定义所有操作类型的执行方法映射
        Map<String, Map<String, StepExecution>> operationStrategies = Map.of(
                "1", Map.of( // 浏览器操作
                        "1", this::openWebPage,
                        "2", this::closeBrowser,
                        "3", this::switchWindow,
                        "4", this::resizeWindow,
                        "5", this::selectEmbeddedPage
                ),
                "2", Map.of( // 弹窗操作
                        "1", this::handlePopup
                ),
                "3", Map.of( // 元素操作
                        "1", this::submitForm,
                        "2", this::handleDropdown,
                        "3", this::setOption,
                        "4", this::waitForElement
                ),
                "4", Map.of( // 鼠标操作
                        "1", this::mouseClick,
                        "2", this::mouseMove
                ),
                "5", Map.of( // 输入操作
                        "1", this::inputText,
                        "2", this::uploadFile
                )
        );

        // 获取对应的操作类型映射
        Map<String, StepExecution> operationMap = operationStrategies.get(step.getOperateType());
        if (operationMap == null) {
            throw new IllegalArgumentException("不支持的操作类型: " + step.getOperateType());
        }

        // 获取具体的执行方法
        StepExecution execution = operationMap.get(step.getStepType());
        if (execution == null) {
            throw new IllegalArgumentException("不支持的步骤类型: " + step.getStepType() +
                    " 对于操作类型: " + step.getOperateType());
        }

        // 执行步骤并处理结果
        boolean shouldContinue = executeStepWithLog(step, seleniumUtils, sceneStepsReportId,
                execution, uiHighSettingVOList);
        if (!shouldContinue) {
            //关闭浏览器
//            seleniumUtils.quit();
            throw new IllegalArgumentException("步骤执行失败，根据设置终止场景执行");
        }
    }


    /**
     * 执行步骤并记录日志
     *
     * @param step                步骤对象
     * @param seleniumUtils       Selenium工具类
     * @param sceneStepsReportId  报告步骤ID
     * @param stepExecution       具体的步骤执行方法
     * @param uiHighSettingVOList 高级设置列表
     * @return boolean 是否继续执行后续步骤 (true=继续, false=终止)
     */
    private boolean executeStepWithLog(UiSceneSteps step, SeleniumUtils seleniumUtils,
                                       Long sceneStepsReportId, StepExecution stepExecution,
                                       List<UiHighSettingVO> uiHighSettingVOList) {
        // 1. 初始化设置对象
        UiHighSettingVO errorSetting = extractErrorSetting(uiHighSettingVOList);
        UiHighSettingVO otherSetting = extractOtherSetting(uiHighSettingVOList);
        OtherSettingsQO otherSettingsQO = otherSetting.getOtherSettingsQO();
        Integer screenshotConfiguration = 0;
        if (otherSettingsQO != null) {
            screenshotConfiguration = otherSettingsQO.getScreenshotConfiguration() == null ? 0 : otherSettingsQO.getScreenshotConfiguration();
            //等待元素超时时间
            Integer waitElementTime = otherSettingsQO.getWaitElementTime() == null ? -1 : otherSettingsQO.getWaitElementTime();
            if (waitElementTime > 0) {
                // 转换为秒（至少1秒）
                long timeoutInSeconds = Math.max(1, waitElementTime / 1000);
                log.info("元素等待时间{}秒", timeoutInSeconds);
                seleniumUtils.setWaitTimeout(timeoutInSeconds);
            }
        }


        //前置操作设置
        List<UiHighSettingVO> beforeSettingList = extractbeforeSetting(uiHighSettingVOList);
        // 获取前置操作数据集合（判空）
        Map<String, Object> beforeData = CollectionUtils.isEmpty(beforeSettingList)
                ? new HashMap<>()
                : filterBydataExtractionQOList(beforeSettingList, seleniumUtils);
        //后置操作设置
        List<UiHighSettingVO> afterSettingList = extractafterSetting(uiHighSettingVOList);

        //前置等待时间
        int beforeAwaitTime = getAwaitTime(beforeSettingList);
        //后置等待时间
        int afterAwaitTime = getAwaitTime(afterSettingList);
        // 2. 准备报告对象
        UiSceneStepsReport report = new UiSceneStepsReport();
        report.setId(sceneStepsReportId);
        Date startTime = DateUtils.getNowDate();
        Date endTime = null;
        boolean continueExecution = true; // 默认继续执行
        try {
            // 执行前置等待
            if (beforeAwaitTime > 0) {
                log.info("执行前置等待: {}ms", beforeAwaitTime);
                Thread.sleep(beforeAwaitTime);
            }

            if (screenshotConfiguration == 1) {
                report.setScreenshot(seleniumUtils.takeScreenshotAsFile(TestConfig.getProfile()));
                log.info("截图成功,路径:{}", seleniumUtils.takeScreenshotAsFile(TestConfig.getProfile()));
            }
            // 3. 执行具体步骤
            stepExecution.execute(step, seleniumUtils);

            // 执行后置等待
            if (afterAwaitTime > 0) {
                log.info("执行后置等待: {}ms", afterAwaitTime);
                Thread.sleep(afterAwaitTime);
            }
            report.setLogInfo("OK");
            report.setExecutionFlag("1");
        } catch (Exception e) {
            // 4. 错误处理
            report.setLogInfo(e.toString());
            report.setExecutionFlag("2");
            // 根据错误处理设置决定是否继续
            if ("1".equals(errorSetting.getErrorHandling())) {
                continueExecution = false; // 不继续执行后续步骤
            }
            if (screenshotConfiguration == 2) {
                report.setScreenshot(seleniumUtils.takeScreenshotAsFile(TestConfig.getProfile()));
                log.info("截图成功,路径:{}", seleniumUtils.takeScreenshotAsFile(TestConfig.getProfile()));
            }
            log.error("步骤执行失败: {}", e.getMessage());
        } finally {
            //获取后置操作数据集合
            Map<String, Object> afterData = CollectionUtils.isEmpty(afterSettingList)
                    ? new HashMap<>()
                    : filterBydataExtractionQOList(afterSettingList, seleniumUtils);
            Map<String, Object> mergedData = new HashMap<>();
            if (!beforeData.isEmpty()) {
                mergedData.put("前置数据提取", beforeData);
            }
            if (!afterData.isEmpty()) {
                mergedData.put("后置数据提取", afterData);
            }
            // 设置到报告
            try {
                String json = mergedData.isEmpty() ? null : new ObjectMapper().writeValueAsString(mergedData);
                report.setExtractionDataJson(json);
            } catch (JsonProcessingException e) {
                report.setExtractionDataJson("JsonProcessingException error");
            }
            Map<String, Object> stringObjectMap = filterByAssertionQOList(afterSettingList, seleniumUtils);
            //断言失败是否终止
            String continueExecution1 = (String) stringObjectMap.get("continueExecution");
            if (continueExecution1.equals("1")) {
                continueExecution = false;
                report.setLogInfo("");
                report.setExecutionFlag("2");
            }
            List<AssertionReportVO> assertionReportVOS = (List<AssertionReportVO>) stringObjectMap.get("assertionReportVOS");
            Gson gson = new Gson();
            report.setAssertionJson(assertionReportVOS.isEmpty() ? null : gson.toJson(assertionReportVOS));
            endTime = DateUtils.getNowDate();
            // 更新报告
            report.setCreateTime(startTime);
            report.setUpdateTime(endTime);
            report.setTake(DateUtils.differenceInMilliseconds(startTime, endTime) + "ms");
            uiSceneStepsReportMapper.updateUiSceneStepsReport(report);

        }

        return continueExecution;
    }


    /**
     * 提取前置操作
     *
     * @param settings
     * @return
     */
    private List<UiHighSettingVO> extractbeforeSetting(List<UiHighSettingVO> settings) {
        return settings.stream()
                .filter(e -> "1".equals(e.getSettingType()) && 0 == e.getIsDisabled()  )
                .collect(Collectors.toList()); // 返回默认设置
    }

    /**
     * 断言
     *
     * @param settingList
     * @param seleniumUtils
     * @return
     */
    public Map<String, Object> filterByAssertionQOList(List<UiHighSettingVO> settingList, SeleniumUtils seleniumUtils) {
        Map<String, Object> result = new HashMap<>();
        String continueExecution = "2";
        List<AssertionReportVO> assertionReports = new ArrayList<>();

        result.put("continueExecution", continueExecution);
        result.put("assertionReportVOS", assertionReports);
        List<UiHighSettingVO> uiHighSettingVOS = settingList.stream()
                .filter(setting -> setting.getOperateType().equals("2") && 0 == setting.getIsDisabled())
                .toList();

        if (CollectionUtils.isEmpty(uiHighSettingVOS)) {
            return result;
        }
        for (UiHighSettingVO uiHighSettingVO : uiHighSettingVOS) {
            AssertionConfigQO assertion = uiHighSettingVO.getAssertionQOList();
            List<AssertionQO> assertionQOList = new ArrayList<>();
            assertionQOList.addAll(assertion.getPopupTexts());
            assertionQOList.addAll(assertion.getElementAssertions());
            assertionQOList.addAll(assertion.getDropdownBoxes());
            assertionQOList.addAll(assertion.getWebTitles());
            if (!CollectionUtils.isEmpty(assertionQOList)) {
                for (AssertionQO assertionQO : assertionQOList) {
                    if ("1".equals(assertionQO.getIsDisabled())) {
                        continue;
                    }
                    AssertionReportVO report = new AssertionReportVO();
                    report.setName("1".equals(assertionQO.getIsFailedAbort()) ? "assert" : "verify");
                    report.setIsSuccess("1"); // 默认成功

                    try {
                        boolean assertionResult = false;
                        String errorDetail = "";

                        switch (assertionQO.getAssertionType()) {
                            case "1": // 弹窗文本断言
                                assertionResult = handleAlertAssertion(assertionQO, seleniumUtils, report);
                                break;
                            case "2": // 元素断言
                                assertionResult = handleElementAssertion(assertionQO, seleniumUtils, report);
                                break;
                            case "3": // 下拉框断言
                                assertionResult = handleDropdownAssertion(assertionQO, seleniumUtils, report);
                                break;
                            case "4": // 网页标题断言
                                assertionResult = handleTitleAssertion(assertionQO, seleniumUtils, report);
                                break;
                            default:
                                errorDetail = "不支持的断言类型: " + assertionQO.getAssertionType();
                                report.setErrorInfo(errorDetail);
                                report.setIsSuccess("0");
                        }

                        if (!assertionResult && "1".equals(assertionQO.getIsFailedAbort())) {
                            continueExecution = "1";
                            result.put("continueExecution", continueExecution);
                        }
                    } catch (Exception e) {
                        report.setErrorInfo("断言执行异常: " + e.getMessage());
                        report.setIsSuccess("0");
                        log.error("断言执行异常", e);
                    }

                    assertionReports.add(report);
                }
            }
        }
        result.put("assertionReportVOS", assertionReports);
        return result;
    }

    private boolean handleAlertAssertion(AssertionQO assertionQO,
                                         SeleniumUtils seleniumUtils,
                                         AssertionReportVO report) {
        try {
            // 1. 获取弹窗文本（带10秒等待）
            String actualText = seleniumUtils.getAlertText();
            String expectedText = assertionQO.getExpectations();
            report.setErrorInfo("");
            // 3. 文本比较
            if (!actualText.equals(expectedText)) {
                report.setErrorInfo(String.format(
                        "弹窗文本不匹配，实际值：'%s'，期望值：'%s'",
                        actualText, expectedText
                ));
                report.setIsSuccess("0");
                return false;
            }

            // 4. 根据配置处理弹窗
            if ("1".equals(assertionQO.getIsPopText())) {
                seleniumUtils.acceptAlert(); // 点击确认
                log.info("弹窗断言成功，已点击确认");
            } else {
                log.info("弹窗断言成功，未执行点击操作");
            }
            report.setIsSuccess("1");
            return true;

        } catch (NoAlertPresentException e) {
            report.setErrorInfo("弹窗未出现：" + e.getMessage());
            report.setIsSuccess("0");
            return false;
        } catch (Exception e) {
            report.setErrorInfo("弹窗处理异常：" + e.getMessage());
            report.setIsSuccess("0");
            return false;
        }
    }

    /**
     * 处理元素断言
     *
     * @param assertionQO   断言配置
     * @param seleniumUtils Selenium工具类
     * @param report        断言报告
     * @return 断言是否成功
     */
    private boolean handleElementAssertion(AssertionQO assertionQO,
                                           SeleniumUtils seleniumUtils,
                                           AssertionReportVO report) {
        WebElement element = null;
        String locatorDescription = "";
        report.setErrorInfo("");
        try {
            // 1. 获取元素定位信息
            if ("1".equals(assertionQO.getOperateObject())) {
                // 从数据库查询元素信息
                //元素定位
                UiElement uiElement = uiElementService.selectUiElementById(assertionQO.getOperateElementId());
                if (uiElement == null) {
                    report.setErrorInfo("元素ID " + assertionQO.getOperateElementId() + " 不存在");
                    report.setIsSuccess("0");
                    return false;
                }
                element = seleniumUtils.findElement(uiElement.getLocType(), uiElement.getElementLoc());
                locatorDescription = String.format("元素ID:%d (%s=%s)",
                        assertionQO.getOperateElementId(),
                        uiElement.getLocType(),
                        uiElement.getElementLoc());
            } else if ("2".equals(assertionQO.getOperateObject())) {
                // 直接使用配置的定位信息
                element = seleniumUtils.findElement(
                        assertionQO.getOperateLocType(),
                        assertionQO.getOperateLocValue()
                );
                locatorDescription = String.format("直接定位 (%s=%s)",
                        assertionQO.getOperateLocType(),
                        assertionQO.getOperateLocValue());
            }
            // 3. 执行断言
            boolean result = false;
            String actualValue = "";
            String expectedValue = assertionQO.getExpectations();

            switch (assertionQO.getAssertionMode()) {
                case "1": // 元素被选中(Checked)
                    actualValue = String.valueOf(element.isSelected());
                    result = element.isSelected();
                    if (!result) {
                        report.setErrorInfo(locatorDescription + " 未被选中");
                    }
                    break;

                case "2": // 元素可编辑(Editable)
                    actualValue = String.valueOf(element.isEnabled());
                    result = element.isEnabled();
                    if (!result) {
                        report.setErrorInfo(locatorDescription + " 不可编辑");
                    }
                    break;

                case "3": // 元素存在(ElementPresent)
                    result = true; // 如果能找到元素，说明存在
                    break;

                case "4": // 元素不存在(ElementNotPresent)
                    report.setErrorInfo("元素存在性断言应使用其他方式实现");
                    return false;

                case "5": // 元素未被选中(NotChecked)
                    actualValue = String.valueOf(element.isSelected());
                    result = !element.isSelected();
                    if (!result) {
                        report.setErrorInfo(locatorDescription + " 已被选中");
                    }
                    break;

                case "6": // 元素不可编辑(NotEditable)
                    actualValue = String.valueOf(element.isEnabled());
                    result = !element.isEnabled();
                    if (!result) {
                        report.setErrorInfo(locatorDescription + " 可编辑");
                    }
                    break;

                case "7": // 元素文本不等于期望(NotText)
                    actualValue = element.getText();
                    result = !actualValue.equals(expectedValue);
                    if (!result) {
                        report.setErrorInfo(String.format(
                                "%s 文本等于期望值（实际：'%s'，期望不等于：'%s'）",
                                locatorDescription, actualValue, expectedValue
                        ));
                    }
                    break;

                case "8": // 元素文本等于期望(Text)
                    actualValue = element.getText();
                    result = actualValue.equals(expectedValue);
                    if (!result) {
                        report.setErrorInfo(String.format(
                                "%s 文本不等于期望值（实际：'%s'，期望：'%s'）",
                                locatorDescription, actualValue, expectedValue
                        ));
                    }
                    break;

                case "9": // 元素值等于期望(Value)
                    actualValue = element.getAttribute("value");
                    result = actualValue.equals(expectedValue);
                    if (!result) {
                        report.setErrorInfo(String.format(
                                "%s 值不等于期望值（实际：'%s'，期望：'%s'）",
                                locatorDescription, actualValue, expectedValue
                        ));
                    }
                    break;

                case "10": // 元素文本包含期望(InText)
                    actualValue = element.getText();
                    result = actualValue.contains(expectedValue);
                    if (!result) {
                        report.setErrorInfo(String.format(
                                "%s 文本不包含期望内容（实际：'%s'，期望包含：'%s'）",
                                locatorDescription, actualValue, expectedValue
                        ));
                    }
                    break;

                case "11": // 元素文本不包含期望(NotlnText)
                    actualValue = element.getText();
                    result = !actualValue.contains(expectedValue);
                    if (!result) {
                        report.setErrorInfo(String.format(
                                "%s 文本包含不应出现的内容（实际：'%s'，期望不包含：'%s'）",
                                locatorDescription, actualValue, expectedValue
                        ));
                    }
                    break;
                default:
                    report.setErrorInfo("不支持的断言方式: " + assertionQO.getAssertionMode());
                    return false;
            }
            report.setIsSuccess(result ? "1" : "0");
            return result;
        } catch (NoSuchElementException e) {
            if ("3".equals(assertionQO.getAssertionMode())) {
                // 元素存在断言时，找不到元素应该返回false
                report.setErrorInfo(locatorDescription + " 不存在");
                report.setIsSuccess("0");
                return false;
            }
            report.setErrorInfo("元素定位失败: " + e.getMessage());
            report.setIsSuccess("0");
            return false;
        } catch (Exception e) {
            report.setErrorInfo("元素断言异常: " + e.getMessage());
            report.setIsSuccess("0");
            return false;
        }
    }


    /**
     * 处理下拉框断言
     *
     * @param assertionQO   断言配置
     * @param seleniumUtils Selenium工具类
     * @param report        断言报告
     * @return 断言是否成功
     */
    private boolean handleDropdownAssertion(AssertionQO assertionQO,
                                            SeleniumUtils seleniumUtils,
                                            AssertionReportVO report) {
        WebElement dropdown = null;
        String locatorDescription = "";
        report.setErrorInfo("");
        try {
            // 1. 获取下拉框元素
            if ("1".equals(assertionQO.getOperateObject())) {
                UiElement uiElement = uiElementService.selectUiElementById(assertionQO.getOperateElementId());
                if (uiElement == null) {
                    report.setErrorInfo("元素ID " + assertionQO.getOperateElementId() + " 不存在");
                    report.setIsSuccess("0");
                    return false;
                }
                dropdown = seleniumUtils.findElement(uiElement.getLocType(), uiElement.getElementLoc());
                locatorDescription = String.format("下拉框ID:%d (%s=%s)",
                        assertionQO.getOperateElementId(),
                        uiElement.getLocType(),
                        uiElement.getElementLoc());
            } else {
                dropdown = seleniumUtils.findElement(
                        assertionQO.getOperateLocType(),
                        assertionQO.getOperateLocValue()
                );
                locatorDescription = String.format("下拉框 (%s=%s)",
                        assertionQO.getOperateLocType(),
                        assertionQO.getOperateLocValue());
            }
            // 2. 转换为Select对象
            Select select = new Select(dropdown);
            WebElement selectedOption = select.getFirstSelectedOption();
            String actualValue = selectedOption.getAttribute("value");
            String actualText = selectedOption.getText();
            String expectedValue = assertionQO.getExpectations();

            // 4. 执行断言
            boolean result = false;
            switch (assertionQO.getAssertionMode()) {
                case "1": // 所选元素的值等于期望(SelectedValue)
                    result = actualValue.equals(expectedValue);
                    if (!result) {
                        report.setErrorInfo(String.format(
                                "下拉框选中值不匹配（实际：'%s'，期望：'%s'）",
                                actualValue, expectedValue
                        ));
                    }
                    break;

                case "2": // 下拉框选项显示的文本等于期望(SelectedLabel)
                    result = actualText.equals(expectedValue);
                    if (!result) {
                        report.setErrorInfo(String.format(
                                "下拉框选中文本不匹配（实际：'%s'，期望：'%s'）",
                                actualText, expectedValue
                        ));
                    }
                    break;

                case "3": // 所选元素的值不等于期望(NotSelectedValue)
                    result = !actualValue.equals(expectedValue);
                    if (!result) {
                        report.setErrorInfo(String.format(
                                "下拉框选中值不应匹配（实际：'%s'，期望不等于：'%s'）",
                                actualValue, expectedValue
                        ));
                    }
                    break;
                default:
                    report.setErrorInfo("不支持的断言方式: " + assertionQO.getAssertionMode());
                    return false;
            }

            report.setIsSuccess(result ? "1" : "0");
            return result;
        } catch (NoSuchElementException e) {
            report.setErrorInfo("下拉框元素不存在: " + e.getMessage());
            report.setIsSuccess("0");
            return false;
        } catch (UnexpectedTagNameException e) {
            report.setErrorInfo("元素不是下拉框: " + e.getMessage());
            report.setIsSuccess("0");
            return false;
        } catch (Exception e) {
            report.setErrorInfo("下拉框断言异常: " + e.getMessage());
            report.setIsSuccess("0");
            return false;
        }
    }


    /**
     * 处理网页标题断言
     *
     * @param assertionQO   断言配置
     * @param seleniumUtils Selenium工具类
     * @param report        断言报告
     * @return 断言是否成功
     */
    private boolean handleTitleAssertion(AssertionQO assertionQO,
                                         SeleniumUtils seleniumUtils,
                                         AssertionReportVO report) {
        try {
            // 1. 获取实际标题和期望标题
            String actualTitle = seleniumUtils.getTitle();
            String expectedTitle = assertionQO.getExpectations();
            report.setErrorInfo("");

            // 3. 执行完全匹配比较
            boolean result = actualTitle.equals(expectedTitle);
            if (!result) {
                report.setErrorInfo(String.format(
                        "网页标题不匹配（实际：'%s'，期望：'%s'）",
                        actualTitle, expectedTitle
                ));
            }
            report.setIsSuccess(result ? "1" : "0");
            return result;
        } catch (Exception e) {
            report.setErrorInfo("获取网页标题失败: " + e.getMessage());
            report.setIsSuccess("0");
            return false;
        }
    }

    /**
     * 获取数据提取集合
     *
     * @param settingList
     * @return
     */
    public Map<String, Object> filterBydataExtractionQOList(List<UiHighSettingVO> settingList, SeleniumUtils seleniumUtils) {
        List<UiHighSettingVO> uiHighSettingVOS = settingList.stream()
                .filter(setting -> setting.getOperateType().equals("3") && setting.getIsDisabled() == 0)
                .toList();
        Map<String, Object> variableStorage = new HashMap<>();
        if (CollectionUtils.isEmpty(uiHighSettingVOS)) {
            return variableStorage;
        }
        for (UiHighSettingVO uiHighSettingVO : uiHighSettingVOS) {
            DataExtractionConfigQO dataExtractionQO = uiHighSettingVO.getDataExtractionQOList();
            List<DataExtractionQO> dataExtractionQOList = new ArrayList<>();
            dataExtractionQOList.addAll(dataExtractionQO.getElementExtractions());
            dataExtractionQOList.addAll(dataExtractionQO.getWindowExtractions());
            if (!CollectionUtils.isEmpty(dataExtractionQOList)) {
                for (DataExtractionQO extraction : dataExtractionQOList) {
                    if ("1".equals(extraction.getIsDisabled())) {
                        continue;
                    }
                    try {
                        String variableName = extraction.getValue();
                        String extractedValue = extractData(extraction, seleniumUtils);
                        variableStorage.put(variableName, extractedValue);
                        log.info("Extracted '{}' = {}", variableName, extractedValue);
                    } catch (NoSuchElementException e) {
                        log.info("Extracted '" + extraction.getValue() + "' = NoSuchElementException");
                        variableStorage.put(extraction.getValue(), "NoSuchElementException");
                    } catch (Exception e) {
                        log.error("Error extracting '" + extraction.getValue() + "': " + e.getMessage());
                    }
                }
            }

        }
        return variableStorage;
    }

    /**
     * 获取数据提取集合具体实现
     *
     * @param extraction
     * @param seleniumUtils
     * @return
     */
    private String extractData(DataExtractionQO extraction, SeleniumUtils seleniumUtils) {
        if ("1".equals(extraction.getInformationType())) {
            return seleniumUtils.extractWindowInformation(extraction.getWindowType());
        } else if ("2".equals(extraction.getInformationType())) {
            WebElement element = null;
            String locType = "";
            String elementLoc = "";
            if ("1".equals(extraction.getOperateObject())) {
                //元素定位
                UiElement uiElement = uiElementService.selectUiElementById(extraction.getOperateElementId());
                if (uiElement == null) {
                    return "";
                }
                locType = uiElement.getLocType();    //属性
                elementLoc = uiElement.getElementLoc();//值
            } else if ("2".equals(extraction.getOperateObject())) {
                locType = extraction.getOperateLocType();    //属性
                elementLoc = extraction.getOperateLocValue();//值
            }
            element = seleniumUtils.findElement(locType, elementLoc);
            switch (extraction.getElementType()) {
                case "1": // 普通对象
                    return extraction.getElementAttribute(); // 直接返回属性值
                case "2": // 元素文本
                    return element.getText();
                case "3": // 元素值
                    return element.getAttribute("value");
                case "4": // 元素属性
                    return element.getAttribute(extraction.getElementAttribute());
                case "5": // CSS属性
                    return element.getCssValue(extraction.getElementAttribute());
                case "6": // 匹配 xpath 的元素数量(storeXpathCount)
                    return String.valueOf(seleniumUtils.findElements(locType, elementLoc).size());
                default:
                    return "";
            }
        }
        return "";
    }


    /**
     * 获取(前置//后置)等待时间
     *
     * @param settingList
     * @return
     */
    private int getAwaitTime(List<UiHighSettingVO> settingList) {
        //获取前置等待时间
        int totalWaitTime = settingList.stream()
                .map(UiHighSettingVO::getAwateTime)
                .filter(Objects::nonNull)
                .filter(s -> !s.isEmpty())
                .mapToInt(s -> {
                    try {
                        return Integer.parseInt(s);
                    } catch (NumberFormatException e) {
                        return 0;
                    }
                })
                .sum();
        return totalWaitTime;
    }


    /**
     * 提取后置操作
     *
     * @param settings
     * @return
     */
    private List<UiHighSettingVO> extractafterSetting(List<UiHighSettingVO> settings) {
        return settings.stream()
                .filter(e -> "2".equals(e.getSettingType()) &&0 == e.getIsDisabled())
                .collect(Collectors.toList()); // 返回默认设置
    }

    /**
     * 提取错误处理设置
     *
     * @param settings
     * @return
     */
    private UiHighSettingVO extractErrorSetting(List<UiHighSettingVO> settings) {
        return settings.stream()
                .filter(e -> "3".equals(e.getSettingType()))
                .findFirst()
                .orElse(new UiHighSettingVO()); // 返回默认设置
    }


    /**
     * 提取错误处理设置
     *
     * @param settings
     * @return
     */
    private UiHighSettingVO extractOtherSetting(List<UiHighSettingVO> settings) {
        return settings.stream()
                .filter(e -> "4".equals(e.getSettingType()))
                .findFirst()
                .orElse(new UiHighSettingVO()); // 返回默认设置
    }


    //---------------------浏览器操作---------------------------

    /**
     * 打开网页
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    private void openWebPage(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        //追加页面，在新的页面打开url，不勾选覆盖当前url 0不追加 1追加
        Integer isAppendPage = step.getIsAppendPage() == null ? 0 : step.getIsAppendPage(); // 0=当前页，1=新标签页
        String url = step.getUrl();
        log.info("打开网页：{}, 模式：{}", url, isAppendPage == 1 ? "新标签页" : "当前页");
        if (isAppendPage != null && isAppendPage == 1) {
            // 新标签页打开
            seleniumUtils.openUrlInNewTab(url);
        } else {
            // 默认当前页打开
            seleniumUtils.openUrl(url);
        }
    }

    /**
     * 关闭网页
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    private void closeBrowser(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        log.info("关闭网页");
        seleniumUtils.closeCurrentWindow();
    }

    /**
     * 切换窗口
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    private void switchWindow(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        if (step.getOperate() == 1) {
            log.info("根据句柄 ID 切换到指定窗口");
            //句柄id
            String handleId = step.getHandleId();
            seleniumUtils.switchToWindowByHandle(handleId);
        } else if (step.getOperate() == 2) {
            log.info("根据网页索引号切换到指定窗口");
            //网页索引号
            Integer frameIndex = step.getFrameIndex();
            seleniumUtils.switchToWindowByIndex(frameIndex);
        } else if (step.getOperate() == 3) {
            log.info("切换到初始窗口");
            seleniumUtils.switchToDefaultWindow();
        } else {
            log.info("无效的窗口切换操作类型");
            throw new IllegalArgumentException("无效的窗口切换操作类型");
        }
    }

    /**
     * 设置窗口大小
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    private void resizeWindow(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        if (step.getOperate() == 1) {
            log.info("窗口最大化");
            //窗口最大化
            seleniumUtils.maximizeWindow();
        } else if (step.getOperate() == 2) {
            log.info("设置窗口大小");
            String windowSize = step.getWindowSize();
            String[] dimensions = windowSize.split("\\*");
            int width = Integer.parseInt(dimensions[0]);
            int height = Integer.parseInt(dimensions[1]);
            seleniumUtils.setWindowSize(width, height);
        } else {
            log.info("无效的设置窗口大小");
            throw new IllegalArgumentException("无效的设置窗口大小");
        }
    }


    /**
     * 选择内嵌网页
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    private void selectEmbeddedPage(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        if (step.getOperate() == 1) {
            log.info("退出当前 frame(回到主页面)");
            seleniumUtils.switchToDefaultContent();
        } else if (step.getOperate() == 2) {
            log.info("根据 frame 索引号切换到指定 frame");
            //网页索引号
            seleniumUtils.switchToFrameByIndex(step.getFrameIndex());
        } else if (step.getOperate() == 3) {
            log.info("根据定位方式切换 frame");
            if ("2".equals(step.getOperateObject())) {
                //元素对象
                String operateLocType = step.getOperateLocType();   //属性
                String operateLocValue = step.getOperateLocValue(); //值
                seleniumUtils.switchToFrameByLocator(
                        operateLocType,
                        operateLocValue
                );
            } else {
                //元素定位
                UiElement uiElement = uiElementService.selectUiElementById(step.getOperateElementId());
                if (uiElement == null) {
                    throw new IllegalArgumentException("未找到ID为 " + step.getOperateElementId() + " 的元素");
                }
                String locType = uiElement.getLocType();    //属性
                String elementLoc = uiElement.getElementLoc();//值
                seleniumUtils.switchToFrameByLocator(
                        locType,
                        elementLoc
                );
            }
        } else {
            log.info("无效的选择内嵌网页");
            throw new IllegalArgumentException("无效的选择内嵌网页");
        }
    }


    //--------------------弹窗操作--------------------

    /**
     * 弹窗操作
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    private void handlePopup(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        if (step.getOperate() == 1) {
            log.info("弹窗操作");
            // 获取输入内容（如果需要）
            String inputValue = "1".equals(step.getIsEnter()) ? step.getInputValue() : null;
            String operateWay = step.getOperateWay();   //操作方式 1确定 2取消
            // 执行弹窗操作
            seleniumUtils.handleAlert(
                    Integer.parseInt(operateWay), // 操作方式 (1=确定, 2=取消)
                    inputValue                              // 输入内容
            );
        } else {
            log.info("无效的弹窗操作");
            throw new IllegalArgumentException("无效的弹窗操作");
        }
    }

    //---------------------------元素操作----------------------------------

    /**
     * 提交表单
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    private void submitForm(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        if ("2".equals(step.getOperateObject())) {
            //元素对象
            String operateLocType = step.getOperateLocType();   //属性
            String operateLocValue = step.getOperateLocValue(); //值
            // 根据属性和值定位元素并提交表单
            seleniumUtils.findElement(operateLocType, operateLocValue).submit();
            log.info("通过元素对象提交表单，定位方式：{}，定位值：{}", operateLocType, operateLocValue);
        } else {
            //元素定位
            UiElement uiElement = uiElementService.selectUiElementById(step.getOperateElementId());
            if (uiElement == null) {
                throw new IllegalArgumentException("未找到ID为 " + step.getOperateElementId() + " 的元素");
            }
            String locType = uiElement.getLocType();    //属性
            String elementLoc = uiElement.getElementLoc();//值
            seleniumUtils.findElement(locType, elementLoc).submit();
            log.info("通过元素定位提交表单，定位方式：{}，定位值：{}", locType, elementLoc);
        }
    }


    /**
     * 下拉框操作
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    private void handleDropdown(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        log.info("下拉框操作");
        Integer operate = step.getOperate();    //操作 1选择 2取消
        WebElement dropdownElement;
        if ("2".equals(step.getOperateObject())) {
            //元素对象
            String operateLocType = step.getOperateLocType();   //属性
            String operateLocValue = step.getOperateLocValue(); //值
            dropdownElement = seleniumUtils.findElement(operateLocType, operateLocValue);
            log.info("通过元素对象定位下拉框，定位方式：{}，定位值：{}", operateLocType, operateLocValue);
        } else {
            //元素定位
            UiElement uiElement = uiElementService.selectUiElementById(step.getOperateElementId());
            if (uiElement == null) {
                throw new IllegalArgumentException("未找到ID为 " + step.getOperateElementId() + " 的元素");
            }
            String locType = uiElement.getLocType();    //属性
            String elementLoc = uiElement.getElementLoc();//值
            dropdownElement = seleniumUtils.findElement(locType, elementLoc);
            log.info("通过元素定位定位下拉框，定位方式：{}，定位值：{}", locType, elementLoc);
        }
        //子选项
        String subOptions = step.getSubOptions();   //1 选项 2索引 3值
        String subOptionsValue = step.getSubOptionsValue(); //值

        if (operate == 1) {
            // 选择操作
            seleniumUtils.selectDropdownOption(dropdownElement, subOptions, subOptionsValue);
        } else if (operate == 2) {
            // 取消操作
            // 如果需要取消下拉框的选择，可以使用默认选项或其他逻辑
            log.info("取消下拉框选择");
            // 示例：取消选择（假设下拉框的第一个选项是默认选项）
            seleniumUtils.selectDropdownOption(dropdownElement, "2", "0");
        } else {
            throw new IllegalArgumentException("无效的操作类型: " + operate);
        }
    }

    /**
     * 设置选项
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    private void setOption(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        log.info("设置选项");
        Integer operate = step.getOperate();    // 操作 1选择 2取消
        WebElement element;

        if ("2".equals(step.getOperateObject())) {
            // 元素对象
            String operateLocType = step.getOperateLocType();   // 属性
            String operateLocValue = step.getOperateLocValue(); // 值
            element = seleniumUtils.findElement(operateLocType, operateLocValue);
            log.info("通过元素对象定位选项，定位方式：{}，定位值：{}", operateLocType, operateLocValue);
        } else {
            // 元素定位
            UiElement uiElement = uiElementService.selectUiElementById(step.getOperateElementId());
            if (uiElement == null) {
                log.error("未找到指定的元素，元素ID：{}", step.getOperateElementId());
                throw new IllegalArgumentException("未找到指定的元素");
            }
            String locType = uiElement.getLocType();    // 属性
            String elementLoc = uiElement.getElementLoc(); // 值
            element = seleniumUtils.findElement(locType, elementLoc);
            log.info("通过元素定位定位选项，定位方式：{}，定位值：{}", locType, elementLoc);
        }

        if (operate == 1) {
            // 选择操作
            if (!element.isSelected()) {
                element.click();
                log.info("设置选项为选中状态");
            }
        } else if (operate == 2) {
            // 取消操作
            if (element.isSelected()) {
                element.click();
                log.info("设置选项为未选中状态");
            }
        } else {
            throw new IllegalArgumentException("无效的操作类型: " + operate);
        }
    }


    /**
     * 等待元素
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    private void waitForElement(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        log.info("等待元素");
        //操作 1等待元素等于给定的定值(Text) 2 等待元素存在 3等待元素显示 4等待元素不显示 5等待元素不存在 6等待元素可编辑 7等待元素不可编辑
        By locator;
        if ("2".equals(step.getOperateObject())) {
            //元素对象
            String operateLocType = step.getOperateLocType();   //属性
            String operateLocValue = step.getOperateLocValue(); //值
            // 使用直接指定的定位方式
            locator = seleniumUtils.getLocator(
                    operateLocType,
                    operateLocValue
            );
        } else {
            //元素定位
            UiElement uiElement = uiElementService.selectUiElementById(step.getOperateElementId());
            String locType = uiElement.getLocType();    //属性
            String elementLoc = uiElement.getElementLoc();//值
            locator = seleniumUtils.getLocator(
                    locType,
                    elementLoc
            );
        }
        // 解析等待时间（默认15000ms）
        long timeout = 15000;
        if (step.getOperate() != 1 && step.getAwaitValue() != null) {
            try {
                timeout = Long.parseLong(step.getAwaitValue());
            } catch (NumberFormatException e) {
                log.warn("等待时间格式错误，使用默认值15000ms");
            }
        }
        // 执行等待操作
        seleniumUtils.waitForElement(
                locator,
                step.getOperate(),
                step.getOperate() == 1 ? step.getAwaitValue() : null,
                timeout
        );
    }


    //--------------------------鼠标操作------------------------------


    /**
     * 鼠标点击操作
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    private void mouseClick(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        try {
            log.info("执行鼠标操作，方式: {}", step.getClickMethod());
            // 获取定位器
            By locator;
            if ("2".equals(step.getOperateObject())) {
                // 使用直接指定的定位方式
                locator = seleniumUtils.getLocator(
                        step.getOperateLocType(),
                        step.getOperateLocValue()
                );
            } else {
                // 使用元素ID从数据库获取定位信息
                UiElement uiElement = uiElementService.selectUiElementById(step.getOperateElementId());
                if (uiElement == null) {
                    throw new IllegalArgumentException("未找到ID为 " + step.getOperateElementId() + " 的元素");
                }
                locator = seleniumUtils.getLocator(
                        uiElement.getLocType(),
                        uiElement.getElementLoc()
                );
            }
            // 执行鼠标操作
            seleniumUtils.performMouseAction(
                    locator,
                    Integer.parseInt(step.getClickMethod())
            );
        } catch (NumberFormatException e) {
            log.error("鼠标操作类型格式错误: {}", step.getClickMethod());
            throw new RuntimeException("鼠标操作类型必须是数字(1-5)", e);
        } catch (Exception e) {
            log.error("鼠标操作执行失败", e);
            throw new RuntimeException("鼠标操作失败: " + e, e);
        }
    }

    /**
     * 鼠标移动操作
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    private void mouseMove(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        try {
            log.info("执行鼠标移动操作，方式: {}", step.getClickMethod());
            // 获取定位器
            By locator;
            if ("2".equals(step.getOperateObject())) {
                // 使用直接指定的定位方式
                locator = seleniumUtils.getLocator(
                        step.getOperateLocType(),
                        step.getOperateLocValue()
                );
            } else {
                // 使用元素ID从数据库获取定位信息
                UiElement uiElement = uiElementService.selectUiElementById(step.getOperateElementId());
                if (uiElement == null) {
                    throw new IllegalArgumentException("未找到ID为 " + step.getOperateElementId() + " 的元素");
                }
                locator = seleniumUtils.getLocator(
                        uiElement.getLocType(),
                        uiElement.getElementLoc()
                );
            }
            // 执行鼠标移动操作
            seleniumUtils.performMouseMove(
                    locator,
                    Integer.parseInt(step.getClickMethod()) // 1=移出，2=移入
            );


        } catch (NumberFormatException e) {
            log.error("鼠标移动类型格式错误: {}", step.getClickMethod());
            throw new RuntimeException("鼠标移动类型必须是数字(1-2)", e);
        } catch (Exception e) {
            log.error("鼠标移动操作执行失败", e);
            throw new RuntimeException("鼠标移动操作失败: " + e, e);
        }
    }

    /**
     * 输入操作
     *
     * @param step          步骤对象
     * @param seleniumUtils Selenium工具类
     */
    public void inputText(UiSceneSteps step, SeleniumUtils seleniumUtils) {
        try {
            log.info("执行输入操作，类型: {}", step.getOperate());

            // 获取定位器
            By locator;
            if ("2".equals(step.getOperateObject())) {
                // 使用直接指定的定位方式
                locator = seleniumUtils.getLocator(
                        step.getOperateLocType(),
                        step.getOperateLocValue()
                );
            } else {
                // 使用元素ID从数据库获取定位信息
                UiElement uiElement = uiElementService.selectUiElementById(step.getOperateElementId());
                if (uiElement == null) {
                    throw new IllegalArgumentException("未找到ID为 " + step.getOperateElementId() + " 的元素");
                }
                locator = seleniumUtils.getLocator(
                        uiElement.getLocType(),
                        uiElement.getElementLoc()
                );
            }

            // 获取输入内容
            String inputValue = step.getInputValue();
            if (inputValue == null || inputValue.trim().isEmpty()) {
                log.warn("输入内容为空，将执行清空操作");
                seleniumUtils.clearInput(locator);
                return;
            }
            // 执行输入操作
            if (step.getOperate() == 1) { // 普通输入框
                seleniumUtils.inputText(
                        locator,
                        inputValue,
                        true, // 默认先清空
                        1    // 默认使用普通输入方式
                );
            } else if (step.getOperate() == 2) { // 可编辑段落
                seleniumUtils.inputToEditableDiv(
                        locator,
                        inputValue
                );
            } else {
                throw new IllegalArgumentException("不支持的输入操作类型: " + step.getOperate());
            }

        } catch (Exception e) {
            log.error("输入操作执行失败", e);
            throw new RuntimeException("输入操作失败: " + e, e);
        }
    }

    /**
     * 文件上传
     *
     * @param step
     * @param seleniumUtils
     */
    public void uploadFile(UiSceneSteps step, SeleniumUtils seleniumUtils) {

    }


}