/*
package cn.sikey.selenium.api;

import cn.sikey.framework.common.exception.ServiceException;
import cn.sikey.framework.common.pojo.CommonResult;
import cn.sikey.selenium.api.selenium.SmsApi;
import cn.sikey.selenium.api.selenium.dto.AuthRespVO;
import cn.sikey.selenium.api.selenium.dto.TriggerSmsReqDTO;
import cn.sikey.selenium.api.selenium.dto.VerifyCodeReqDTO;
import cn.sikey.selenium.util.AppIdGeneratorUtil;
import cn.sikey.selenium.util.PlaywrightContextManagerUtil;
import cn.sikey.selenium.util.PlaywrightContextManagerUtil.PlaywrightSession;
import com.microsoft.playwright.*;
import com.microsoft.playwright.options.WaitUntilState;
import io.netty.handler.timeout.TimeoutException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

*/
/**
 * @Author: nelson
 * @Date: 2025/6/26
 * @Description: 验证码
 *//*

@Slf4j
@RestController // 提供 RESTful API 接口，给 Feign 调用
@Validated
public class SmsNewServiceImpl implements SmsApi {

    @Value("${selenium.remote.url}")
    private String url;

    @Value("${selenium.remote.loginUrl}")
    private String loginUrl;

    @Value("${selenium.remote.redirectUrl}")
    private String redirectUrl;

    public static final String ACCESS_TOKEN1 = "/app-api/mcdisk/wristwatch/authorize/accessToken1";

    @Value("${selenium.remote.appid}")
    private String appid;

    @Value("${selenium.remote.appkey}")
    private String appkey;

    public static final String APP_TITLE = "移动云盘";

    public static final int GET_VERIFICATION_CODE_AGAIN = 10001;

    public static final int UUID_EXPIRES_MINUTE = 15;

    public static final String SMS_CODE_KEY = "sms:code:phone:";

    public static final String SMS_SESSION_ID_KEY = "sms:session:id:";

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 超时时间（秒）
    private static final long SMS_TIMEOUT = 30;

    // 最大重试次数
    private static final int MAX_RETRY = 3;

    */
/**
     * 发送验证码
     *
     * @param request 请求
     * @return
     *//*

    @Override
    public CommonResult<Void> triggerSmsVerification(TriggerSmsReqDTO request) {
        String uuid = AppIdGeneratorUtil.generateUuid(appid);
        PlaywrightSession session;

        String phoneNumber = request.getPhoneNumber();
        String deviceId = request.getTicket();

        try {
            // 创建 Playwright 会话（使用 Chromium 浏览器）
            String sessionId = PlaywrightContextManagerUtil.createSession(url);
            session = PlaywrightContextManagerUtil.getSession(sessionId);
            Page page = session.getPage();

            // 设置导航超时和操作超时
            page.setDefaultNavigationTimeout(TimeUnit.SECONDS.toMillis(SMS_TIMEOUT));
            page.setDefaultTimeout(TimeUnit.SECONDS.toMillis(SMS_TIMEOUT));

            // 构建登录URL
            String encodedUrl = URLEncoder.encode(redirectUrl + ACCESS_TOKEN1 + "?uuid=" + uuid + "&ticket=" + deviceId, StandardCharsets.UTF_8);
            String toLoginUrl = String.format("%s?pageType=3&appId=%s&appKey=%s&deviceId=%s&appTitle=%s&uuid=%s&redirectUrl=%s", loginUrl, appid, appkey, deviceId, APP_TITLE, uuid, encodedUrl);

            log.info("[发送验证码]获取移动云盘地址：{}", toLoginUrl);

            // 导航到登录页，等待网络空闲
            Response response = page.navigate(toLoginUrl, new Page.NavigateOptions().setWaitUntil(WaitUntilState.NETWORKIDLE));

            if (!response.ok()) {
                throw new ServiceException(HttpStatus.SC_BAD_GATEWAY, "页面加载失败，状态码: " + response.status());
            }

            // 确保页面包含关键元素
            page.waitForSelector("//input[@type='number' and @maxlength='11']", new Page.WaitForSelectorOptions().setTimeout(SMS_TIMEOUT * 1000));

            // 输入手机号 - 使用精确选择器
            Locator phoneInput = page.locator("xpath=//input[@type='number' and @maxlength='11']");
            phoneInput.fill(phoneNumber);

            // 获取验证码按钮 - 使用文本定位
            Locator codeBtn = page.locator("button:has-text('获取验证码')");

            // 重试机制处理可能的点击失败
            boolean clickSuccess = false;
            for (int i = 0; i < MAX_RETRY && !clickSuccess; i++) {
                try {
                    // 确保按钮可见
                    codeBtn.scrollIntoViewIfNeeded();

                    // 使用 Playwright 的点击（带自动等待）
                    codeBtn.click(new Locator.ClickOptions().setTimeout(5000) // 5秒超时
                            .setForce(true)); // 强制点击

                    clickSuccess = true;
                    log.info("[发送验证码]第{}次点击成功", i + 1);
                } catch (PlaywrightException e) {
                    log.warn("[发送验证码]点击失败，重试中... 原因: {}", e.getMessage());

                    // 等待可能的状态更新
                    page.waitForTimeout(1000); // 1秒等待
                }
            }

            if (!clickSuccess) {
                throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "无法点击验证码按钮");
            }

            // 检查是否出现成功提示
            Locator successTip = page.locator("text=验证码已发送");
            if (successTip.isVisible(new Locator.IsVisibleOptions().setTimeout(3000))) {
                log.info("[发送验证码]短信验证码已发送，手机号：{}", phoneNumber);
            } else {
                // 检查错误提示
                Locator errorTip = page.locator("div.el-message--error, div.error-message");
                if (errorTip.isVisible()) {
                    throw new ServiceException(HttpStatus.SC_BAD_REQUEST, "发送失败: " + errorTip.textContent());
                }
                log.warn("[发送验证码]未检测到成功提示，但可能已发送");
            }

            // 存储到Redis
            redisTemplate.opsForValue().set(SMS_CODE_KEY + phoneNumber, uuid, Duration.ofMinutes(UUID_EXPIRES_MINUTE));

            redisTemplate.opsForValue().set(SMS_SESSION_ID_KEY + phoneNumber, sessionId, Duration.ofMinutes(UUID_EXPIRES_MINUTE));
        } catch (Exception e) {
            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "触发短信验证码失败: " + e.getMessage());
        }

        return CommonResult.success();
    }

    */
/**
     * 验证码登录
     *
     * @param request 校验验证码登录
     * @return
     *//*

    @Override
    public CommonResult<AuthRespVO> verifyCodeAndLogin(VerifyCodeReqDTO request) {
        String phoneNumber = request.getPhoneNumber();
        Object uuid = redisTemplate.opsForValue().get(SMS_CODE_KEY + phoneNumber);
        if (Objects.isNull(uuid)) {
            throw new ServiceException(GET_VERIFICATION_CODE_AGAIN, "请重新获取验证码");
        }

        Object sessionId = redisTemplate.opsForValue().get(SMS_SESSION_ID_KEY + phoneNumber);
        if (Objects.isNull(sessionId)) {
            throw new ServiceException(GET_VERIFICATION_CODE_AGAIN, "会话已过期，请重新获取验证码");
        }

        String deviceId = request.getTicket();
        String verificationCode = request.getVerificationCode();
        String sId = String.valueOf(sessionId);
        PlaywrightSession session;

        try {
            // 获取Playwright会话
            session = PlaywrightContextManagerUtil.getSession(sId);
            Page page = session.getPage();

            // 设置超时
            page.setDefaultTimeout(TimeUnit.SECONDS.toMillis(SMS_TIMEOUT));

            // 构建登录URL
            String encodedUrl = redirectUrl + ACCESS_TOKEN1;
            String toLoginUrl = String.format(
                    "%s?pageType=3&appId=%s&appKey=%s&deviceId=%s&appTitle=%s&uuid=%s&redirectUrl=%s",
                    loginUrl, appid, appkey, deviceId, APP_TITLE, uuid, encodedUrl
            );

            log.info("[校验验证码登录]获取移动云盘地址：{}", toLoginUrl);

            // 如果当前页面不是登录页，则导航到登录页
            if (!page.url().contains("middlePage")) {
                page.navigate(toLoginUrl, new Page.NavigateOptions()
                        .setWaitUntil(WaitUntilState.NETWORKIDLE));

                // 等待页面关键元素
                page.waitForSelector("//input[@type='number' and @maxlength='11']",
                        new Page.WaitForSelectorOptions().setTimeout(5000));
            }

            // 输入验证码
            Locator codeInput = page.locator("xpath=//input[@placeholder='输入验证码' and @maxlength='6']");
            if (!codeInput.isVisible()) {
                log.warn("验证码输入框不可见，尝试滚动到视图中");
                codeInput.scrollIntoViewIfNeeded();
            }
            codeInput.fill(verificationCode);

            // 勾选协议
            Locator agreementLabel = page.locator("xpath=//div[contains(@class,'login_agreementGroup')]//label[contains(@class,'el-checkbox')]");

            // 确保协议复选框可见
            if (!agreementLabel.isVisible()) {
                agreementLabel.scrollIntoViewIfNeeded();
            }

            // 检查是否已勾选
            Locator agreementCheckbox = page.locator("xpath=//div[contains(@class,'login_agreementGroup')]//input[@type='checkbox']");
            if (!"true".equals(agreementCheckbox.getAttribute("aria-checked"))) {
                log.info("尝试勾选协议复选框");

                // 重试勾选协议
                boolean agreementChecked = false;
                for (int i = 0; i < MAX_RETRY && !agreementChecked; i++) {
                    try {
                        agreementLabel.click(new Locator.ClickOptions()
                                .setForce(true)
                                .setTimeout(3000));

                        // 验证是否勾选成功
                        if ("true".equals(agreementCheckbox.getAttribute("aria-checked"))) {
                            agreementChecked = true;
                            log.info("协议勾选成功");
                        } else {
                            log.warn("协议未勾选，尝试JavaScript点击");
                            page.evaluate("document.querySelector('div.login_agreementGroup input[type=checkbox]').click()");
                        }
                    } catch (PlaywrightException e) {
                        log.warn("勾选协议失败，重试中... ({}/3)", i + 1);
                        page.waitForTimeout(1000); // 等待1秒
                    }
                }

                if (!agreementChecked) {
                    throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR,
                            "无法勾选协议复选框");
                }
            }

            // 3. 点击登录按钮
            Locator loginButton = page.locator("xpath=//button[contains(@class, 'login_btn') " +
                    "and not(contains(@class, 'one_key_login_btn')) " +
                    "and .//span[normalize-space()='登录'] " +
                    "and not(@disabled)]");

            // 确保按钮可见
            if (!loginButton.isVisible()) {
                loginButton.scrollIntoViewIfNeeded();
            }

            // 重试点击登录按钮
            boolean loginClicked = false;
            for (int i = 0; i < MAX_RETRY && !loginClicked; i++) {
                try {
                    loginButton.click(new Locator.ClickOptions()
                            .setForce(true)
                            .setTimeout(5000));
                    loginClicked = true;
                    log.info("登录按钮点击成功");
                } catch (PlaywrightException e) {
                    log.warn("登录按钮点击失败，重试中... ({}/3)", i + 1);
                    page.waitForTimeout(1000); // 等待1秒

                    // 检查按钮是否被禁用
                    if ("true".equals(loginButton.getAttribute("disabled"))) {
                        log.warn("登录按钮被禁用，检查协议是否勾选");
                        if (!"true".equals(agreementCheckbox.getAttribute("aria-checked"))) {
                            agreementLabel.click(new Locator.ClickOptions().setForce(true));
                        }
                    }
                }
            }

            if (!loginClicked) {
                throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR,
                        "无法点击登录按钮");
            }

            // 验证登录成功
            verifyLoginSuccess(page);

            log.info("[校验验证码登录]登录成功，手机号：{}，验证码：{}", phoneNumber, verificationCode);
        } catch (Exception e) {
            log.error("[校验验证码登录]异常：{0}", e);
            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "触发校验验证码登录失败");
        } finally {
            // 清理会话
            if (sessionId != null) {
                PlaywrightContextManagerUtil.destroySession(sId);

                // 清理Redis中的会话信息
                redisTemplate.delete(SMS_CODE_KEY + phoneNumber);
                redisTemplate.delete(SMS_SESSION_ID_KEY + phoneNumber);
            }
        }
        return null;
    }

    // 验证登录成功
    private void verifyLoginSuccess(Page page) {
        try {
            // 验证URL变化 - 等待重定向
            page.waitForURL(url ->
                            url.contains("/home") ||
                                    url.contains("/dashboard") ||
                                    url.contains("/console"),
                    new Page.WaitForURLOptions().setTimeout(15000));

            log.info("[校验验证码登录]登录成功！当前URL：{}", page.url());

            // 验证用户信息元素
            Locator userInfo = page.locator("xpath=//div[contains(@class, 'user-info') or contains(@class, 'user-name')]");
            if (userInfo.isVisible(new Locator.IsVisibleOptions().setTimeout(5000))) {
                log.info("[校验验证码登录]登录成功！用户名：{}", userInfo.textContent());
                return;
            }

            // 检查认证Cookie
            BrowserContext context = page.context();
            String authToken = context.cookies().stream()
                    .filter(cookie ->
                            "CMCC-TOKEN".equals(cookie.name) ||
                                    "CMCC_SESSION".equals(cookie.name))
                    .findFirst()
                    .map(cookie -> cookie.value)
                    .orElse(null);

            if (authToken != null) {
                log.info("[校验验证码登录]登录成功！认证Token存在");
                return;
            }

            // 检查本地存储
            Object localStorageToken = page.evaluate(
                    "() => localStorage.getItem('CMCC_AUTH_TOKEN') || " +
                            "localStorage.getItem('access_token')");

            if (localStorageToken != null) {
                log.info("[校验验证码登录]登录成功！本地存储Token存在");
                return;
            }

            // 如果以上验证都失败，尝试检查错误
            checkLoginErrors(page);

            // 最终验证失败
            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR,
                    "无法确认登录状态，但未检测到错误");

        } catch (TimeoutException e) {
            // 处理登录超时
            checkLoginErrors(page);
            throw new ServiceException(HttpStatus.SC_REQUEST_TIMEOUT, "登录验证超时");
        }
    }

    // 检查登录错误
    private void checkLoginErrors(Page page) {
        // 检查错误消息
        Locator errorTip = page.locator("div.el-message--error, div.error-message");
        if (errorTip.isVisible(new Locator.IsVisibleOptions().setTimeout(3000))) {
            String errorText = errorTip.textContent();
            log.error("[校验验证码登录]登录失败！原因：{}", errorText);

            if (errorText.contains("验证码") || errorText.contains("验证码错误")) {
                throw new ServiceException(HttpStatus.SC_BAD_REQUEST, "验证码错误，请重新输入");
            } else if (errorText.contains("账号") || errorText.contains("用户不存在")) {
                throw new ServiceException(HttpStatus.SC_BAD_REQUEST, "账号不存在或密码错误");
            } else if (errorText.contains("协议") || errorText.contains("未同意")) {
                throw new ServiceException(HttpStatus.SC_BAD_REQUEST, "请同意用户协议");
            }
            throw new ServiceException(HttpStatus.SC_BAD_REQUEST, "登录失败: " + errorText);
        }

        // 检查表单错误
        Locator formErrors = page.locator("div.el-form-item__error");
        if (formErrors.isVisible()) {
            String errorText = formErrors.first().textContent();
            log.error("[校验验证码登录]表单错误：{}", errorText);
            throw new ServiceException(HttpStatus.SC_BAD_REQUEST, "表单错误: " + errorText);
        }

        // 检查系统错误提示
        Locator systemErrors = page.locator("text=/状态码|错误码|系统异常/");
        if (systemErrors.isVisible()) {
            String errorText = systemErrors.textContent();
            log.error("[校验验证码登录]系统错误：{}", errorText);
            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "系统错误: " + errorText);
        }

        // 检查页面内容中的错误关键字
        String pageContent = page.content();
        if (pageContent.contains("错误") || pageContent.contains("exception")) {
            log.error("[校验验证码登录]页面包含错误信息关键字");
            throw new ServiceException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "系统异常，请稍后重试");
        }
    }

}

*/
