import Msg from "../utils/msg";
import {
    RefreshToken,
    PackageName,
    ProductId,
    IosUrl,
    Account,
    ClientSecret,
    ClientId,
    ORDER_SIGN_KEY,
} from "../config/thirdParams";
import {
    generateOrderNumber,
    formatDate,
    getServerList,
    getClientIp, getServerAddr,
} from "../utils/common";
import {PaymentHelper} from "../utils/PaymentHelper";
import {SignatureVerifier} from "../utils/SignatureVerifier";
import {ChannelConfigManager} from "../utils/ChannelConfigManager";
import {getRoleInfoByUidAndServerId, getRoleListByUserId} from "../mongo/mongodb";
import {min} from "moment-timezone";

// platform内存缓存，key: ip:device_no，TTL 5分钟
const platformCache = new Map<string, { value: string; expireAt: number }>();
const PLATFORM_CACHE_TTL = 60 * 1000; // 缓存1分钟

function setPlatformCache(ip: string, platform: string) {
    platformCache.set(ip, {value: platform, expireAt: Date.now() + PLATFORM_CACHE_TTL});
}

function getPlatformCache(ip: string): string {
    const entry = platformCache.get(ip);
    if (!entry) return '';
    if (Date.now() > entry.expireAt) {
        platformCache.delete(ip);
        return '';
    }
    return entry.value;
}

// 导入依赖
const CryptoJS = require("crypto-js");
const Order = require("../model/OrderModel");
const Server = require("../model/ServerModel");
const System = require("../model/SystemModel");
const User = require("../model/UserModel");
const logger = require("../utils/log");
const axios = require("axios");
const {channelFactory} = require("../channels/factory/ChannelFactory");
const {MianyouChannelHandler} = require("../channels/handlers/MianyouChannelHandler");
import {getWebgameAuthInfo} from "../utils/webgameAuthCache";

/**
 * Google支付回调处理
 */
const googleCallPay = async (ctx, config) => {
    let data = ctx.request.body;
    let orderId = data.orderId;
    let googleToken = data.purchaseToken;
    let out_trade_no = "";

    logger.info("Google支付回调参数:", {url: ctx.href, params: data});

    // 更新订单Token
    await Order.updateOrderToken(orderId, googleToken, "google");

    // 获取Google访问令牌
    const redisClient = ctx.redis.client;
    let access_token = await redisClient.get("access_token");

    if (!access_token) {
        logger.info("请求Google API获取token");
        const apiData = {
            grant_type: "refresh_token",
            client_id: ClientId,
            client_secret: ClientSecret,
            refresh_token: RefreshToken,
        };

        try {
            const response = await axios.post(
                "https://accounts.google.com/o/oauth2/token",
                apiData,
                {
                    headers: {
                        "Content-Type": "application/x-www-form-urlencoded",
                    },
                }
            );

            logger.info("Google token响应:", {data: response.data});

            if (!response.data.access_token) {
                return PaymentHelper.DEFAULT_RESULT;
            }

            access_token = response.data.access_token;
            await redisClient.set("access_token", response.data.access_token);
            await redisClient.expire("access_token", 1800);
        } catch (error) {
            logger.error("获取Google token失败:", error);
            return PaymentHelper.DEFAULT_RESULT;
        }
    }

    if (!access_token) {
        return PaymentHelper.DEFAULT_RESULT;
    }

    // 验证订单
    const validation = await PaymentHelper.validateOrder(orderId);
    if (!validation.valid) {
        return {
            code: validation.message?.includes("重复发货") ? 1 : 0,
            msg: validation.message,
        };
    }

    const orderInfo = validation.orderInfo;

    // 验证Google订单
    const productId = ProductId + orderInfo.product_id;
    let apiUrl = `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/${PackageName}/purchases/products/${productId}/tokens/${googleToken}?access_token=${access_token}`;
    let maxRetries = 3;
    let currentRetry = 0;
    let isCheck = false;

    // 使用循环进行重试
    while (currentRetry < maxRetries) {
        try {
            const googleRes = await axios.get(apiUrl, {
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded",
                },
            });

            logger.info("Google验证响应:", googleRes.data);

            if (googleRes.data.purchaseState == 0) {
                out_trade_no = googleRes.data.orderId;
                isCheck = true;
                break;
            }
        } catch (error) {
            logger.error("Google验证请求失败:", error);
        }

        currentRetry++;
    }

    if (!isCheck) {
        return PaymentHelper.DEFAULT_RESULT;
    }

    // 发货处理
    const result = await PaymentHelper.deliverOrder(
        orderInfo,
        ctx.request.ip,
        validation.url,
        out_trade_no
    );

    // 确认和消费Google订单
    try {
        // 确认订单
        let acknowledgeUrl = `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/${PackageName}/purchases/products/${productId}/tokens/${googleToken}:acknowledge?access_token=${access_token}`;
        await axios.post(
            acknowledgeUrl,
            {developerPayload: ""},
            {headers: {"Content-Type": "application/json"}}
        );

        // 消费订单
        let consumeUrl = `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/${PackageName}/purchases/products/${productId}/tokens/${googleToken}:consume?access_token=${access_token}`;
        await axios.post(
            consumeUrl,
            {developerPayload: ""},
            {headers: {"Content-Type": "application/json"}}
        );

        logger.info("Google订单处理完成:", {orderId: orderId});
    } catch (error) {
        logger.error("Google订单确认/消费失败:", error);
        // 即使确认/消费失败，我们仍然返回发货结果，因为发货可能已经成功
    }

    return result;
};

/**
 * Apple支付回调处理
 */
const appleCallPay = async (ctx, config) => {
    let data = ctx.request.body;
    logger.info("Apple支付回调参数:", {url: ctx.href, params: data});

    if (!data.purchaseToken || !data.orderId) {
        return PaymentHelper.DEFAULT_RESULT;
    }

    let receipt_data = data.purchaseToken.replace(/ /g, "+");
    let orderId = data.orderId;
    let out_trade_no = orderId;

    // 更新订单Token
    await Order.updateOrderToken(orderId, receipt_data, "apple");

    // 验证Apple收据
    let maxRetries = 5;
    let currentRetry = 0;
    let isCheck = false;

    while (currentRetry < maxRetries) {
        try {
            const apiData = {
                "receipt-data": receipt_data,
            };

            const response = await axios.post(IosUrl, apiData, {
                headers: {
                    "Content-Type": "application/json",
                },
            });

            logger.info(`Apple验证响应状态:${response.data.status}`);

            if (response.data.status == 0) {
                isCheck = true;
                break;
            }
        } catch (error) {
            logger.error("Apple验证请求失败:", error);
        }

        currentRetry++;
    }

    if (!isCheck) {
        logger.info(`Apple票据验证失败！`);
        return {code: 0, msg: "票据验证失败！"};
    }

    // 验证订单
    const validation = await PaymentHelper.validateOrder(orderId);
    if (!validation.valid) {
        return {
            code: validation.message?.includes("重复发货") ? 1 : 0,
            msg: validation.message,
        };
    }

    const orderInfo = validation.orderInfo;

    // 发货处理
    logger.info(`订单${orderId}通知游戏发货开始`);
    const result = await PaymentHelper.deliverOrder(
        orderInfo,
        ctx.request.ip,
        validation.url,
        out_trade_no
    );
    logger.info(`订单${orderId}通知游戏发货结束，结果:`, result);

    return result;
};

function splitString(input: string, separator: string): string[] {
    return input.split(separator);
}

//验证账号
const checkUserToken = async (ctx) => {
    let ip = getClientIp(ctx);
    let {
        uid,
        channel_id,
        device_no,
        reg_device,
        device_type,
        device_model,
        device_version,
        system_version,
        platform = ''
    } = ctx.request.body;

    // 前端未传platform时，从内存缓存（thirdLogin写入）中查询补全
    if (channel_id == 17 && !platform && ip) {
        platform = getPlatformCache(ip);
        logger.info("从缓存获取platform:", {platform, ip});
    }

    const create_time = formatDate(new Date());
    const accountInfo = (await User.checkAccountIsExist(uid, channel_id))[0];

    let accountRes = null;
    let res = null;
    if (!accountInfo) {
        accountRes = await User.createAccount(
            uid,
            channel_id,
            ip,
            device_no,
            reg_device,
            create_time,
            platform
        );

        if (accountRes.affectedRows <= 0) {
            return ApiController.fail("添加账户失败");
        }
    }

    res = await User.logAccountLogin(
        uid,
        ip,
        device_type,
        device_no,
        device_model,
        device_version,
        system_version,
        create_time,
        channel_id,
        platform,
    );

    if (res.affectedRows <= 0) {
        return ApiController.fail("添加日志失败");
    }

    return ApiController.success("请求成功", 1, false, {ip: ip});
};

class ApiController {
    /**
     * 生成成功响应
     * @param msg 成功消息
     * @param code 成功码，默认为1（部分API使用0表示成功）
     * @param useMessage 是否使用message字段而不是msg字段，默认为false
     * @param data 响应数据
     * @returns 标准化的成功响应对象
     */
    static success(
        msg: string = "请求成功",
        code: number = 1,
        useMessage: boolean = false,
        data: any = null
    ) {
        return {
            code: code,
            [useMessage ? "message" : "msg"]: msg,
            data: data,
        };
    }

    /**
     * 生成失败响应
     * @param msg 错误消息
     * @param code 错误码，默认为0
     * @param data 额外的错误数据
     * @param useMessage 是否使用message字段而不是msg字段，默认为false
     * @returns 标准化的失败响应对象
     */
    static fail(
        msg: string = "请求失败",
        code: number = 0,
        data: any = null,
        useMessage: boolean = false
    ) {
        return {
            code: code,
            [useMessage ? "message" : "msg"]: msg,
            data: data,
        };
    }

    async createOrder(ctx) {
        let {
            uid,
            level,
            amount,
            role_id,
            role_name,
            product_id,
            server_id,
            channel_id,
            platform = '',
        } = ctx.request.body;

        logger.info("create params:", {params: ctx.request.body});

        if (
            !product_id ||
            !server_id ||
            !role_name ||
            !role_id ||
            !amount ||
            !uid ||
            !channel_id
        ) {
            ctx.body = ApiController.fail("参数错误,创建订单失败!!", -1);
            return;
        }

        const data = ctx.request.body;
        if (!SignatureVerifier.verifyMD5Sign(data, ORDER_SIGN_KEY)) {
            logger.error("创建订单签名验证失败");
            ctx.body = ApiController.fail("签名错误,创建订单失败!!", -1);
            return;
        }

        if (amount <= 0) {
            ctx.body = ApiController.fail("金额错误,创建订单失败!!", -1);
            return;
        }

        const orderId = generateOrderNumber(); // 生成一个长度为8的订单号
        const create_time = formatDate(new Date());
        const res = await Order.createOrder(
            orderId,
            uid,
            level,
            amount,
            role_id,
            role_name,
            product_id,
            server_id,
            channel_id,
            create_time,
            platform
        );

        if (res.affectedRows > 0) {
            ctx.body = ApiController.success("创建订单成功", 0, false, orderId);
        } else {
            ctx.body = ApiController.fail("创建订单失败", -1, null);
        }

        logger.info("创建订单返回结果:", {params: ctx.body});
    }

    async checkUserToken(ctx) {
        let {channel_id} = ctx.request.body;
        const channelConfig = ChannelConfigManager.getConfig(channel_id);
        if (!channelConfig) {
            logger.info("未找到渠道配置:", {channel_id});
            ctx.body = {
                code: 0,
                msg: "未知渠道id",
            };
            return;
        }

        var result = await checkUserToken(ctx);
        ctx.body = result;
    }

    async thirdLogin(ctx) {
        const data = ctx.request.body;
        let channelId = parseInt(data.channel_id) || 1;
        const platform = data.platform; // 平台参数：ios、Android、miniapp、h5

        logger.info("thirdLogin请求参数:", {data, channelId, platform});

        // 渠道11和渠道12数据打通，统一使用渠道11
        if (channelId === 12) {
            channelId = 11;
            logger.info("渠道12映射到渠道11，平台参数:", platform);
        }

        // 获取渠道配置
        let channelConfig = ChannelConfigManager.getConfig(channelId);

        if (data.is_new == 1) {
            if (channelId == 6) {
                channelConfig = ChannelConfigManager.getConfig(999);
            }

            if (channelId == 1) {
                channelConfig = ChannelConfigManager.getConfig(9);
            }
        }

        if (!channelConfig) {
            logger.info("未找到渠道配置:", {channelId});
            ctx.body = ApiController.fail("未知渠道id");
            return;
        }

        // 从工厂获取对应的渠道处理器
        const handler = channelFactory.getHandler(channelId);

        if (!handler) {
            logger.info("未找到渠道处理器:", {channelId});
            ctx.body = ApiController.fail("未知渠道id");
            return;
        }

        logger.info("使用渠道处理器处理登录:", {
            channelId: channelId,
            channelName: channelConfig.name,
            platform: platform
        });

        // 调用渠道处理器的登录方法
        const result = await handler.handleLogin(ctx, channelConfig);

        // 在返回结果中添加平台信息
        if (result && result.data) {
            result.data.platform = platform || 'unknown';
        }

        // 登录成功且有platform时，缓存到内存供checkUserToken查询
        if (result && result.code === 1 && platform) {
            const ip = getClientIp(ctx);
            const device_no = data.device_no || '';
            if (ip) {
                setPlatformCache(ip, platform);
                logger.info("thirdLogin缓存platform:", {ip, platform});
            }
        }

        // 根据不同渠道的返回格式要求处理结果
        ctx.body = result;
    }

    /**
     * WebGame：校验登录 URL 的签名，用于客户端判断是否有权限进入。
     * 支持 GET/POST：客户端只需传 gameUrl 上的 sign 即可。
     * 必填：sign
     */
    async webgameAuth(ctx) {
        const body = (ctx.request && ctx.request.body) ? ctx.request.body : {};
        const query = ctx.query || {};
        const data = {...query, ...body} as any;

        const sign = data.sign;

        if (sign == null || String(sign) === "") {
            ctx.body = ApiController.fail("缺少必要参数: sign");
            return;
        }

        const info = getWebgameAuthInfo(String(sign));
        if (!info) {
            ctx.body = ApiController.fail("签名无效或已过期");
            return;
        }

        ctx.body = ApiController.success("success", 1, false, info);
    }

    /**
     * WebGame：生成带签名的 gameUrl（对外独立接口，直接调用 WebGameChannelHandler.getGameUrl）。
     * POST JSON Body：参考 webGameApi.md
     */
    async webgameGameUrl(ctx) {
        const channelId = 25;
        const channelConfig = ChannelConfigManager.getConfig(channelId);
        if (!channelConfig) {
            ctx.body = ApiController.fail("未知渠道id");
            return;
        }

        const handler = channelFactory.getHandler(channelId);
        if (!handler || typeof (handler as any).getGameUrl !== "function") {
            ctx.body = ApiController.fail("渠道处理器不支持该接口");
            return;
        }

        const result = await (handler as any).getGameUrl(ctx, channelConfig);
        ctx.body = result;
    }

    async webgameCreateOrder(ctx) {
        const channelId = 25;
        const channelConfig = ChannelConfigManager.getConfig(channelId);
        if (!channelConfig) { ctx.body = {code: 0, message: '未知渠道id'}; return; }
        const handler = channelFactory.getHandler(channelId);
        ctx.body = await (handler as any).handleCreateOrder(ctx, channelConfig);
    }

    async webgameLoginUrl(ctx) {
        const channelId = 25;
        const channelConfig = ChannelConfigManager.getConfig(channelId);
        if (!channelConfig) { ctx.body = {code: 0, msg: '未知渠道id'}; return; }
        const handler = channelFactory.getHandler(channelId);
        ctx.body = await (handler as any).handleLoginUrl(ctx, channelConfig);
    }

    async webgameRoleList(ctx) {
        const channelId = 25;
        const channelConfig = ChannelConfigManager.getConfig(channelId);
        if (!channelConfig) { ctx.body = {code: 5, message: '服务器错误', data: []}; return; }
        const handler = channelFactory.getHandler(channelId);
        ctx.body = await (handler as any).handleRoleList(ctx, channelConfig);
    }

    async webgamePayCallback(ctx) {
        const channelId = 25;
        const channelConfig = ChannelConfigManager.getConfig(channelId);
        if (!channelConfig) { ctx.body = {code: 4, message: '充值失败，请联系客服'}; return; }
        const handler = channelFactory.getHandler(channelId);
        ctx.body = await (handler as any).handlePayCallback(ctx, channelConfig);
    }

    async webgameChatMonitor(ctx) {
        const channelId = 25;
        const channelConfig = ChannelConfigManager.getConfig(channelId);
        if (!channelConfig) { ctx.body = {code: 0, data: null, msg: '未知渠道id'}; return; }
        const handler = channelFactory.getHandler(channelId);
        ctx.body = await (handler as any).handleChatMonitor(ctx, channelConfig);
    }

    async callPay(ctx) {
        const data = ctx.request.body;
        let channelId = 1; // 默认渠道ID
        //打印回调参数
        logger.info("callPay支付回调参数:", {url: ctx.href, params: data, query: ctx.query});

        // 从请求中获取渠道ID
        if (ctx.query.channel_id) {
            channelId = parseInt(ctx.query.channel_id);
        }

        if (data.channel_id) {
            channelId = parseInt(data.channel_id);
        }

        logger.info("当前支付渠道id:", {channelId});

        // 获取渠道配置
        let channelConfig = ChannelConfigManager.getConfig(channelId);
        if (!channelConfig) {
            logger.info("未找到渠道配置:", {channelId});
            ctx.body = ApiController.fail("未知渠道id");
            return;
        }

        if (ctx.query.is_new && ctx.query.is_new == 1) {
            if (channelId == 6) {
                channelConfig = ChannelConfigManager.getConfig(999);
            }

            if (channelId == 1) {
                channelConfig = ChannelConfigManager.getConfig(9);
            }
        }

        const handler = channelFactory.getHandler(channelId);

        if (!handler) {
            logger.info("未找到渠道处理器:", {channelId});
            ctx.body = ApiController.fail("未知渠道id");
            return;
        }

        logger.info("使用渠道处理器处理支付:", {
            channelId: channelId,
            channelName: channelConfig.name,
        });

        // 调用渠道处理器的支付方法
        const result = await handler.handlePayment(ctx, channelConfig);
        // 根据不同渠道的返回格式要求处理结果
        switch (channelConfig.channelId) {
            case 6:
                ctx.body = result.code === 1 ? 1 : result.code;
                break;
            case 2:
            case 3:
            case 5:
            case 8:
                ctx.body = result.code === 1 ? "SUCCESS" : "Fail";
                break;
            case 7:
                ctx.body = result.code === 1 ? "success" : "fail";
                break;
            case 11:
                ctx.body = result.code === 1 ? "SUCCESS" : "Fail";
                break;
            case 12:
                ctx.body = result.code === 1 ? "SUCCESS" : "Fail";
                break;
            case 13:
                //{"status":0,"msg":"status 非 0 时，传入失败信息"}
                ctx.body = {status: result.code === 1 ? 0 : -1, msg: result.msg};
                break;
            case 17:
                // 华为渠道：成功返回 {"errCode": 0}，失败返回 {"errCode": 1, "errMsg": "..."}
                if (result.code === 0 || result.code === 1) {
                    // 发货成功
                    if (ctx.query.platform == "mtminiapp") {
                        ctx.body = {"code": 0, "msg": "ok"}
                    } else {
                        ctx.body = {errCode: 0};
                    }
                } else {
                    // 发货失败
                    ctx.body = {errCode: 1, errMsg: result.msg || "发放失败"};
                }
                break;
            case 14:
            case 20:
                ctx.body = result.code === 1 ? "SUCCESS" : "Fail";
                break;
            case 21:
                ctx.body = result.code === 1 ? "SUCCESS" : "Fail";
                break;
            case 22:
                ctx.body = result.code === 1 ? "SUCCESS" : "Fail";
                break;
            case 23:
                ctx.body = result.code === 1 ? "SUCCESS" : "Fail";
                break;
            case 24:
                ctx.body = result.code === 1 ? "SUCCESS" : "Fail";
                break;
            case 25:
                ctx.body = result.code === 1 ? "SUCCESS" : "Fail";
                break;
            default:
                ctx.body = result;
        }
    }

    async getServerList(ctx) {
        let tag = ctx.query.channel_id || 1;
        let data = ctx.request.body;
        let ip = getClientIp(ctx);

        // 尝试从渠道ID获取配置
        const channelConfig = ChannelConfigManager.getConfig(1);
        const signStr = data.timestamp + channelConfig.loginConfig?.signKey;
        let newSign = CryptoJS.MD5(signStr).toString();

        if (data.sign != newSign) {
            console.log("signStr:", signStr);
            logger.info(
                `签名错误: 签名串 ${signStr} newSign ${newSign} sign ${data.sign}`
            );
            ctx.body = ApiController.fail("签名错误");
            return;
        }

        let sort = data.sort == 0 ? "asc" : "desc";
        logger.info("区服接口", {ip: ip});
        const servers = await Server.getServerList(tag, 0, sort);

        let serverList = [];
        if (servers.length > 0) {
            servers.forEach(function (element) {
                let status = element.status;
                if ((status == 0 || status == 3) && element.white_list) {
                    const list = element.white_list.split(",");

                    if (list.length > 0) {
                        if (list.includes(ip)) {
                            status = 1;
                        }
                    }
                }

                serverList.push({
                    serverId: element.id,
                    serverName: element.name,
                });
            });
        }

        ctx.body = ApiController.success("成功", 1, false, serverList);
    }

    async getAllServerList(ctx) {
        let tag = ctx.query.channel_id || 1;
        let uid = ctx.query.uid || ctx.request.body?.uid; // uid是可选参数，支持GET和POST请求
        let ip = getClientIp(ctx);
        logger.info("getAllServerList 区服接口", {tag: tag, uid: uid, ip: ip});

        const servers = await Server.getAllServerList(tag, ip);

        // 如果提供了玩家ID，则查询每个服务器的角色信息；否则只返回基本服务器信息
        if (uid) {
            const {getRoleInfoByUidAndServerId} = require("../mongo/mongodb");

            // 为每个服务器添加角色信息
            const serversWithRoleInfo = await Promise.all(
                servers.map(async (server) => {
                    try {
                        const newUniqueTag = `${server.tag}|${server.sid}|${uid}`;
                        const roleInfo = await getRoleInfoByUidAndServerId(newUniqueTag, server.db_name);
                        return {
                            ...server,
                            roleName: roleInfo?.roleName || null,
                            roleLevel: roleInfo?.roleLevel || null,
                            head: roleInfo?.head || null,
                            hasRole: !!roleInfo
                        };
                    } catch (error) {
                        logger.error(`查询服务器${server.id}角色信息失败:`, error);
                        return {
                            ...server,
                            roleName: null,
                            roleLevel: null,
                            head: null,
                            hasRole: false
                        };
                    }
                })
            );

            // tag=11 时，找到第一个有角色的服，隐藏它之前的服；都没角色则只显示最新的1个服
            if (tag == 11 || tag === '11') {
                const firstRoleIndex = serversWithRoleInfo.findIndex(s => s.hasRole);
                if (firstRoleIndex > 0) {
                    ctx.body = serversWithRoleInfo.slice(firstRoleIndex);
                } else if (firstRoleIndex === -1) {
                    ctx.body = serversWithRoleInfo.slice(-1);
                } else {
                    ctx.body = serversWithRoleInfo;
                }
            } else {
                ctx.body = serversWithRoleInfo;
            }
        } else {
            ctx.body = servers;
        }
    }

    async enterServer(ctx) {
        let {uid, server_id, channel_id} = ctx.request.body;

        let url = await getServerList(server_id, channel_id || 1);
        if (!url) {
            ctx.body = ApiController.fail(`区服id错误: serverId ${server_id}`, -1);
            return;
        }

        logger.info("create params:", {params: ctx.request.body});

        if (!server_id || !uid) {
            ctx.body = ApiController.fail("参数错误!!", -1);
            return;
        }

        const create_time = formatDate(new Date());
        const serverInfo = (
            await Server.checkEnterServerByUid(uid, server_id, channel_id)
        )[0];
        let res = null;

        if (serverInfo) {
            res = await Server.updateEnterServer(serverInfo.id, create_time);
        } else {
            res = await Server.enterServer(uid, server_id, create_time, channel_id);
        }

        if (res.affectedRows > 0) {
            ctx.body = ApiController.success("请求成功", 0, true, "");
        } else {
            ctx.body = ApiController.fail("请求失败", -1, null, true);
        }
    }

    async getLastServerList(ctx) {
        let {uid, channel_id} = ctx.request.body;
        // uid是可选参数，如果不提供则只返回基本服务器信息

        let tag = channel_id || 1;
        let data = [];
        let isNewAccount = 1;
        let enterServerList = [];
        let ip = getClientIp(ctx);
        logger.info("getLastServerList 区服接口", {uid: uid, ip: ip});

        // 获取渠道配置
        const channelConfig = ChannelConfigManager.getConfig(tag);
        if (!channelConfig) {
            logger.info("未找到渠道配置:", {tag});
            ctx.body = ApiController.fail("未知渠道id");
            return;
        }

        // 如果提供了uid，查询用户进入过的服务器列表
        if (uid) {
            enterServerList = await Server.getEnterServerListByUid(uid, channel_id);
        }

        if (enterServerList.length > 0) {
            isNewAccount = 0;
            const servers = await Server.getAllServerList(tag, ip);
            const {getRoleInfoByUidAndServerId} = require("../mongo/mongodb");
            // 为每个进入过的服务器添加角色信息
            for (const element of enterServerList) {
                let roleInfo = null;
                // 只有在提供了uid时才查询角色信息
                if (uid) {
                    try {
                        const newUniqueTag = `${tag}|${element.server_id}|${uid}`;
                        roleInfo = await getRoleInfoByUidAndServerId(newUniqueTag, element.db_name);
                    } catch (error) {
                        logger.error(`查询服务器${element.server_id}角色信息失败:`, error);
                    }
                }
                let minSid = 1
                if (tag == 11) {
                    const sid = Number(element.server_id) || 1;
                    minSid = Math.floor((sid - 1) / 10) * 10 + 1;
                }


                data.push({
                    channel: channelConfig.name, //渠道固定
                    minSid: minSid, //最小服务器
                    maxSid: servers.length, //最大服务器 这里会控制 服务器列表显示的数量
                    isNewAccount: isNewAccount, //1为新号 会弹出用户协议
                    //以下是最近登陆的服务器 (不可为空 如果没有参数可以填最后一个区)
                    sid: element.server_id || 1,
                    id: element.server_id || 1,
                    name: element.name || "1区",
                    tips: element.tips || "",
                    server: element.wss
                        ? element.wss
                        : `ws://${element.ip}:${element.port}`,
                    status: element.status || 0,
                    // 角色信息（只有在提供了uid时才包含）
                    roleName: roleInfo?.roleName || null,
                    roleLevel: roleInfo?.roleLevel || null,
                    head: roleInfo?.head || null,
                    hasRole: !!roleInfo
                });

            }
            logger.info("getLastServerList 区服接口 enterServerList", {
                enterServerList: enterServerList,
            });
        } else {
            const servers = await Server.getAllServerList(tag, ip);
            let roleInfo = null;

            if (servers.length > 0) {
                const serverInfo = servers[servers.length - 1];
                logger.info("getLastServerList 区服接口 serverInfo", {
                    serverInfo: serverInfo,
                });

                // 只有在提供了uid时才查询最后一个服务器的角色信息
                if (uid) {
                    const {getRoleInfoByUidAndServerId} = require("../mongo/mongodb");
                    try {
                        const newUniqueTag = `${serverInfo.tag}|${serverInfo.sid}|${uid}`;
                        roleInfo = await getRoleInfoByUidAndServerId(newUniqueTag, serverInfo.db_name);
                    } catch (error) {
                        logger.error(`查询服务器${serverInfo.id}角色信息失败:`, error);
                    }
                }
                let minSid = 1
                if (tag == 11) {
                    minSid = servers.length //新号没有角色,minsid = 最新的服
                }

                data.push({
                    channel: channelConfig.name, //渠道固定
                    minSid: minSid, //最小服务器
                    maxSid: servers.length, //最大服务器 这里会控制 服务器列表显示的数量
                    isNewAccount: isNewAccount, //1为新号 会弹出用户协议
                    //以下是最近登陆的服务器 (不可为空 如果没有参数可以填最后一个区)
                    sid: serverInfo.sid || 1,
                    id: serverInfo.id || 1,
                    name: serverInfo.name || "1区",
                    tips: serverInfo.tips || "",
                    server: serverInfo.server
                        ? serverInfo.server
                        : `ws://${serverInfo.ip}:${serverInfo.port}/`,
                    status: serverInfo.status || 0,
                    // 角色信息
                    roleName: roleInfo?.roleName || null,
                    roleLevel: roleInfo?.roleLevel || null,
                    head: roleInfo?.head || null,
                    hasRole: !!roleInfo
                });
            } else {
                data.push({
                    channel: "未知", //渠道固定
                    minSid: 1, //最小服务器
                    maxSid: 10, //最大服务器 这里会控制 服务器列表显示的数量
                    isNewAccount: isNewAccount, //1为新号 会弹出用户协议
                    //以下是最近登陆的服务器 (不可为空 如果没有参数可以填最后一个区)
                    sid: 1,
                    id: 1,
                    name: "1区",
                    tips: "",
                    server: "",
                    status: 0,
                    // 角色信息（新用户，无角色）
                    roleName: null,
                    roleLevel: null,
                    head: null,
                    hasRole: false
                });
            }
        }
        // 根据sid去重
        const seenSids = new Set();
        data = data.filter(item => {
            if (seenSids.has(item.sid)) return false;
            seenSids.add(item.sid);
            return true;
        });
        // 所有组的minSid统一取最小值
        if (data.length > 0) {
            const globalMinSid = Math.min(...data.map(item => item.minSid));
            data.forEach(item => {
                item.minSid = globalMinSid;
            });
        }

        // tag=11 时，找到第一个有角色的服，隐藏它之前的服；都没角色则只显示最新的1个服
        if ((tag == 11 || tag === '11') && enterServerList.length > 0) {
            const firstRoleIndex = data.findIndex(s => s.hasRole);
            if (firstRoleIndex > 0) {
                data = data.slice(firstRoleIndex);
            } else if (firstRoleIndex === -1) {
                data = data.slice(-1);
            }
        }

        ctx.body = data;
        // ctx.body = ApiController.success("获取最近服务器列表成功", 1, false, data);
    }

    async getNotice(ctx) {
        let tag = ctx.query.channel_id || 1;

        let content = "";
        let content_wh = "";
        const system = await System.getSystemConfig(tag);

        if (system) {
            system.forEach((element) => {
                if (element.key == "content") {
                    content = element.value;
                }

                if (element.key == "content_wh") {
                    content_wh = element.value;
                }
            });
        }

        let notice_data = [
            {
                status: 1,
                content: content,
                content_wh: content_wh,
            },
        ];
        ctx.body = notice_data;
        // ctx.body = ApiController.success("获取公告成功", 1, false, notice_data);
    }

    // 维护服务器，踢掉所有玩家
    async maintenance(ctx) {
        let data = {
            channel_id: parseInt(ctx.query.channel_id),
            serverId: parseInt(ctx.query.serverId) || "",
        };

        logger.info("maintenance :", {channelId: data.channel_id});

        let url = await getServerList(data.serverId, data.channel_id || 1);
        if (!url) {
            ctx.body = ApiController.fail(`区服id错误: serverId ${data.serverId}`, 1);
            return;
        }
        let param = JSON.stringify({
            type: "kickAllUser",
        });
        let sendMsg = new Msg();
        sendMsg.connect(url, Account);
        new Promise((resolve) => {
            setTimeout(async () => {
                sendMsg.CG_TEST_PROTO("test", param);
            }, 1000);
        });
        ctx.body = ApiController.success("success", 0, false, null);
    }

    async maintenanceAll(ctx) {
        let channelId = parseInt(ctx.query.channel_id);
        let isMaintenance = ctx.query.isMaintenance;
        let key = ctx.query.key;
        logger.info("maintenanceAll :", {
            channelId: channelId,
            isMaintenance: isMaintenance,
        });

        if (key != "dsdadsadajkkjkxab") {
            ctx.body = ApiController.fail("无权使用", -1);
            return;
        }

        const servers = await Server.getAllServerList(channelId, "");
        logger.info("maintenanceAll servers:", {servers: servers});

        if (servers.length === 0) {
            ctx.body = ApiController.fail("不存在可操作区服", -1);
            return;
        }
        const serversId = servers.map((obj) => obj.id);

        if (isMaintenance == "true") {
            const res = await Server.updateServerStatus(serversId, 3);
        }

        servers.forEach(function (element) {
            let url = "ws://" + element.ip + ":" + element.port;
            if (!url) {
                ctx.body = ApiController.fail(`区服id错误: serverId ${element.id}`, 1);
                return;
            }

            let param = JSON.stringify({
                type: "kickAllUser",
            });
            // Msg.connect(url, Account);
            let sendMsg = new Msg();
            sendMsg.connect(url, Account);
            new Promise((resolve) => {
                setTimeout(async () => {
                    sendMsg.CG_TEST_PROTO("test", param);
                }, 1000);
            });
        });

        logger.info("maintenanceAll 更新的区服id:", {serversId: serversId});

        ctx.body = ApiController.success("success", 0, false, null);
    }

    async sendMail(ctx) {
        let data = ctx.request.body;
        let url = await getServerList(data.serverId, data.channel_id || 1);
        if (!url) {
            ctx.body = ApiController.fail(`区服id错误: serverId ${data.serverId}`, 1);
            return;
        }
        let param = JSON.stringify({
            type: "sendMail",
            mail: JSON.stringify({
                uuid: data.uuid,
                title: data.title,
                content: data.content,
                items: JSON.parse(data.items),
                expire: data.expire,
            }),
        });
        // Msg.connect(url, Account);
        let sendMsg = new Msg();
        sendMsg.connect(url, Account);
        new Promise((resolve) => {
            setTimeout(async () => {
                sendMsg.CG_TEST_PROTO("test", param);
            }, 1000);
        });
        ctx.body = ApiController.success("success", 0, false, null);
    }

    async sendAllMail(ctx) {
        let data = ctx.request.body;

        if (!data.server_list || data.server_list.length === 0) {
            ctx.body = ApiController.fail("区服不能为空", -1);
            return;
        }

        logger.info("sendAllMail 接口请求 data:", {data: data});

        const server_list = data.server_list;
        const servers = await Server.getServerList(data.channel_id || 1);
        const filteredServer = servers.filter((item) =>
            server_list.includes(item.id)
        );

        filteredServer.forEach(function (element) {
            let url = "ws://" + element.ip + ":" + element.port;
            if (!url) {
                ctx.body = ApiController.fail(
                    `区服id错误: serverId ${element.server_id}`,
                    1
                );
                return;
            }

            let param = JSON.stringify({
                type: "sendAllMail",
                mail: JSON.stringify({
                    title: data.title,
                    content: data.content,
                    items: JSON.parse(data.items),
                    expire: data.expire,
                }),
            });
            let sendMsg = new Msg();
            sendMsg.connect(url, Account);
            new Promise((resolve) => {
                setTimeout(async () => {
                    sendMsg.CG_TEST_PROTO("test", param);
                }, 1000);
            });
        });

        ctx.body = ApiController.success("success", 0);
    }

    async useCDKV2(ctx) {
        let body = ctx.request.body;

        logger.info("useCDKV2接口请求参数:", {data: body});

        let data = {
            code: body.code,
            userId: body.account,
            channelId: body.channel_id || 0,
        };

        if (body.uuid) {
            data.userId = body.uuid;
        }

        let serverUrl = await getServerAddr(body.serverId, body.channel_id);
        let err = "";
        if (!serverUrl) {
            ctx.body = ApiController.fail(`区服id错误: serverId ${body.serverId}`, 1);
            return;
        }
        let port = 8004;
        let host = "127.0.0.1";
        let url = "http://" + host + ":" + port + "/api/giftCode/exchange";

        logger.info("接口请求后台 data:", {data: data, url: url});

        try {
            const specialCodes = [
                "gfyx555",
                "gfyx666",
                "gfyx777",
                "gfyx1000",
                "gfyx2000",
                "gfyx3000",
                "gfyxrqlb",
                "GFYX555",
                "GFYX666",
                "GFYX777",
                "GFYX1000",
                "GFYX2000",
                "GFYX3000",
                "GFYXrqlb",
                "GFYX000",
                "GFYX222",
                "GFYX333",
                "GFYX888",
                "GFYX1818",
                "GQ888",
                "ZQ888",
                "gfyx8888",
                "gfyx6666",
                "gfyx7777",
                "gfyxxjbc",
                "LIDONGLB",
                "XIAOXULB",
                "GANENLB",
                "WANSHLB",
                "HERO666",
                "HERO777",
                "HERO888",
                "gfyx9999",
                "aiqi6666",
                "aiqi7777",
                "aiqi8888",
                "aiqi9999",
                "gf555",
                "gf666",
                "gf777",
                "gf100",
                "gf200",
                "gf300",
                "YDKL888",
                "SDKL888",
                "CHUXILB",
                "CHUNJIELB",
                "YUANXIAOLB",
                "VIP555",
                "VIP666",
                "VIP777",
                "VIP888",
                "QINGMINGLB",
                "yxlb666",
                "yxlb777",
                "yxlb888",
                "yxlb999",
                "WUYILB",
                "ERTONGLB",
            ];
            let param: string = "";

            const h5Codes = ["gfyx000", "gfyx222", "gfyx333", "gfyx888", "gfyx1818"];
            if (h5Codes.includes(data.code) && body.channel_id != 5) {
                ctx.body = ApiController.fail("该激活码无法在h5渠道使用", 1);
            }

            if (specialCodes.includes(data.code) || h5Codes.includes(data.code)) {
                param = JSON.stringify({
                    code: data.code,
                    type: "UseFixCDK",
                    channel_id: body.channel_id,
                });
            } else {
                const response = await axios.post(url, data, {
                    headers: {
                        "Content-Type": "application/json",
                    },
                    timeout: 10000,
                });
                logger.info("接口请求后台 接口返回 response:", {data: response.data});
                if (response.data.code != 200) {
                    ctx.body = ApiController.fail(response.data.msg, 1);
                    return;
                }
                const result = splitString(response.data.data, ";");
                // 重构itemList
                let itemList = Array();
                for (let i = 0; i < result.length; i++) {
                    let elem = splitString(result[i], ":");
                    let output0 = parseInt(elem[0], 10);
                    let output1 = parseInt(elem[1], 10);
                    itemList.push([output0, output1]);
                }
                param = JSON.stringify({
                    type: "UseCDKV2",
                    itemList: itemList,
                    channel_id: body.channel_id,
                });
            }

            logger.info("api cdk 发送服务器 param:", {param: param});
            const maxRetries = 3;
            let sent = false;
            for (let attempt = 1; attempt <= maxRetries; attempt++) {
                try {
                    const sendMsg = new Msg();
                    await sendMsg.connect(serverUrl, Account);
                    const result = sendMsg.CG_TEST_PROTO(body.account, param, parseInt(body.serverId, 10));
                    if (result) {
                        sent = true;
                        break;
                    }
                } catch (err) {
                    logger.info(`api cdk 第${attempt}次发送失败`, {error: err?.message});
                    if (attempt < maxRetries) {
                        await new Promise(r => setTimeout(r, 500 * attempt));
                    }
                }
            }
            if (!sent) {
                logger.info("api cdk 消息发送失败，已达最大重试次数", {account: body.account});
            }
        } catch (error) {
            logger.info("发送cdk错误",{error:error});
        }
        ctx.body = ApiController.success("success", 0);
    }

    async banUser(ctx) {
        let data = ctx.request.body;
        logger.info("banUser 接口请求 data:", {data: data});

        if (!data.server_tag) {
            ctx.body = ApiController.fail("区服id不能为空", -1, "");
            return;
        }

        let banInfo = {};

        switch (parseInt(data.ban_type)) {
            case 1:
                banInfo = {
                    roleBanInfo: {
                        channelTag: data.channel_id,
                        serverTag: data.server_tag,
                        roleTag: data.role_tag,
                        banTime: data.ban_time,
                    },
                    type: "setBan",
                };
                break;
            case 2:
                banInfo = {
                    accountBanInfo: {
                        channelTag: data.channel_id,
                        accountTag: data.account_tag,
                        banTime: data.ban_time,
                    },
                    type: "setBan",
                };
                break;
            case 3:
                banInfo = {
                    appBanInfo: {
                        appId: data.device_id,
                        banTime: data.ban_time,
                    },
                    type: "setBan",
                };
                break;
            default:
        }

        logger.info("banUser 发送服务器 banInfo:", banInfo);

        let url = await getServerList(data.server_tag, data.channel_id);
        if (!url) {
            ctx.body = ApiController.fail(
                `区服id错误: serverId ${data.server_tag}`,
                1
            );
            return;
        }

        let param = JSON.stringify(banInfo);

        logger.info("banUser 发送服务器 json字符串: " + param);

        let sendMsg = new Msg();
        sendMsg.connect(url, Account);
        new Promise((resolve) => {
            setTimeout(async () => {
                sendMsg.CG_TEST_PROTO("test", param, data.server_tag);
                // sendMsg.CG_SET_BAN(param);
            }, 1000);
        });
        ctx.body = ApiController.success("success", 0);
    }

    async uicFilter4399(ctx) {
        let body = ctx.request.body;

        let channelConfig = ChannelConfigManager.getConfig(6);
        if (body.is_new == 1) {
            channelConfig = ChannelConfigManager.getConfig(999);
        }
        logger.info("uicFilter4399 body:", {data: body});

        let toCheck = body.toCheck;
        let signStr = channelConfig.loginConfig?.signKey + toCheck;
        logger.info("uicFilter4399 signStr:", {signStr: signStr});

        let apiParams = {
            toCheck: toCheck,
            app: channelConfig.loginConfig.appId,
            byPinyin: true,
            isBatch: false,
            sig: CryptoJS.MD5(signStr).toString(),
        };

        logger.info("uicFilter4399 apiParams:", {data: apiParams});

        const queryStr = Object.entries(apiParams)
            .map(
                ([key, value]) =>
                    `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
            )
            .join("&");

        let url = "https://wo.webgame138.com/test/matchService.do" + "?" + queryStr;
        // let url = "https://wo.webgame138.com/test/matchService.do";
        logger.info("uicFilter4399 url:", {url: url});

        try {
            //   let response = await axios.get(url, {
            //     timeout: 10000, // 设置10秒超时
            //   });

            const response = await axios.post(url, apiParams, {
                headers: {
                    "Content-Type": "application/json",
                },
            });
            logger.info("uicFilter4399 接口返回 response:", {data: response.data});

            ctx.body = ApiController.success("success", 0, false, response.data);
        } catch (error) {
            console.log(error);
            ctx.body = ApiController.fail(error.message || "请求失败", 1);
        }
    }

    async roleList4399(ctx) {
        let body = ctx.request.body;
        logger.info("RoleList4399 请求参数:", {body: body});

        let data = {
            userId: body.userId,
            server: body.server,
            is_new: ctx.query.is_new || 0
        };

        let port = 8004;
        let host = "127.0.0.1";
        let url = "http://" + host + ":" + port + "/api/roleList/checkBy4399";

        logger.info("RoleList4399 url:", {url: url});

        try {
            const response = await axios.post(url, data, {
                headers: {
                    "Content-Type": "application/json",
                },
            });

            logger.info("RoleList4399 返回 response:", {data: response.data});

            ctx.body = response.data;
        } catch (error) {
            console.log(error);
        }
    }

    async cdkBy4399(ctx) {
        let body = ctx.request.body;

        logger.info("cdkBy4399:", {data: body});
        let server = body.server || 1;
        let channel = 5;

        let data = {
            code: body.giftId,
            userId: body.userId,
            channelId: channel,
        };

        if (body.uuid) {
            data.userId = body.uuid;
        }

        let serverUrl = await getServerList(server, channel);
        let err = "";
        if (!serverUrl) {
            ctx.body = ApiController.fail(`区服id错误: serverId ${body.serverId}`, 1);
            return;
        }
        let port = 8004;
        let host = "127.0.0.1";
        let url = "http://" + host + ":" + port + "/api/giftCode/exchange";

        logger.info("cdkBy4399接口请求后台 data:", {data: data, url: url});

        try {
            const response = await axios.post(url, data, {
                headers: {
                    "Content-Type": "application/json",
                },
                timeout: 10000,
            });
            logger.info("cdkBy4399接口请求后台 接口返回 response:", {
                data: response.data,
            });
            if (response.data.code != 200) {
                ctx.body = ApiController.fail(response.data.msg, 10004);
                return;
            }
            const result = splitString(response.data.data, ";");
            // 重构itemList
            let itemList = Array();
            for (let i = 0; i < result.length; i++) {
                let elem = splitString(result[i], ":");
                let output0 = parseInt(elem[0], 10);
                let output1 = parseInt(elem[1], 10);
                itemList.push([output0, output1]);
            }
            let param = JSON.stringify({
                type: "UseCDKV2",
                itemList: itemList,
                channel_id: channel,
            });

            let sendMsg = new Msg();
            sendMsg.connect(serverUrl, Account);
            logger.info("cdkBy4399 cdk 发送服务器 param:", {param: param});
            new Promise((resolve) => {
                setTimeout(async () => {
                    sendMsg.CG_TEST_PROTO("test", param, server);
                }, 1000);
            });
        } catch (error) {
            console.log(error);
        }
        ctx.body = ApiController.success("success", 10000);
    }

    async roleListYfy(ctx) {
        let gameCode = ctx.query.gameCode;
        let timestamp = ctx.query.timestamp;
        let uid = ctx.query.uid;
        let sign = ctx.query.sign;
        let AppSecret = "H1EqhbpA80jt0Jw6Q3T2";

        logger.info("roleListYfy 请求参数:", {
            uid: uid,
            gameCode: gameCode,
            timestamp: timestamp,
        });

        const signStr = `gameCode-${gameCode}&timestamp=${timestamp}&uid=${uid}${AppSecret}`;
        const newSign = CryptoJS.MD5(signStr).toString();
        if (newSign != sign) {
            logger.info("roleListYfy 签名错误:", {
                signStr: signStr,
                sign: sign,
                newSign: newSign,
            });
            ctx.body = ApiController.fail("签名错误!!", -1);
        }

        let port = 8004;
        let host = "127.0.0.1";
        let url = "http://" + host + ":" + port + "/api/roleList/checkByYfy";

        logger.info("roleListYfy url:", {url: url});

        try {
            const response = await axios.post(
                url,
                {
                    userId: uid,
                },
                {
                    headers: {
                        "Content-Type": "application/json",
                    },
                }
            );

            logger.info("roleListYfy 返回 response:", {data: response.data});

            ctx.body = response.data;
        } catch (error) {
            console.log(error);
        }
    }

    async getQuickSign(ctx) {
        let data = ctx.request.body;
        logger.info("getQuickSign 请求参数:", {body: data});

        if (!SignatureVerifier.verifyMD5Sign(data, ORDER_SIGN_KEY)) {
            logger.error("签名验证失败");
            ctx.body = ApiController.fail("签名错误!!", -1);
            return;
        }

        let key = "H1EqhbpA80jt0Jw6Q3T2";
        let signStr =
            "uid=" +
            data.uid +
            "&username=" +
            data.username +
            "&serverId=" +
            data.serverId +
            "&serverName=" +
            data.serverName +
            "&userRoleId=" +
            data.userRoleId +
            "&userRoleName=" +
            data.userRoleName +
            "&userRoleLevel=" +
            data.userRoleLevel +
            "&vipLevel=" +
            data.vipLevel +
            "&gameRolePower=" +
            data.gameRolePower +
            "&key=" +
            key;
        logger.info("getQuickSign signStr:", {signStr: signStr});

        data.sign = CryptoJS.MD5(signStr).toString();

        ctx.body = ApiController.success("success", 0, false, data);
    }

    /**
     * mianyou开服同步处理
     * @param ctx Koa上下文
     */
    async mianyouSyncServer(ctx) {
        try {
            const data = ctx.request.body;
            logger.info("mianyou开服同步请求参数:", {data});

            // 验证必要参数
            const {serverId, gameServerId, serverName, startTime, channelId} = data;

            if (!serverId || !gameServerId || !serverName || !startTime) {
                logger.warn("mianyou开服同步失败: 缺少必要参数", {data});
                ctx.body = ApiController.fail("缺少必要参数: serverId, gameServerId, serverName, startTime");
                return;
            }

            // 获取渠道配置
            const channelConfig = ChannelConfigManager.getConfig(channelId || 1);
            if (!channelConfig) {
                logger.error("mianyou开服同步失败: 未找到渠道配置", {channelId});
                ctx.body = ApiController.fail("未找到渠道配置");
                return;
            }

            // 创建MianyouChannelHandler实例
            const mianyouHandler = new MianyouChannelHandler();

            // 调用开服同步方法
            const result = await mianyouHandler.openServer(
                parseInt(serverId),
                gameServerId,
                serverName,
                startTime,
                channelConfig
            );

            logger.info("mianyou开服同步结果:", {result});

            // 根据返回结果设置响应
            if (result.status === 0) {
                ctx.body = ApiController.success("开服同步成功", 0, false, result);
            } else {
                ctx.body = ApiController.fail(result.msg || "开服同步失败", result.status);
            }

        } catch (error) {
            logger.error("mianyou开服同步异常:", error);
            ctx.body = ApiController.fail("服务器内部错误", -1);
        }
    }

    /**
     * WebGame 渠道封禁接口：接收 SDK 平台发送的禁言/封号/解封请求。
     * 支持 GET/POST，签名验证：md5(account_name + role_id + timestamp + login_key)
     */
    async webgamePunish(ctx) {
        const body = (ctx.request && ctx.request.body) ? ctx.request.body : {};
        const query = ctx.query || {};
        const data = {...query, ...body} as any;

        logger.info("webgamePunish 接口请求:", {data});

        const {
            server_id, account_name, role_id,
            punish_type, punish_time, timestamp, platform_sign
        } = data;

        if (!account_name || !role_id || !timestamp || !platform_sign || !server_id || punish_type === undefined) {
            ctx.body = {code: 0, data: null, msg: "缺少必要参数"};
            return;
        }

        const channelConfig = ChannelConfigManager.getConfig(25);
        if (!channelConfig) {
            ctx.body = {code: 0, data: null, msg: "渠道配置不存在"};
            return;
        }

        const loginKey = channelConfig.loginConfig?.signKey;
        if (!loginKey) {
            ctx.body = {code: 0, data: null, msg: "渠道密钥未配置"};
            return;
        }

        // 签名验证: md5(account_name + role_id + timestamp + login_key)
        const signStr = String(account_name) + String(role_id) + String(timestamp) + loginKey;
        const expectedSign = CryptoJS.MD5(signStr).toString().toLowerCase();
        if (expectedSign !== String(platform_sign).toLowerCase()) {
            logger.warn("webgamePunish 签名验证失败", {expectedSign, platform_sign});
            ctx.body = {code: 0, data: null, msg: "签名验证失败"};
            return;
        }

        // 时间戳有效期验证（5分钟）
        const now = Math.floor(Date.now() / 1000);
        const ts = parseInt(timestamp);
        if (Math.abs(now - ts) > 300) {
            logger.warn("webgamePunish 时间戳过期", {timestamp, now});
            ctx.body = {code: 0, data: null, msg: "请求已过期"};
            return;
        }

        const channelTag = data.channel_id || 25;
        const url = await getServerList(server_id, channelTag);
        if (!url) {
            logger.warn("webgamePunish 区服不存在", {server_id, channelTag});
            ctx.body = {code: 0, data: null, msg: `区服id错误: serverId ${server_id}`};
            return;
        }

        const banTime = parseInt(punish_time) || 0;
        let banInfo: any;

        switch (parseInt(punish_type)) {
            case 1: // 禁言
                banInfo = {
                    type: "setBan",
                    roleBanInfo: {
                        channelTag: channelTag,
                        serverTag: server_id,
                        roleTag: role_id,
                        banTime: banTime,
                    },
                };
                break;
            case 2: // 封号
                banInfo = {
                    type: "setBan",
                    accountBanInfo: {
                        channelTag: channelTag,
                        accountTag: account_name,
                        banTime: banTime,
                    },
                };
                break;
            case 3: // 解封
                banInfo = {
                    type: "setBan",
                    accountBanInfo: {
                        channelTag: channelTag,
                        accountTag: account_name,
                        banTime: 0,
                    },
                };
                break;
            default:
                ctx.body = {code: 0, data: null, msg: `不支持的处罚类型: ${punish_type}`};
                return;
        }

        logger.info("webgamePunish 发送服务器:", {banInfo});

        const param = JSON.stringify(banInfo);
        const sendMsg = new Msg();
        sendMsg.connect(url, Account);
        new Promise((resolve) => {
            setTimeout(async () => {
                sendMsg.CG_TEST_PROTO("test", param, server_id);
            }, 1000);
        });

        ctx.body = {code: 1, data: null, msg: "封禁操作成功"};
    }

    /**
     * 通过 userId 查询该账号在各区服的角色列表
     * 请求参数（GET/POST 均可）：uid、channel_id
     * 返回：[{ roleId, roleName, zhandouli, serverName, serverId, createTime }]
     */
    async getUserRoleList(ctx) {
        const uid = ctx.query.uid || ctx.request.body?.uid;
        const tag = ctx.query.channel_id || ctx.request.body?.channel_id || 1;

        logger.info('getUserRoleList 请求参数:', {uid, tag});

        if (!uid) {
            ctx.body = ApiController.fail('uid 不能为空', -1);
            return;
        }

        // 1. 查询该用户进入过的所有区服（join game_server 取 db_name 和区服名）
        const enterServerList: any[] = await Server.getEnterServerListByUid(uid, tag);

        logger.info('getUserRoleList enterServerList:', {
            count: enterServerList?.length,
            first: enterServerList?.[0] ? {
                server_id: enterServerList[0].server_id,
                db_name: enterServerList[0].db_name,
                name: enterServerList[0].name,
            } : null,
        });

        if (!enterServerList || enterServerList.length === 0) {
            ctx.body = ApiController.success('请求成功', 1, false, []);
            return;
        }

        // 去重（同一 server_id 可能有多条进区记录，取最新一条即可）
        const seen = new Set<string>();
        const uniqueServers = enterServerList.filter((s) => {
            if (seen.has(String(s.server_id))) return false;
            seen.add(String(s.server_id));
            return true;
        });

        // 2. 并发查询每个区服的 MongoDB 角色信息
        const roleList = await getRoleListByUserId(
            uniqueServers.map((s) => ({
                server_id: s.server_id,
                db_name: s.db_name,
                name: s.name,
            })),
            tag,
            uid
        );

        logger.info('getUserRoleList 返回角色列表:', {uid, count: roleList.length});
        ctx.body = ApiController.success('请求成功', 1, false, roleList);
    }
}

module.exports = new ApiController();
