package com.webengage.we_notificationinbox_rn;



import android.text.TextUtils;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
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.WritableMap;
import com.webengage.notification.inbox.WENotificationInbox;
import com.webengage.notification.inbox.callbacks.WEInboxCallback;
import com.webengage.notification.inbox.data.models.PushNotificationTemplateData;
import com.webengage.notification.inbox.data.models.WEInboxData;
import com.webengage.notification.inbox.data.models.WEInboxMessage;
import com.webengage.notification.inbox.data.models.WEInboxMessageData;
import com.webengage.inbox.utils.Constants;
import com.webengage.notification.inbox.utils.WENIHelper;
import com.webengage.notification.inbox.utils.WEUtils;
import com.webengage.sdk.android.Logger;

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class WEInboxBridge extends ReactContextBaseJavaModule {
    private static final String MODULE_NAME = "WEInboxBridge";
    private static final String TAG = "weInboxBridge";

    private ReactApplicationContext reactApplicationContext;
    private static final Object lock = new Object();

    private static volatile WEInboxBridge INSTANCE = null;


    public static WEInboxBridge getInstance(ReactApplicationContext reactContext) {
        if (INSTANCE == null) {
            synchronized (lock) {
                INSTANCE = new WEInboxBridge(reactContext);
            }
        }
        return INSTANCE;
    }

    WEInboxBridge(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    public void setReactNativeContext(ReactApplicationContext context) {
        reactApplicationContext = context;
    }

    private WritableMap convertJsonToWriteableMap(WEInboxData weInboxData) {
        JSONArray jsonArray = new JSONArray();

        for (WEInboxMessage weInboxMessage : weInboxData.getMessageList()) {
            JSONObject jsonData = weInboxMessage.getJsonData();
            try {
                JSONObject jsonObject = new JSONObject(jsonData.toString());
                jsonArray.put(jsonObject);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        WritableMap dataMap = Arguments.createMap();
        dataMap.putBoolean("hasNext", weInboxData.getHasNext());
        dataMap.putString("messageList", jsonArray.toString());

        return dataMap;
    }

    public WEInboxMessage convertToWEInboxMessage(JSONObject jsonObject) throws JSONException {
        String childExperimentId = "";
        String childVariationId = "";

        String experimentId = jsonObject.getString("experimentId");
        String variationId = jsonObject.getString("variationId");
        String status = jsonObject.getString("status");
        String channelType = jsonObject.getString("channelType");
        String creationTime = jsonObject.getString("creationTime");
        String scope = jsonObject.getString("scope");
        String category = jsonObject.getString("category");
        if (jsonObject.has("childExperimentId")) {
            childExperimentId = jsonObject.getString("childExperimentId");
        }
        if (jsonObject.has("childVariationId")) {
            childVariationId = jsonObject.getString("childVariationId");
        }
        Map<String, Object> resultMap = WEUtils.INSTANCE.toMap(jsonObject.getJSONObject("message"));
        WEInboxMessageData msg = new PushNotificationTemplateData(new HashMap<>(resultMap));

        return new WEInboxMessage(
                experimentId,
                variationId,
                status,
                channelType,
                childExperimentId,
                childVariationId,
                creationTime,
                scope,
                category,
                msg,
                jsonObject
        );
    }

    private WritableMap convertErrorToWriteableMap(Map<String, ?> errorMap) {
        WritableMap map = Arguments.createMap();
        // Create a new map and copy the entries from errorMap with a safe cast to Object
        Map<String, Object> castedErrorMap = new HashMap<>(errorMap);
        map.putMap("error", Arguments.makeNativeMap(castedErrorMap));
        return map;
    }


    private HashMap<String, String> convertReadableMapToHashMap(ReadableMap readableMap) {
        HashMap<String, String> hashMap = new HashMap<>();

        ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
        while (iterator.hasNextKey()) {
            String key = iterator.nextKey();
            if (readableMap.getType(key) == ReadableType.String) {
                String value = readableMap.getString(key);
                hashMap.put(key, value);
            }
        }
        return hashMap;
    }

    private List<HashMap<String, String>> convertReadableArrayToList(ReadableArray readableArray) {
        List<HashMap<String, String>> list = new ArrayList<>();
        for (int i = 0; i < readableArray.size(); i++) {
            ReadableMap readableMap = readableArray.getMap(i);
            HashMap<String, String> hashMap = convertReadableMapToHashMap(readableMap);
            list.add(hashMap);
        }
        return list;
    }

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

    @ReactMethod
    public void initWENotificationInbox() {
    }

    @ReactMethod
    public void getNotificationList(final String jsonData, final Callback callback) {
        WEInboxCallback<WEInboxData> inboxCallback = new WEInboxCallback<WEInboxData>() {
            @Override
            public void onSuccess(WEInboxData weInboxData) {
                successCallback(callback, weInboxData);
            }

            @Override
            public void onError(int i, Map<String, ?> error) {
                errorCallback(callback, error);
            }
        };

        if (TextUtils.isEmpty(jsonData)) {
            WENotificationInbox.get(reactApplicationContext).getNotificationList(reactApplicationContext, inboxCallback);
        } else {
            try {

                JSONObject jsonObject = new JSONObject(jsonData);
                WEInboxMessage weInboxMessage = convertToWEInboxMessage(jsonObject);
                WENotificationInbox.get(reactApplicationContext).getNotificationList(reactApplicationContext, weInboxMessage, inboxCallback);
            } catch (JSONException e) {

                errorCallback(callback, Collections.singletonMap("error", "Invalid JSON format: " + e.getMessage()));
            }
        }
    }

    @ReactMethod
    public void getNotificationCount(final Callback callback) {
        WENotificationInbox.get(reactApplicationContext).getUserNotificationCount(reactApplicationContext, new WEInboxCallback<String>() {
            @Override
            public void onSuccess(String countStr) {
                Logger.d(TAG, "count - success " + countStr);
                callback.invoke(countStr, null);
            }

            @Override
            public void onError(int i, @NonNull Map<String, ?> errorMap) {
                WritableMap map = convertErrorToWriteableMap(errorMap);
                callback.invoke(null, map);
            }
        });
    }

    @ReactMethod
    public void markRead(ReadableMap readMap) {
        WENIHelper helper = WENIHelper.Companion.getInstance();
        helper.handleInboxEvent(Constants.EVENT_TYPES.MARK_READ, convertReadableMapToHashMap(readMap));
    }

    @ReactMethod
    public void markUnread(ReadableMap readMap) {
        WENIHelper helper = WENIHelper.Companion.getInstance();
        helper.handleInboxEvent(Constants.EVENT_TYPES.MARK_UNREAD, convertReadableMapToHashMap(readMap));
    }

    @ReactMethod
    public void trackClick(ReadableMap readMap) {
        WENIHelper helper = WENIHelper.Companion.getInstance();
        helper.handleInboxEvent(Constants.EVENT_TYPES.TRACK_CLICK, convertReadableMapToHashMap(readMap));
    }

    @ReactMethod
    public void trackView(ReadableMap readMap) {
        WENIHelper helper = WENIHelper.Companion.getInstance();
        helper.handleInboxEvent(Constants.EVENT_TYPES.TRACK_VIEW, convertReadableMapToHashMap(readMap));
    }

    @ReactMethod
    public void markDelete(ReadableMap readMap) {
        WENIHelper helper = WENIHelper.Companion.getInstance();
        helper.handleInboxEvent(Constants.EVENT_TYPES.MARK_DELETE, convertReadableMapToHashMap(readMap));
    }

    @ReactMethod
    public void readAll(ReadableArray notificationList) {
        WENIHelper helper = WENIHelper.Companion.getInstance();
        helper.handleMultipleInboxEvent(Constants.EVENT_TYPES.MARK_READ, convertReadableArrayToList(notificationList));

    }

    @ReactMethod
    public void unReadAll(ReadableArray notificationList) {
        WENIHelper helper = WENIHelper.Companion.getInstance();
        helper.handleMultipleInboxEvent(Constants.EVENT_TYPES.MARK_UNREAD, convertReadableArrayToList(notificationList));

    }

    @ReactMethod
    public void deleteAll(ReadableArray notificationList) {
        WENIHelper helper = WENIHelper.Companion.getInstance();
        helper.handleMultipleInboxEvent(Constants.EVENT_TYPES.MARK_DELETE, convertReadableArrayToList(notificationList));

    }

    @ReactMethod
    public void onNotificationIconClick() {
        WENotificationInbox.get(reactApplicationContext).onNotificationIconClick();
    }

    public void successCallback(Callback callback, WEInboxData weInboxData) {
        WritableMap dataMap = convertJsonToWriteableMap(weInboxData);
        if(callback != null) {
            callback.invoke(dataMap, null);
        }
    }

    public void errorCallback(Callback callback, Map<String, ?> error) {
        WritableMap errorMap = convertErrorToWriteableMap(error);
        if(callback != null) {
            callback.invoke(null, errorMap);
        }
    }

}
