package com.atmob.gravity_engine;

import android.content.Context;
import android.text.TextUtils;

import androidx.annotation.NonNull;

import org.json.JSONObject;

import android.util.Log;

import java.util.Map;
import java.util.Objects;

import cn.gravity.android.GEConfig;
import cn.gravity.android.GravityEngineSDK;
import cn.gravity.android.InitializeCallback;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

/**
 * GravityEnginePlugin
 */
public class GravityEnginePlugin implements FlutterPlugin, MethodCallHandler {

    /* Method names */
    private static final String METHOD_INITIALIZE = "initialize";
    private static final String METHOD_TRACK = "track";
    private static final String METHOD_TRACK_PAY = "trackPay";
    private static final String METHOD_LOGIN = "login";
    private static final String METHOD_LOGOUT = "logout";

    /* Arguments */
    // Initialize
    private static final String ARG_ACCESS_TOKEN = "accessToken";
    private static final String ARG_DEBUG = "debug";
    private static final String ARG_CLIENT_ID = "clientId";
    private static final String ARG_CHANNEL = "channel";

    // Track event
    private static final String ARG_EVENT_ID = "eventId";
    private static final String ARG_EVENT_PRAMS = "eventParams";

    // Track pay
    private static final String ARG_ORDER_NO = "orderNo";
    private static final String ARG_AMOUNT = "amount";
    private static final String ARG_CURRENCY = "currency";
    private static final String ARG_PAY_TYPE = "payType";
    private static final String ARG_ITEM_NAME = "itemName";

    private MethodChannel methodChannel;
    private Context applicationContext;

    private volatile GravityEngineSDK gravityEngineSDK;
    private volatile boolean isInitialized = false;

    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
        applicationContext = flutterPluginBinding.getApplicationContext();
        methodChannel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "gravity_engine");
        methodChannel.setMethodCallHandler(this);
    }

    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
        switch (call.method) {
            case METHOD_INITIALIZE:
                initialize(call, result);
                break;
            case METHOD_TRACK:
                track(call, result);
                break;
            case METHOD_TRACK_PAY:
                trackPay(call, result);
                break;
            case METHOD_LOGIN:
                login(call, result);
                break;
            case METHOD_LOGOUT:
                logout(call, result);
                break;
            default:
                result.notImplemented();
        }
    }

    private void initialize(MethodCall call, Result result) {
        try {
            String accessToken = call.argument(ARG_ACCESS_TOKEN);
            boolean debug = Boolean.TRUE.equals(call.argument(ARG_DEBUG));
            String clientId = call.argument(ARG_CLIENT_ID);
            String channel = call.argument(ARG_CHANNEL);

            if (accessToken == null || clientId == null || channel == null) {
                result.error("-1", "GravityEngine init failed, msg: missing required arguments", null);
                return;
            }
            GravityEngineSDK gravityEngineSdk = getGravityEngineSdk(accessToken, debug);
            if (gravityEngineSdk == null) {
                result.error("-1", "GravityEngine init failed, msg: getGravityEngineSdk failed", null);
            } else {
                gravityEngineSdk.initialize(accessToken, clientId, clientId, channel, new InitializeCallback() {

                    boolean isCallback = false;

                    @Override
                    public void onFailed(String s, JSONObject jsonObject) {
                        Log.e("GravityEngineSDK", "onFailed: " + s);
                        if (isCallback) {
                            return;
                        }
                        isCallback = true;
                        result.error(s, "GravityEngine init failed, msg: " + jsonObject, null);
                    }

                    @Override
                    public void onSuccess(JSONObject responseJson, JSONObject registerBody) {
                        Log.e("GravityEngineSDK", "onSuccess: ");
                        isInitialized = true;
                        if (isCallback) {
                            return;
                        }
                        isCallback = true;
                        result.success(isFromPromote(responseJson));
                    }

                    private Boolean isFromPromote(JSONObject responseJson) {
                        if (responseJson == null) {
                            return false;
                        }
                        String clickCompany = responseJson.optString("clickCompany");
                        return !TextUtils.isEmpty(clickCompany) && !Objects.equals("natural", clickCompany);
                    }
                }, true);
            }
        } catch (Exception e) {
            result.error("-1", "GravityEngine init failed, msg: " + e.getMessage(), null);
        }
    }

    private GravityEngineSDK getGravityEngineSdk(String accessToken, boolean debug) {
        if (gravityEngineSDK == null) {
            synchronized (this) {
                if (gravityEngineSDK == null) {
                    GEConfig config = GEConfig.getInstance(applicationContext, accessToken);
                    config.setMode(debug ? GEConfig.ModeEnum.DEBUG : GEConfig.ModeEnum.NORMAL);
                    try {
                        gravityEngineSDK = GravityEngineSDK.setupAndStart(config);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return gravityEngineSDK;
    }

    private void track(MethodCall call, Result result) {
        String eventId = call.argument(ARG_EVENT_ID);
        Map<String, Object> eventParams = call.argument(ARG_EVENT_PRAMS);
        if (eventId == null) {
            result.error("-1", "GravityEngine track failed, msg: missing required arguments", null);
            return;
        }
        if (!isInitialized || gravityEngineSDK == null) {
            result.error("-1", "GravityEngine track failed, msg: GravityEngineSDK is not initialized", null);
            return;
        }
        JSONObject params = null;
        if (eventParams != null) {
            try {
                params = new JSONObject(eventParams);
            } catch (Exception e) {
                result.error("-1", "GravityEngine track failed, msg: parse eventParams failed", null);
                return;
            }
        }
        gravityEngineSDK.track(eventId, params);
        result.success(null);
    }

    private void trackPay(MethodCall call, Result result) {
        Integer amount = call.argument(ARG_AMOUNT);
        String currency = call.argument(ARG_CURRENCY);
        String orderNo = call.argument(ARG_ORDER_NO);
        String itemName = call.argument(ARG_ITEM_NAME);
        String payType = call.argument(ARG_PAY_TYPE);
        if (amount == null || currency == null || orderNo == null || itemName == null || payType == null) {
            result.error("-1", "GravityEngine trackPay failed, msg: missing required arguments", null);
            return;
        }
        if (!isInitialized || gravityEngineSDK == null) {
            result.error("-1", "GravityEngine trackPay failed, msg: GravityEngineSDK is not initialized", null);
            return;
        }
        try {
            gravityEngineSDK.trackPayEvent(amount, currency, orderNo, itemName, payType);
            result.success(null);
        } catch (Exception e) {
            result.error("-1", "GravityEngine trackPay failed, msg: " + e.getMessage(), null);
        }
    }

    private void login(MethodCall call, Result result) {
        if (!isInitialized || gravityEngineSDK == null) {
            result.error("-1", "GravityEngine login failed, msg: GravityEngineSDK is not initialized", null);
            return;
        }
        String clientId = call.argument(ARG_CLIENT_ID);
        if (clientId == null) {
            result.error("-1", "GravityEngine login failed, msg: missing required arguments", null);
            return;
        }
        try {
            gravityEngineSDK.login(clientId);
            result.success(null);
        } catch (Exception e) {
            result.error("-1", "GravityEngine login failed, msg: " + e.getMessage(), null);
        }
    }

    private void logout(MethodCall call, Result result) {
        if (!isInitialized || gravityEngineSDK == null) {
            result.error("-1", "GravityEngine logout failed, msg: GravityEngineSDK is not initialized", null);
            return;
        }
        try {
            gravityEngineSDK.logout(() -> result.success(null));
        } catch (Exception e) {
            result.error("-1", "GravityEngine logout failed, msg: " + e.getMessage(), null);
        }
    }

    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
        methodChannel.setMethodCallHandler(null);
    }
}