package com.azerion.bluestack.react;

import com.azerion.bluestack.initialization.MediationAdapterStatus;
import com.azerion.bluestack.react.common.ReactNativeModule;
import com.azerion.bluestack.react.utils.MainThreadDispatcher;
import com.azerion.bluestack.react.utils.BlueStackLogger;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

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

import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.*;

import com.azerion.bluestack.MobileAds;
import com.azerion.bluestack.initialization.InitializationListener;
import com.azerion.bluestack.initialization.SDKInitializationStatus;

// Used for the call to addCallback() within this snippet.
// import com.google.common.util.concurrent.Futures;

public class BluestackManager extends ReactNativeModule implements InitializationListener, LifecycleEventListener {

    private static final String TAG = "BluestackManager Bridge";
    private static final String CLASS_NAME = "BluestackManager";
    private static final int EVENT_INIT_SUCCESS = 1;
    private static final String EVENT_INIT_FAILED = "SDK_FAILED_TO_INIT";

    // private Promise mPromise;
    private final List<Promise> promiseList = new ArrayList<>(); // list of promises
    private final List<InitializationCallback> initCallbacks = new ArrayList<>(); // callbacks for internal initialization
    private final boolean isInitInProgress = false;

    private static BluestackManager instance;
    private final ReactApplicationContext reactApplicationContext;
    private boolean mIsBridgeReady = true;
    private boolean mIsDebugMode = false;
    private Runnable deferredAction;

    MobileAds blueStack = null;

    public boolean isDebugMode() {
        return mIsDebugMode;
    }

    public BluestackManager(ReactApplicationContext reactContext) {
        super(reactContext, TAG);
        reactApplicationContext = reactContext;
        reactApplicationContext.addLifecycleEventListener(this);
        instance = this;
    }

    public static BluestackManager getInstance() {
        return instance;
    }

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

    @Override
    public void initialize() {
        // Log.d(TAG, "BluestackManager initialize");
        super.initialize();
    }

    @ReactMethod
    public void initializeSDK(String appId, boolean enableDebug, Promise p) {
        MainThreadDispatcher.runOnUiThread(() -> {
            if (!mIsDebugMode) mIsDebugMode = enableDebug;
            if (p != null) promiseList.add(p);
            if (MobileAds.isInitialized())
            {
                invokeSdkInitCompleteAction(true, "MobileAds SDK is already initialized");
                return;
            }
            BlueStackLogger.debug(TAG, ": Initializing MobileAds SDK with appId: "+appId+ " and debugMode: "+mIsDebugMode);
            MobileAds.INSTANCE.setDebugModeEnabled(mIsDebugMode);
            MobileAds.INSTANCE.initialize(getActivity(), appId, this);
        });
    }

    @ReactMethod(isBlockingSynchronousMethod = true)
    public boolean isSdkInitialized() {
        return MobileAds.isInitialized();
    }

    // For internal initialization
    protected interface InitializationCallback {
        void onSuccess();
        void onFailure(String code, String message);
    }

    protected void initializeInternally(String placementId, InitializationCallback callback) {
        String appId = placementId.split("/")[1];

        if (MobileAds.isInitialized() && callback != null) {
            // Log.d(TAG, "initializeInternally: MobileAds is already initialized");
            callback.onSuccess();
            return;
        }

        if (callback != null) {
            initCallbacks.add(callback);
        }

        initializeSDK(appId, true, null);
    }

    @Override
    public void onInitialized(SDKInitializationStatus initializationStatus) {
        MainThreadDispatcher.runOnUiThread(() -> {
            if (initializationStatus.getMediationAdapterStatusMap().isEmpty()) {
                invokeSdkInitCompleteAction(false, "No Adapters found");
                return;
            }
            try {
                Map<String, Map<String, Object>> adapterStatusMap = new HashMap<>();
                boolean isAnyAdapterReady = false;
                String errorMessage = "Adapters are not initialized";

                for (Map.Entry<String, MediationAdapterStatus> entry : initializationStatus.getMediationAdapterStatusMap().entrySet()) {
                    String adNetworkName = entry.getKey();
                    MediationAdapterStatus adapterStatus = entry.getValue();

                    String adapterState = adapterStatus.getState().toString();
                    String adapterDescription = adapterStatus.getDescription();
                    Map<String, Object> statusMap = new HashMap<>();
                    statusMap.put("name", adapterStatus.getName());
                    statusMap.put("state", adapterState);
                    statusMap.put("description", adapterDescription);
                    adapterStatusMap.put(adNetworkName, statusMap);

                    if (adapterState == "READY") {
                        isAnyAdapterReady = true;
                    } else if ("mngPerf".equals(adNetworkName)) {
                        errorMessage = adapterDescription;
                    }
                }

                if (isAnyAdapterReady) {
                    invokeSdkInitCompleteAction(true, "MobileAds SDK is successfully initialized");
                } else {
                    invokeSdkInitCompleteAction(false, errorMessage);
                }
                // Log adapters status for debugging
                logAdaptersStatus(initializationStatus.getMediationAdapterStatusMap());
            } catch (Exception e) {
                BlueStackLogger.error(TAG, "Error parsing initialization status: " + e.getMessage());
                invokeSdkInitCompleteAction(false, e.getMessage());
            }
        });
    }

    private void logAdaptersStatus(Map<String, MediationAdapterStatus> adapterMap) {
        BlueStackLogger.debug(TAG, "Adapters Initialization Status: ");
        for (Map.Entry<String, MediationAdapterStatus> entry : adapterMap.entrySet()) {
            MediationAdapterStatus adapterStatus = entry.getValue();
            BlueStackLogger.debug(TAG, " Provider: " + adapterStatus.getName() +
                    ", Description: " + adapterStatus.getDescription() +
                    ", State: " + adapterStatus.getState());
        }
    }

    private void executeOrDefer(Runnable action) {
        if (mIsBridgeReady) {
            MainThreadDispatcher.runOnUiThread(action);
        } else {
            deferredAction = action;
        }
    }

    private void flushDeferredAction() {
        if (deferredAction != null) {
            MainThreadDispatcher.runOnUiThread(deferredAction);
            deferredAction = null;
        }
    }

    private void invokeSdkInitCompleteAction(boolean isSuccess, String message) {
        executeOrDefer(() -> {
            if (isSuccess) {
                BlueStackLogger.debug(TAG, "SDK Did Finish Initializing: " + message);
                // call all callbacks in the list
                for (InitializationCallback callback : initCallbacks) {
                    callback.onSuccess();
                }
                // resolve all promises in the list
                for (Promise promise : promiseList) {
                    promise.resolve(EVENT_INIT_SUCCESS);
                }
            } else {
                BlueStackLogger.error(TAG, "SDK Failed to Initialize: " + message);
                // call all callbacks in the list
                for (InitializationCallback callback : initCallbacks) {
                    callback.onFailure(EVENT_INIT_FAILED, message);
                }
                // reject all promises in the list
                for (Promise promise : promiseList) {
                    promise.reject(EVENT_INIT_FAILED, message);
                }
            }
            promiseList.clear();
            initCallbacks.clear();
        });
    }

    @Override
    public void onHostResume() {
        // Log.d(TAG, "onHostResume");
        mIsBridgeReady = true;
        flushDeferredAction();
    }

    @Override
    public void onHostPause() {
        // Log.d(TAG, "onHostPause");
        // mIsBridgeReady = false;
    }

    @Override
    public void onHostDestroy() {
        mIsBridgeReady = false;
        promiseList.clear();
        initCallbacks.clear();
        deferredAction = null;
        reactApplicationContext.removeLifecycleEventListener(this);
    }
}
