// RN1FlowModule.java

package com.rnoneflowlibrary;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.oneflow.analytics.OneFlow;

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

import java.sql.SQLOutput;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class RN1FlowModule extends ReactContextBaseJavaModule {

    private final ReactApplicationContext reactContext;
    public RN1FlowModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
        IntentFilter inf = new IntentFilter();
        inf.addAction("survey_finished");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            reactContext.registerReceiver(listFetched, inf, Context.RECEIVER_NOT_EXPORTED);
        } else {
            reactContext.registerReceiver(listFetched, inf);
        }
    }

    BroadcastReceiver listFetched = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent.getAction().equalsIgnoreCase("survey_finished")){
               

                String surveyDetailJSONString = intent.getStringExtra("surveyDetail");
                try {
                    JSONObject surveyDetailJSONObj = new JSONObject(surveyDetailJSONString);
                    WritableMap payload = convertJsonToMap(surveyDetailJSONObj);
                    reactContext
                            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                            .emit("oneFlowSurveyFinishNotification", payload);
                } catch (JSONException e) {
                    e.printStackTrace();
                }

            }
        }
    };

    @Override
    public String getName() {
        return "RN1Flow";
    }

    @ReactMethod
    public void configure(String projectKey, boolean enableSurveys, Promise promise) {
        try {
            OneFlow.configure(this.getCurrentActivity().getApplicationContext(),projectKey);
            OneFlow.shouldShowSurvey(enableSurveys);
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

    @ReactMethod
    public void enableSurveys(boolean enableSurveys, Promise promise) {
        try {
            OneFlow.shouldShowSurvey(enableSurveys);
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

    @ReactMethod
    public void shouldPrintLog(boolean shouldPrintLog, Promise promise) {
        try {
            OneFlow.shouldPrintLog(shouldPrintLog);
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

    @ReactMethod
    public void useFont(String fontFamilyName, String path, Promise promise) {
        try {
            OneFlow.useFont(path);
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

    @ReactMethod
    public void recordEventName(String eventName, ReadableMap parameters, Promise promise) {
        try {
            HashMap hashMap = null;
            if(parameters != null){
                hashMap = recursivelyDeconstructReadableMap(parameters);
            }
            System.out.println("recordEventName:" + eventName + " | parameters: " +hashMap);
            OneFlow.recordEvents(eventName, hashMap);
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

    @ReactMethod
    public void recordEventNameWithoutParam(String eventName, Promise promise) {
        try {
            System.out.println("recordEventNameWithoutParam:" + eventName);
            OneFlow.recordEvents(eventName);
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

    @ReactMethod
    public void startFlow(String flowId, Promise promise) {
        try {
            OneFlow.startFlow(flowId);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

    @ReactMethod
    public void logUser(String uniqueId, ReadableMap attributes, Promise promise) {
        try {
            HashMap hashMap = null;
            if(attributes != null){
                 hashMap = recursivelyDeconstructReadableMap(attributes);
            }
            OneFlow.logUser(uniqueId, hashMap);
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

     @ReactMethod
    public void logUserWithoutParam(String uniqueId, Promise promise) {
        try {
            OneFlow.logUser(uniqueId);
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

      @ReactMethod
    public void showInbox(Promise promise) {
        try {
            OneFlow.showInbox();
            promise.resolve(null);
        } catch (Exception e) {
            promise.reject(e);
        }
    }

    private HashMap<String, Object> recursivelyDeconstructReadableMap(ReadableMap readableMap) {
        ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
        HashMap<String, Object> deconstructedMap = new HashMap<>();
        while (iterator.hasNextKey()) {
            String key = iterator.nextKey();
            ReadableType type = readableMap.getType(key);
            switch (type) {
                case Null:
                    deconstructedMap.put(key, null);
                    break;
                case Boolean:
                    deconstructedMap.put(key, readableMap.getBoolean(key));
                    break;
                case Number:
                    deconstructedMap.put(key, readableMap.getDouble(key));
                    break;
                case String:
                    deconstructedMap.put(key, readableMap.getString(key));
                    break;
                case Map:
                    deconstructedMap.put(key, recursivelyDeconstructReadableMap(readableMap.getMap(key)));
                    break;
                case Array:
                    deconstructedMap.put(key, recursivelyDeconstructReadableArray(readableMap.getArray(key)));
                    break;
                default:
                    throw new IllegalArgumentException("Could not convert object with key: " + key + ".");
            }

        }
        return deconstructedMap;
    }
    private List<Object> recursivelyDeconstructReadableArray(ReadableArray readableArray) {
        List<Object> deconstructedList = new ArrayList<>(readableArray.size());
        for (int i = 0; i < readableArray.size(); i++) {
            ReadableType indexType = readableArray.getType(i);
            switch(indexType) {
                case Null:
                    deconstructedList.add(i, null);
                    break;
                case Boolean:
                    deconstructedList.add(i, readableArray.getBoolean(i));
                    break;
                case Number:
                    deconstructedList.add(i, readableArray.getDouble(i));
                    break;
                case String:
                    deconstructedList.add(i, readableArray.getString(i));
                    break;
                case Map:
                    deconstructedList.add(i, recursivelyDeconstructReadableMap(readableArray.getMap(i)));
                    break;
                case Array:
                    deconstructedList.add(i, recursivelyDeconstructReadableArray(readableArray.getArray(i)));
                    break;
                default:
                    throw new IllegalArgumentException("Could not convert object at index " + i + ".");
            }
        }
        return deconstructedList;
    }

    private static WritableMap convertJsonToMap(JSONObject jsonObject) throws JSONException {
        WritableMap map = new WritableNativeMap();

        Iterator<String> iterator = jsonObject.keys();
        while (iterator.hasNext()) {
            String key = iterator.next();
            Object value = jsonObject.get(key);
            if (value instanceof JSONObject) {
                map.putMap(key, convertJsonToMap((JSONObject) value));
            } else if (value instanceof  JSONArray) {
                map.putArray(key, convertJsonToArray((JSONArray) value));
            } else if (value instanceof  Boolean) {
                map.putBoolean(key, (Boolean) value);
            } else if (value instanceof  Integer) {
                map.putInt(key, (Integer) value);
            } else if (value instanceof  Double) {
                map.putDouble(key, (Double) value);
            } else if (value instanceof String)  {
                map.putString(key, (String) value);
            } else {
                map.putString(key, value.toString());
            }
        }
        return map;
    }
    private static WritableArray convertJsonToArray(JSONArray jsonArray) throws JSONException {
        WritableArray array = new WritableNativeArray();

        for (int i = 0; i < jsonArray.length(); i++) {
            Object value = jsonArray.get(i);
            if (value instanceof JSONObject) {
                array.pushMap(convertJsonToMap((JSONObject) value));
            } else if (value instanceof  JSONArray) {
                array.pushArray(convertJsonToArray((JSONArray) value));
            } else if (value instanceof  Boolean) {
                array.pushBoolean((Boolean) value);
            } else if (value instanceof  Integer) {
                array.pushInt((Integer) value);
            } else if (value instanceof  Double) {
                array.pushDouble((Double) value);
            } else if (value instanceof String)  {
                array.pushString((String) value);
            } else {
                array.pushString(value.toString());
            }
        }
        return array;
    }
}
