package com.seayoo.sdk;

import android.util.Log;

import com.google.gson.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.seayoo.sdk.api.ComboSDK;
import com.seayoo.sdk.api.model.ComboSDKLoginInfo;
import com.seayoo.sdk.api.model.options.AdsOptions;
import com.seayoo.sdk.api.model.options.ComboSDKFeature;
import com.seayoo.sdk.api.model.options.ComboSDKImageShareOptions;
import com.seayoo.sdk.api.model.options.ComboSDKPurchaseOptions;
import com.seayoo.sdk.api.model.options.ComboSDKRoleInfo;
import com.seayoo.sdk.api.model.options.ComboSDKSetupOptions;
import com.seayoo.sdk.api.model.options.ComboSDKShareOptions;
import com.seayoo.sdk.api.model.options.ComboSDKShareScene;
import com.seayoo.sdk.api.model.options.ComboSDKShareTarget;
import com.seayoo.sdk.api.model.options.ComboSDKUpdateGameOptions;
import com.seayoo.sdk.api.model.options.ComboSDKVideoShareOptions;
import com.seayoo.sdk.api.model.options.ComboSDKLinkShareOptions;
import com.seayoo.sdk.api.model.options.CheckAnnouncementsOptions;
import com.seayoo.sdk.api.model.options.OpenAnnouncementsOptions;
import com.seayoo.sdk.api.model.options.ComplainOptions;
import com.seayoo.sdk.api.model.options.PromoPseudoPurchaseOptions;
import com.seayoo.sdk.api.model.options.LanguagePreference;
import com.seayoo.sdk.api.model.options.RedeemGiftCodeOptions;
import com.seayoo.sdk.combo.model.GameUrl;
import com.unity3d.player.UnityPlayer;

import org.json.JSONObject;
import org.json.JSONException;

import java.lang.reflect.Type;
import java.util.Map;

public class ComboSDKAndroidWrapper {

    public static final String TAG = "ComboSDKAndroidWrapper";

    private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();

    private static final java.util.regex.Pattern JSON_KEY_PATTERN =
        java.util.regex.Pattern.compile("\"([A-Z])([a-zA-Z]*)\"\\s*:");

    private static Boolean setupFinished = false;

    private static Boolean loginPended = false;

    public static void Setup(String jsonStr) {
        Log.d(TAG, "<Setup> Received unity message: " + jsonStr);
        ComboSDKSetupOptions opts = parseSetupOptions(jsonStr);
        ComboSDK.getInstance().setup(UnityPlayer.currentActivity, opts, result -> {
            setupFinished = true;
            if (result.isSuccess()) {
                RegisterKickOut();
                //RegisterSwitchAccount();
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<Setup> Send message to unity: " + resultStr);
                SendJson("_SetupSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<Setup> Send message to unity: " + resultStr);
                SendJson("_SetupFailed", resultStr);
            }
            if (loginPended) {
                loginPended = false;
                Log.d(TAG, "<Setup> Trigger pended login event");
                _Login();
            }
        });
    }
    
    public static void Login() {
        Log.d(TAG, "<Login> Received unity message: null");
        if (!setupFinished) {
            Log.d(TAG, "<Login> Pended, ComboSDK.Setup not finished");
            loginPended = true;
        } else {
            _Login();
        }
    }

    private static void _Login() {
        SetKeepRenderingOnPause();
        ComboSDK.getInstance().login(UnityPlayer.currentActivity, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<Login> Send message to unity: " + resultStr);
                SendJson("_LoginSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<Login> Send message to unity: " + resultStr);
                SendJson("_LoginFailed", resultStr);
            }
        });
    }

    public static String GetLoginInfo() {
        Log.d(TAG, "<GetLoginInfo> Received unity message: null");
        ComboSDKLoginInfo info = ComboSDK.getInstance().getLoginInfo();
        String resultStr = gson.toJson(info);
        Log.d(TAG, "<GetLoginInfo> Send message to unity: " + resultStr);
        return resultStr;
    }

    public static void Logout() {
        Log.d(TAG, "<Logout> Received unity message: null");
        ComboSDK.getInstance().logout(UnityPlayer.currentActivity, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<Logout> Send message to unity: " + resultStr);
                SendJson("_LogoutSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<Logout> Send message to unity: " + resultStr);
                SendJson("_LogoutFailed", resultStr);
            }
        });
    }

    public static void Purchase(String jsonStr) {
        Log.d(TAG, "<Purchase> Received unity message: " + jsonStr);
        ComboSDKPurchaseOptions opts = parsePurchaseOptions(jsonStr);
        ComboSDK.getInstance().purchase(UnityPlayer.currentActivity, opts, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<Purchase> Send message to unity: " + resultStr);
                SendJson("_PurchaseSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<Purchase> Send message to unity: " + resultStr);
                SendJson("_PurchaseFailed", resultStr);
            }
        });
    }

    public static void PreloadAd(String jsonStr) {
        Log.d(TAG, "<PreloadAd> Received unity message: " + jsonStr);
        AdsOptions opts = parseAdsOptions(jsonStr);
        ComboSDK.getInstance().preloadAd(UnityPlayer.currentActivity, opts, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<PreloadAd> Send message to unity: " + resultStr);
                SendJson("_PreloadAdSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<PreloadAd> Send message to unity: " + resultStr);
                SendJson("_PreloadAdFailed", resultStr);
            }
        });
    }

    public static void ShowAd(String jsonStr) {
        Log.d(TAG, "<ShowAd> Received unity message: " + jsonStr);
        AdsOptions opts = parseAdsOptions(jsonStr);
        ComboSDK.getInstance().showAd(UnityPlayer.currentActivity, opts, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<ShowAd> Send message to unity: " + resultStr);
                SendJson("_ShowAdSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<ShowAd> Send message to unity: " + resultStr);
                SendJson("_ShowAdFailed", resultStr);
            }
        });
    }
    
    public static void Share(String type, String jsonStr) {
        Log.d(TAG, "<Share> Received unity message: " + jsonStr);
        String[] legacyKeys = {"Contents"};
        jsonStr = getAdaptedJsonStr(jsonStr, "Text", legacyKeys);
        Log.d(TAG, "<Share> Share adapted json message: " + jsonStr);
        ComboSDKShareOptions opts = null;
        Gson gson = new GsonBuilder()
            .registerTypeAdapter(ComboSDKShareScene.class, new ComboSDKShareSceneDeserializer())
            .registerTypeAdapter(ComboSDKShareTarget.class, new ComboSDKShareTargetDeserializer())
            .create();
        switch (type) {
            case "ImageShareOptions":
                opts = gson.fromJson(convertFirstCharToLowerCase(jsonStr), ComboSDKImageShareOptions.class);
                break;
            case "VideoShareOptions":
                opts = gson.fromJson(convertFirstCharToLowerCase(jsonStr), ComboSDKVideoShareOptions.class);
                break;
            case "LinkShareOptions":
                opts = gson.fromJson(convertFirstCharToLowerCase(jsonStr), ComboSDKLinkShareOptions.class);
                break;
            default:
                opts = gson.fromJson(convertFirstCharToLowerCase(jsonStr), ComboSDKImageShareOptions.class);
                Log.e(TAG, "<Share> Unknown share type: " + type + ", fallback to ComboSDKImageShareOptions");
                break;
        }
        ComboSDK.getInstance().share(UnityPlayer.currentActivity, opts, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<Share> Send message to unity: " + resultStr);
                SendJson("_ShareSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<Share> Send message to unity: " + resultStr);
                SendJson("_ShareFailed", resultStr);
            }
        });
    }
    
    public static String GetAvailableShareTargets() {
        Log.d(TAG, "<GetAvailableShareTargets> Received unity message: null");
        String result = gson.toJson(ComboSDK.getInstance().getAvailableShareTargets());
        return result;
    }
    
    public static boolean IsFeatureAvailable(int value) {
        Log.d(TAG, "<IsFeatureAvailable> Received unity message: " + value);
        ComboSDKFeature feature;
        switch (value) {
            case 0:
                feature = ComboSDKFeature.UPDATE_GAME;
                break;
            case 1:
                feature = ComboSDKFeature.QUIT;
                break;
            case 2:
                feature = ComboSDKFeature.PRODUCT_QUANTITY;
                break;
            case 3:
                feature = ComboSDKFeature.SEAYOO_ACCOUNT;
                break;
            case 4:
                feature = ComboSDKFeature.CONTACT_SUPPORT;
                break;
            case 5:
                feature = ComboSDKFeature.REPORT_DOUYIN_CUSTOM_EVENT;
                break;
            default:
                return false;
        }
        return ComboSDK.getInstance().isFeatureAvailable(feature);
    }

    public static void UpdateGame(String jsonStr) {
        Log.d(TAG, "<UpdateGame> Received unity message: " + jsonStr);
        ComboSDKUpdateGameOptions opts = parseUpdateGameOptions(jsonStr);
        ComboSDK.getInstance().updateGame(UnityPlayer.currentActivity, opts, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<UpdateGame> Send message to unity: " + resultStr);
                SendJson("_UpdateGameSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<UpdateGame> Send message to unity: " + resultStr);
                SendJson("_UpdateGameFailed", resultStr);
            }
        });
    }
    
    public static void GetDownloadUrl() {
        Log.d(TAG, "<GetDownloadUrl> Received unity message: null");
        ComboSDK.getInstance().getDownloadUrl(result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<GetDownloadUrl> Send message to unity: " + resultStr);
                SendJson("_GetDownloadUrlSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<GetDownloadUrl> Send message to unity: " + resultStr);
                SendJson("_GetDownloadUrlFailed", resultStr);
            }
        });
    }
    
    public static void Quit() {
        Log.d(TAG, "<Quit> Received unity message: null");
        ComboSDK.getInstance().quit(UnityPlayer.currentActivity, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<Quit> Send message to unity: " + resultStr);
                SendJson("_QuitSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<Quit> Send message to unity: " + resultStr);
                SendJson("_QuitFailed", resultStr);
            }
        });
    }
    
    public static void ReportCreateRole(String jsonStr) {
        Log.d(TAG, "<ReportCreateRole> Received unity message: " + jsonStr);
        ComboSDKRoleInfo opts = parseRoleInfo(jsonStr);
        ComboSDK.getInstance().reportCreateRole(opts);
    }

    public static void ReportEnterGame(String jsonStr) {
        Log.d(TAG, "<ReportEnterGame> Received unity message: " + jsonStr);
        ComboSDKRoleInfo opts = parseRoleInfo(jsonStr);
        ComboSDK.getInstance().reportEnterGame(opts);
    }

    public static String GetVersion() {
        Log.d(TAG, "<GetVersion> Received unity message: null");
        return ComboSDK.getInstance().getVersion();
    }

    public static String GetDistro() {
        Log.d(TAG, "<GetDistro> Received unity message: null");
        return ComboSDK.getInstance().getDistro();
    }

    public static String GetDeviceId() {
        Log.d(TAG, "<GetDeviceId> Received unity message: null");
        return ComboSDK.getInstance().getDeviceId();
    }

    public static String GetVariant() {
        Log.d(TAG, "<GetVariant> Received unity message: null");
        return ComboSDK.getInstance().getVariant();
    }

    public static String GetSubvariant() {
        Log.d(TAG, "<GetSubvariant> Received unity message: null");
        return ComboSDK.getInstance().getSubvariant();
    }
    
    public static String GetParameters() {
        Log.d(TAG, "<GetParameters> Received unity message: null");
        return gson.toJson(ComboSDK.getInstance().getParameters());
    }

    public static void OpenAppSettings() {
        Log.d(TAG, "<OpenAppSettings> Received unity message: null");
        ComboSDK.getInstance().openAppSettings(UnityPlayer.currentActivity);
    }

    public static String GetUserId() {
        Log.d(TAG, "<GetUserId> Received unity message: null");
        return ComboSDK.SeayooAccount.getUserId();
    }

    public static void ManageAccount() {
        Log.d(TAG, "<ManageAccount> Received unity message: null");
        ComboSDK.SeayooAccount.manageAccount(UnityPlayer.currentActivity);
    }

    public static void ChangePassword() {
        Log.d(TAG, "<ChangePassword> Received unity message: null");
        ComboSDK.SeayooAccount.changePassword(UnityPlayer.currentActivity);
    }

    public static void DeleteAccount() {
        Log.d(TAG, "<DeleteAccount> Received unity message: null");
        ComboSDK.SeayooAccount.deleteAccount(UnityPlayer.currentActivity);
    }

    public static void ContactSupport() {
        SetKeepRenderingOnPause();
        Log.d(TAG, "<ContactSupport> Received unity message: null");
        ComboSDK.getInstance().contactSupport(UnityPlayer.currentActivity);
    }

    public static void OpenGameUrl(int value) {
        SetKeepRenderingOnPause();
        Log.d(TAG, "<OpenGameUrl> Received unity message: " + value);
        GameUrl url;
        switch (value) {
            case 0:
                url = GameUrl.USER_AGREEMENT;
                break;
            case 1:
                url = GameUrl.PRIVACY_POLICY;
                break;
            case 2:
                url = GameUrl.PRIVACY_CHILDREN;
                break;
            case 3:
                url = GameUrl.THIRD_PARTY;
                break;
            case 4:
                url = GameUrl.FANGCHENMI;
                break;
            default:
                return;
        }
        ComboSDK.getInstance().openGameUrl(UnityPlayer.currentActivity, url);
    }

    public static void OpenShortLink(String shortLink, String gameData) {
        SetKeepRenderingOnPause();
        Log.d(TAG, "<OpenShortLink> Received unity message: null");

        Gson gson = new Gson();
        Type type = new TypeToken<Map<String, String>>(){}.getType();
        Map<String, String> map = gson.fromJson(gameData, type);

        ComboSDK.getInstance().openShortLink(UnityPlayer.currentActivity, shortLink, map, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<OpenShortLink> Send message to unity: " + resultStr);
                SendJson("_OpenShortLinkSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<OpenShortLink> Send message to unity: " + resultStr);
                SendJson("_OpenShortLinkFailed", resultStr);
            }
        });
    }

    public static void CheckAnnouncements(String jsonStr)
    {
        Log.d(TAG, "<CheckAnnouncements> Received unity message: " + jsonStr);
        CheckAnnouncementsOptions opts = parseCheckAnnouncementsOptions(jsonStr);
        ComboSDK.getInstance().checkAnnouncements(UnityPlayer.currentActivity, opts, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<CheckAnnouncements> Send message to unity: " + resultStr);
                SendJson("_CheckAnnouncementsSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<CheckAnnouncements> Send message to unity: " + resultStr);
                SendJson("_CheckAnnouncementsFailed", resultStr);
            }
        });
    }

    public static void OpenAnnouncements(String jsonStr)
    {
        SetKeepRenderingOnPause();
        Log.d(TAG, "<OpenAnnouncements> Received unity message: " + jsonStr);
        OpenAnnouncementsOptions opts = parseOpenAnnouncementsOptions(jsonStr);
        ComboSDK.getInstance().openAnnouncements(UnityPlayer.currentActivity, opts, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<OpenAnnouncements> Send message to unity: " + resultStr);
                SendJson("_OpenAnnouncementsSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<OpenAnnouncements> Send message to unity: " + resultStr);
                SendJson("_OpenAnnouncementsFailed", resultStr);
            }
        });
    }

    public static void Complain(String jsonStr)
    {
        SetKeepRenderingOnPause();
        Log.d(TAG, "<Complain> Received unity message: " + jsonStr);
        ComplainOptions opts = parseComplainOptions(jsonStr);
        ComboSDK.getInstance().complain(UnityPlayer.currentActivity, opts, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<Complain> Send message to unity: " + resultStr);
                SendJson("_ComplainSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<Complain> Send message to unity: " + resultStr);
                SendJson("_ComplainFailed", resultStr);
            }
        });
    }

    public static void PromoPseudoPurchase(String jsonStr)
    {
        Log.d(TAG, "<PromoPseudoPurchase> Received unity message: " + jsonStr);
        PromoPseudoPurchaseOptions opts = parsePromoPseudoPurchaseOptions(jsonStr);
        ComboSDK.getInstance().promoPseudoPurchase(UnityPlayer.currentActivity, opts);
    }

    public static boolean ResetGuest()
    {
        Log.d(TAG, "<ResetGuest> Received unity message: null");
        return ComboSDK.getInstance().resetGuest(UnityPlayer.currentActivity);
    }

    public static String GetLanguagePreference()
    {
        Log.d(TAG, "<GetLanguagePreference> Received unity message: null");
        String result = gson.toJson(ComboSDK.getInstance().getLanguagePreference(UnityPlayer.currentActivity));
        return result;
    }

    public static void SetLanguagePreference(int value)
    {
        Log.d(TAG, "<SetLanguagePreference> Received unity message: " + value);
        LanguagePreference language;
        switch (value) {
            case 0:
                language = LanguagePreference.FollowSystem;
                break;
            case 1:
                language = LanguagePreference.ChineseSimplified;
                break;
            case 2:
                language = LanguagePreference.English;
                break;
            default:
                return;
        }
        ComboSDK.getInstance().setLanguagePreference(UnityPlayer.currentActivity, language);
    }

    public static String GetLanguageCode()
    {
        Log.d(TAG, "<GetLanguageCode> Received unity message: null");
        String result = gson.toJson(ComboSDK.getInstance().getLanguageCode(UnityPlayer.currentActivity));
        return result;
    }

    public static void RedeemGiftCode(String jsonStr)
    {
        SetKeepRenderingOnPause();
        Log.d(TAG, "<RedeemGiftCode> Received unity message: " + jsonStr);
        RedeemGiftCodeOptions opts = parseRedeemGiftCodeOptions(jsonStr);
        ComboSDK.getInstance().redeemGiftCode(UnityPlayer.currentActivity, opts, result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.d(TAG, "<RedeemGiftCode> Send message to unity: " + resultStr);
                SendJson("_RedeemGiftCodeSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.d(TAG, "<RedeemGiftCode> Send message to unity: " + resultStr);
                SendJson("_RedeemGiftCodeFailed", resultStr);
            }
        });
    }

    public static void ReportDouyinCustomEvent(String eventName, String eventParams)
    {
        Log.d(TAG, "<ReportDouyinCustomEvent> Received unity message: " + "eventName: " + eventName + "eventParams: " + eventParams);

        Gson gson = new Gson();
        Type type = new TypeToken<Map<String, String>>(){}.getType();
        Map<String, String> map = gson.fromJson(eventParams, type);

        ComboSDK.getInstance().reportDouyinCustomEvent(eventName, map);
    }
    
    private static void RegisterKickOut() {
        ComboSDK.getInstance().registerKickOut(result -> {
            if (result.isSuccess()) {
                String resultStr = gson.toJson(result.get());
                Log.i(TAG, "<OnKickOut> Send message to unity: " + resultStr);
                SendJson("_KickOutSucceed", resultStr);
            } else {
                String resultStr = gson.toJson(result.error());
                Log.i(TAG, "<OnKickOut> Send message to unity: " + resultStr);
                SendJson("_KickOutFailed", resultStr);
            }
        });
    }

    private static void SendJson(String method, String jsonStr) {
        UnityPlayer.UnitySendMessage("ComboSDKAndroidBridge", method, jsonStr);
    }
    static class ComboSDKShareSceneDeserializer implements JsonDeserializer<ComboSDKShareScene> {
        @Override
        public ComboSDKShareScene deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            int typeInt = json.getAsInt();
            switch (typeInt) {
                case 1:
                    return ComboSDKShareScene.WEIXIN_TIMELINE;
                case 2:
                    return ComboSDKShareScene.WEIXIN_FAVORITE;
                case 3:
                    return ComboSDKShareScene.DOUYIN_EDIT;
                case 4:
                    return ComboSDKShareScene.DOUYIN_PUBLISH;
                case 5:
                    return ComboSDKShareScene.DOUYIN_CONTACTS;
                case 6:
                    return ComboSDKShareScene.QQ;
                case 7:
                    return ComboSDKShareScene.QQ_QZONE;
                default:
                    return ComboSDKShareScene.WEIXIN_SESSION;
            }
        }
    }

    static class ComboSDKShareTargetDeserializer implements JsonDeserializer<ComboSDKShareTarget> {
        @Override
        public ComboSDKShareTarget deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            int typeInt = json.getAsInt();
            switch (typeInt) {
                case 1:
                    return ComboSDKShareTarget.TAPTAP;
                case 2:
                    return ComboSDKShareTarget.AGORA;
                case 3:
                    return ComboSDKShareTarget.WEIXIN;
                case 4:
                    return ComboSDKShareTarget.WEIBO;
                case 5:
                    return ComboSDKShareTarget.DOUYIN;
                case 6:
                    return ComboSDKShareTarget.QQ;
                default:
                    return ComboSDKShareTarget.SYSTEM;
            }
        }
    }
    public static String convertFirstCharToLowerCase(String jsonString) {
        // 使用正则表达式只转换 JSON key 的首字母
        // 匹配 "Key" : 或 "Key": 格式，将 Key 的首字母转为小写
        java.util.regex.Matcher matcher = JSON_KEY_PATTERN.matcher(jsonString);
        StringBuffer result = new StringBuffer();
        while (matcher.find()) {
            String key = matcher.group(1) + matcher.group(2);
            String replacement = "\"" + Character.toLowerCase(key.charAt(0)) + key.substring(1) + "\" :";
            matcher.appendReplacement(result, replacement);
        }
        matcher.appendTail(result);
        return result.toString();
    }

    /**
     *  JSON 字段兼容
     */
    public static String getAdaptedJsonStr(String jsonStr, String newKey, String[] legacyKeys) {
        try {
            Gson gson = new Gson();
            JsonObject jsonObject = JsonParser.parseString(jsonStr).getAsJsonObject();
            
            for (String legacyKey : legacyKeys) {
                if (jsonObject.has(legacyKey) && !jsonObject.get(legacyKey).isJsonNull() 
                    && !jsonObject.get(legacyKey).getAsString().isEmpty()) {
                    jsonObject.add(newKey, jsonObject.get(legacyKey));
                    jsonObject.remove(legacyKey);
                    break;
                }
            }
            
            return gson.toJson(jsonObject);
        } catch (JsonSyntaxException | IllegalStateException e) {
            // Log the error if needed
            Log.e(TAG, "Failed to adapt JSON string: " + e.getMessage());
            return jsonStr;
        }
    }

    private static boolean isKeepRenderingOnPause = false;

    /**
     *  UnityPlayer onPause中继续渲染
     */
    public static void KeepRenderingOnPause() {
        Log.i(TAG, "KeepRenderingOnPause");
        isKeepRenderingOnPause = true;
    }

    /**
     * 设置是否在下一次onPause中继续渲染
     */
    private static void SetKeepRenderingOnPause(){
        try {
            Log.i(TAG, "SetKeepRenderingOnPause: " + isKeepRenderingOnPause);
            ComboSDKMainActivity unityActivity = (ComboSDKMainActivity)UnityPlayer.currentActivity;
            unityActivity.isKeepRenderingOnPause = isKeepRenderingOnPause;
        } catch (NullPointerException | ClassCastException e) {
            Log.e(TAG, "SetKeepRenderingOnPause: " + e);
        }
    }

    // ==================== JSONObject 解析方法 ====================

    /**
     * 安全获取 String 值，处理 null 值返回 ""
     */
    private static String getOptString(JSONObject json, String key) {
        if (json.isNull(key)) {
            return "";
        }
        return json.optString(key, "");
    }

    /**
     * 安全获取 int 值，处理 null 值返回 0
     */
    private static int getOptInt(JSONObject json, String key) {
        if (json.isNull(key)) {
            return 0;
        }
        return json.optInt(key, 0);
    }

    /**
     * 安全获取 long 值，处理 null 值返回 0L
     */
    private static long getOptLong(JSONObject json, String key) {
        if (json.isNull(key)) {
            return 0L;
        }
        return json.optLong(key, 0L);
    }

    /**
     * 解析 ComboSDKSetupOptions
     */
    private static ComboSDKSetupOptions parseSetupOptions(String jsonStr) {
        try {
            JSONObject json = new JSONObject(convertFirstCharToLowerCase(jsonStr));
            String gameId = getOptString(json, "gameId");
            String publishableKey = getOptString(json, "publishableKey");
            String endpoint = getOptString(json, "endpoint");

            Map<String, Object> extra = null;
            if (json.has("extra") && !json.isNull("extra")) {
                JSONObject extraJson = json.getJSONObject("extra");
                extra = new java.util.HashMap<>();
                java.util.Iterator<String> keys = extraJson.keys();
                while (keys.hasNext()) {
                    String key = keys.next();
                    Object value = extraJson.get(key);
                    extra.put(key, value);
                }
            }

            return new ComboSDKSetupOptions(gameId, publishableKey, endpoint, extra);
        } catch (JSONException e) {
            Log.e(TAG, "parseSetupOptions error: " + e.getMessage());
            return new ComboSDKSetupOptions("", "", "", null);
        }
    }

    /**
     * 解析 ComboSDKPurchaseOptions
     */
    private static ComboSDKPurchaseOptions parsePurchaseOptions(String jsonStr) {
        try {
            JSONObject json = new JSONObject(convertFirstCharToLowerCase(jsonStr));
            String orderToken = getOptString(json, "orderToken");
            String productDescription = getOptString(json, "productDescription");
            return new ComboSDKPurchaseOptions(orderToken, productDescription);
        } catch (JSONException e) {
            Log.e(TAG, "parsePurchaseOptions error: " + e.getMessage());
            return new ComboSDKPurchaseOptions("", "");
        }
    }

    /**
     * 解析 AdsOptions
     */
    private static AdsOptions parseAdsOptions(String jsonStr) {
        try {
            JSONObject json = new JSONObject(convertFirstCharToLowerCase(jsonStr));
            String placementId = getOptString(json, "placementId");
            // scenarioId 为可空参数
            String scenarioId = json.isNull("scenarioId") ? null : json.optString("scenarioId");
            return new AdsOptions(placementId, scenarioId);
        } catch (JSONException e) {
            Log.e(TAG, "parseAdsOptions error: " + e.getMessage());
            return new AdsOptions("", null);
        }
    }

    /**
     * 解析 ComboSDKUpdateGameOptions
     */
    private static ComboSDKUpdateGameOptions parseUpdateGameOptions(String jsonStr) {
        return new ComboSDKUpdateGameOptions();
    }

    /**
     * 解析 ComboSDKRoleInfo
     */
    private static ComboSDKRoleInfo parseRoleInfo(String jsonStr) {
        try {
            JSONObject json = new JSONObject(convertFirstCharToLowerCase(jsonStr));
            String roleId = getOptString(json, "roleId");
            String roleName = getOptString(json, "roleName");
            int roleLevel = getOptInt(json, "roleLevel");
            long roleCreateTime = getOptLong(json, "roleCreateTime");
            String serverId = getOptString(json, "serverId");
            String serverName = getOptString(json, "serverName");
            return new ComboSDKRoleInfo(roleId, roleName, roleLevel, roleCreateTime, serverId, serverName);
        } catch (JSONException e) {
            Log.e(TAG, "parseRoleInfo error: " + e.getMessage());
            return new ComboSDKRoleInfo("", "", 0, 0L, "", "");
        }
    }

    /**
     * 解析 CheckAnnouncementsOptions
     */
    private static CheckAnnouncementsOptions parseCheckAnnouncementsOptions(String jsonStr) {
        try {
            JSONObject json = new JSONObject(convertFirstCharToLowerCase(jsonStr));
            String profile = getOptString(json, "profile");
            int level = getOptInt(json, "level");
            return new CheckAnnouncementsOptions(profile, level);
        } catch (JSONException e) {
            Log.e(TAG, "parseCheckAnnouncementsOptions error: " + e.getMessage());
            return new CheckAnnouncementsOptions("", 0);
        }
    }

    /**
     * 解析 OpenAnnouncementsOptions
     */
    private static OpenAnnouncementsOptions parseOpenAnnouncementsOptions(String jsonStr) {
        try {
            JSONObject json = new JSONObject(convertFirstCharToLowerCase(jsonStr));
            String profile = getOptString(json, "profile");
            int level = getOptInt(json, "level");
            int width = getOptInt(json, "width");
            int height = getOptInt(json, "height");
            return new OpenAnnouncementsOptions(profile, level, width, height);
        } catch (JSONException e) {
            Log.e(TAG, "parseOpenAnnouncementsOptions error: " + e.getMessage());
            return new OpenAnnouncementsOptions("", 0, 0, 0);
        }
    }

    /**
     * 解析 ComplainOptions
     */
    private static ComplainOptions parseComplainOptions(String jsonStr) {
        try {
            JSONObject json = new JSONObject(convertFirstCharToLowerCase(jsonStr));
            String targetType = getOptString(json, "targetType");
            String targetId = getOptString(json, "targetId");
            String targetName = getOptString(json, "targetName");
            // category serverId roleId roleName context 为可空参数
            String category = json.isNull("category") ? null : json.optString("category");
            String serverId = json.isNull("serverId") ? null : json.optString("serverId");
            String roleId = json.isNull("roleId") ? null : json.optString("roleId");
            String roleName = json.isNull("roleName") ? null : json.optString("roleName");
            String context = json.isNull("context") ? null : json.optString("context");
            int width = getOptInt(json, "width");
            int height = getOptInt(json, "height");
            return new ComplainOptions(targetType, targetId, targetName, category, serverId, roleId, roleName, context, width, height);
        } catch (JSONException e) {
            Log.e(TAG, "parseComplainOptions error: " + e.getMessage());
            return new ComplainOptions("", "", "", null, null, null, null, null, 0, 0);
        }
    }

    /**
     * 解析 PromoPseudoPurchaseOptions
     */
    private static PromoPseudoPurchaseOptions parsePromoPseudoPurchaseOptions(String jsonStr) {
        try {
            JSONObject json = new JSONObject(convertFirstCharToLowerCase(jsonStr));
            int amount = getOptInt(json, "amount");
            return new PromoPseudoPurchaseOptions(amount);
        } catch (JSONException e) {
            Log.e(TAG, "parsePromoPseudoPurchaseOptions error: " + e.getMessage());
            return new PromoPseudoPurchaseOptions(0);
        }
    }

    /**
     * 解析 RedeemGiftCodeOptions
     */
    private static RedeemGiftCodeOptions parseRedeemGiftCodeOptions(String jsonStr) {
        try {
            JSONObject json = new JSONObject(convertFirstCharToLowerCase(jsonStr));
            int serverId = getOptInt(json, "serverId");
            String roleId = getOptString(json, "roleId");
            // giftCode roleName 为可空参数
            String giftCode = json.isNull("giftCode") ? null : json.optString("giftCode");
            String roleName = json.isNull("roleName") ? null : json.optString("roleName");
            // width 和 height 有特殊默认值
            int width = json.isNull("width") ? 70 : json.optInt("width", 70);
            int height = json.isNull("height") ? 90 : json.optInt("height", 90);
            return new RedeemGiftCodeOptions(serverId, roleId, giftCode, roleName, width, height);
        } catch (JSONException e) {
            Log.e(TAG, "parseRedeemGiftCodeOptions error: " + e.getMessage());
            return new RedeemGiftCodeOptions(0, "", null, null, 70, 90);
        }
    }
}
