package com.chatsdk.rn;

import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.chatsdk.ChatSdkSpec;
import com.chatsdk.common.ExtSdkApi;
import com.chatsdk.common.ExtSdkCallback;
import com.chatsdk.common.ExtSdkContext;
import com.chatsdk.common.ExtSdkListener;
import com.chatsdk.common.ExtSdkThreadUtil;
import com.chatsdk.jni.ExtSdkApiJni;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

@ReactModule(name = ExtSdkApiRN.NAME)
public class ExtSdkApiRN extends ChatSdkSpec implements ExtSdkApi {
    private static final String TAG = "ExtSdkApiRN";
    public static final String NAME = "ChatSdk";
    final private ReactApplicationContext reactContext;
    private DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter;

    public ExtSdkApiRN(ReactApplicationContext reactContext) {
        super(reactContext);
        Log.d(TAG, "ExtSdkApiRN: ");
        this.reactContext = reactContext;
        ExtSdkContext.context = this.reactContext;
    }

    @Override
    public void initialize() {
        Log.d(TAG, "initialize: ");
        super.initialize();
        eventEmitter = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class);
        this.init(new Object());
    }

    @Override
    public void invalidate() {
        Log.d(TAG, "invalidate: ");
        super.invalidate();
        this.unInit(null);
    }

    @Override
    @NonNull
    public String getName() {
        return NAME;
    }

    @ReactMethod
    public void multiply(double a, double b, Promise promise) {
        Log.d(TAG, "multiply: " + a + b);
    }

    @ReactMethod
    public void addListener(String eventName) {
        // Keep: Required for RN built in Event Emitter Calls.
    }

    @ReactMethod
    public void removeListeners(double count) {
        // Keep: Required for RN built in Event Emitter Calls.
    }

    @ReactMethod
    public void removeAllListeners() {
        // Keep: Required for RN built in Event Emitter Calls.
    }

//    @ReactMethod
//    public Object[] listeners(String methodType) throws Exception {
//        // Keep: Required for RN built in Event Emitter Calls.
//        throw new Exception("Required for RN built in Event Emitter Calls.");
//    }

    @Override
    public void init(@NonNull Object config) {
        Log.d(TAG, "init: " + config);
        ExtSdkApiJni.nativeInit(config);
        this.addListener(new ExtSdkListener() {
            @Override
            public void onReceive(@NonNull String methodType, @Nullable Object data) {
                Log.d(TAG, "onReceive: " + methodType + ": " + (data != null ? data : ""));
                ExtSdkThreadUtil.mainThreadExecute(() -> {
                    if (data == null) {
                        eventEmitter.emit(methodType, null);
                        return;
                    }
                    if (data instanceof Map) {
                        WritableMap result = Arguments.createMap();
                        new ExtSdkMapHelperRN().toWritableMap((Map<String, Object>)data, result);
                        eventEmitter.emit(methodType, result);
                    } else if (data instanceof Object[]) {
                        WritableNativeArray result = Arguments.fromJavaArgs(new Object[0]);
                        new ExtSdkMapHelperRN().toWritableArray((Object[])data, result);
                        eventEmitter.emit(methodType, result);
                    } else if (data instanceof List) {
                        int size = ((List<Object>)data).size();
                        Object[] a = new Object[size];
                        for (int i = 0; i < size; ++i) {
                            a[i] = ((List<Object>)data).get(i);
                        }
                        WritableNativeArray result = Arguments.fromJavaArgs(new Object[0]);
                        new ExtSdkMapHelperRN().toWritableArray(a, result);
                        eventEmitter.emit(methodType, result);
                    } else {
                        throw new RuntimeException("Cannot support argument of type " + data.getClass());
                    }
                });
            }

            @Override
            public void setType(@NonNull String listenerType) {}

            @Override
            public void getType() {}
        });
    }

    @Override
    public void unInit(@Nullable Object params) {
        Log.d(TAG, "unInit: " + params);
        ExtSdkApiJni.nativeUnInit();
    }

    @Override
    public void addListener(@NonNull ExtSdkListener listener) {
        Log.d(TAG, "addListener: " + listener);
        ExtSdkApiJni.nativeAddListener(listener);
    }

    @Override
    public void delListener(@NonNull ExtSdkListener listener) {
        Log.d(TAG, "delListener: " + listener);
        ExtSdkApiJni.nativeDelListener(listener);
    }

    @Override
    public void callSdkApi(@NonNull String methodType, @Nullable Object params, @NonNull ExtSdkCallback callback) {
        ExtSdkApiJni.nativeCallSdkApi(methodType, params, callback);
    }

    @ReactMethod
    public void callMethod(String method, @javax.annotation.Nullable ReadableMap args, Promise promise) {
        Log.d(TAG, "callMethod: " + method + ": " + (args != null ? args : ""));
        ExtSdkThreadUtil.asyncExecute(() -> {
            Object subParams = null;
            if (args != null) {
              for (Map.Entry<String, Object> stringObjectEntry : args.toHashMap().entrySet()) {
                subParams = stringObjectEntry.getValue();
                break;
              }
            }

            this.callSdkApi(method, subParams, new ExtSdkCallback() {
                @Override
                public void success(@Nullable Object data) {
                    ExtSdkThreadUtil.mainThreadExecute(() -> {
                        WritableMap result = Arguments.createMap();
                        new ExtSdkMapHelperRN().toWritableMap((Map<String, Object>)data, result);
                        promise.resolve(result);
                    });
                }

                @Override
                public void fail(@NonNull int code, @Nullable Object ext) {
                    ExtSdkThreadUtil.mainThreadExecute(() -> {
                        // todo: errorMessage: 是code的字符串形式
                        String reason = "";
                        try {
                            if (ext != null) {
                                reason = ext.toString();
                            }
                        } catch (Exception e) {
                            reason = e.getMessage();
                        }
                        promise.reject(String.valueOf(code), reason);
                    });
                }
            });
        });
    }
}
