package com.azerion.bluestack.react;

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

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

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import com.azerion.bluestack.MobileAds;
import com.azerion.bluestack.RequestOptions;
import com.azerion.bluestack.interstitial.InterstitialAd;
import com.azerion.bluestack.interstitial.InterstitialAdListener;

// import com.mngads.global.MNGConstants;

import java.util.LinkedList;
import java.util.Queue;

public class BluestackInterstitialAdManager extends ReactNativeModule
        implements LifecycleEventListener, InterstitialAdListener {

    private static final String TAG = "InterstitialAd Bridge";
    private static final String REACT_CLASS = "BluestackInterstitialAdManager";
    private static final String EVENT_LOADED_AD = "onAdLoaded";
    private static final String EVENT_DISAPPEAR_AD = "onAdDismissed";
    private static final String EVENT_DID_SHOWN_AD = "onAdDisplayed";
    private static final String EVENT_AD_CLICKED = "onAdClicked";
    private static final String EVENT_AD_ERROR = "onAdFailedError";
    private static final String EVENT_AD_MESSAGE_ERROR_KEY = "errorMessage";

    private static final String EVENT_KEY = "interstitialEvent";
    private static final String EVENT_NAME = "InterstitialAdEvent";

    private final ReactApplicationContext reactApplicationContext;
    private boolean mIsBridgeReady = true;
    private boolean mAutoDisplay = false;

    private RequestOptions mRequestOptions;
    private InterstitialAd interstitialAd;

    private final Queue<Event> mPendingEvents = new LinkedList<>();
    private static class Event {
        String name;
        WritableMap params;

        Event(String name, WritableMap params) {
            this.name = name;
            this.params = params;
        }
    }

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

    @ReactMethod
    public void loadAd(String placementId, boolean autoDisplay, String optionsJSON) {
        // check if SDK is initialized
        if (!MobileAds.isInitialized()) {
            tryInitializeSDK(placementId, autoDisplay, optionsJSON);
            return;
        }

        // distroy the previous interstitial ad
        distroyInterstitialAd();

        MainThreadDispatcher.runOnUiThread(() -> {
            mAutoDisplay = autoDisplay;
            // instantiate the interstitial ad
            if (interstitialAd == null) {
                interstitialAd = new InterstitialAd(getActivity(), placementId);
            }

            // set event listeners
            interstitialAd.setInterstitialAdListener(this);

            mRequestOptions = BluestackAdsCommon.getRequestOptions(optionsJSON, getApplicationContext());

            try {
                interstitialAd.load(mRequestOptions);
            } catch (Exception e) {
                onAdFailedToLoad(new Exception("Failed to load Interstitial Ad with exception: " + e));
            }
        });
    }

    private void tryInitializeSDK(String placementId, boolean autoDisplay, String optionsJSON) {
        // call the init method to initialize the SDK
        BluestackManager.getInstance().initializeInternally(placementId, new BluestackManager.InitializationCallback() {
            @Override
            public void onSuccess() {
                loadAd(placementId, autoDisplay, optionsJSON);
            }

            @Override
            public void onFailure(String code, String message) {
                onAdFailedToLoad(new Exception(message));
            }
        });
    }

    @ReactMethod
    public void displayAd() {
        if (mAutoDisplay) {
            BlueStackLogger.debug(TAG, "Interstitial Ad will be displayed automatically when ad is loaded");
        }else {
            show();
        }
    }

    private void show() {
        MainThreadDispatcher.runOnUiThread(() -> {
            if (interstitialAd != null && interstitialAd.isReady()) {
                try {
                    interstitialAd.show();
                } catch (Exception e) {
                    onAdFailedToDisplay(new Exception("Failed to display Interstitial Ad: " + e));
                }
            } else {
                onAdFailedToDisplay(new Exception("Interstitial Ad is not loaded"));
                // WritableMap params = Arguments.createMap();
                // params.putString(EVENT_KEY, EVENT_AD_ERROR);
                // params.putString(EVENT_AD_MESSAGE_ERROR_KEY, "Interstitial Ad is not loaded");
                // sendEvent(getContext(), params);
            }
        });
    }

    @Override
    public void onAdLoaded() {
        BlueStackLogger.debug(TAG, "onAdLoaded");
        WritableMap params = Arguments.createMap();
        params.putString(EVENT_KEY, EVENT_LOADED_AD);
        sendEvent(getContext(), params);
        if (mAutoDisplay) {
            BlueStackLogger.debug(TAG, "onAdLoaded: Auto display");
            show();
        }
    }

    @Override
    public void onAdFailedToLoad(Exception e) {
        BlueStackLogger.error(TAG, "onAdFailedToLoad: " + e.getMessage()); 
        WritableMap params = Arguments.createMap();
        params.putString(EVENT_KEY, EVENT_AD_ERROR);
        params.putString(EVENT_AD_MESSAGE_ERROR_KEY,
            "Interstitial failed to load: error code= " + e.hashCode() + " message= " + e.getMessage());
        sendEvent(getContext(), params);
    }   

    @Override
    public void onAdDismissed() {
        BlueStackLogger.debug(TAG, "onAdDismissed");
        WritableMap params = Arguments.createMap();
        params.putString(EVENT_KEY, EVENT_DISAPPEAR_AD);
        sendEvent(getContext(), params);
    }

    @Override
    public void onAdDisplayed() {
        BlueStackLogger.debug(TAG, "onAdDisplayed");
        WritableMap params = Arguments.createMap();
        params.putString(EVENT_KEY, EVENT_DID_SHOWN_AD);
        sendEvent(getContext(), params);
    }

    @Override
    public void onAdFailedToDisplay(@NonNull Exception e) {
        BlueStackLogger.error(TAG, "onAdFailedToDisplay: " + e.getMessage());
        WritableMap params = Arguments.createMap();
        params.putString(EVENT_KEY, EVENT_AD_ERROR);
        params.putString(EVENT_AD_MESSAGE_ERROR_KEY,
                "Interstitial failed to display: error code= " + e.hashCode() + " message= " + e.getMessage());
        sendEvent(getContext(), params);
    }

    @Override
    public void onAdClicked() {
        BlueStackLogger.debug(TAG, "onAdClicked");
        WritableMap params = Arguments.createMap();
        params.putString(EVENT_KEY, EVENT_AD_CLICKED);
        sendEvent(getContext(), params);
    }

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

    @Override
    public void initialize() {
        super.initialize();
    }

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

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

    @Override
    public void onHostDestroy() {
        BlueStackLogger.debug(TAG, "onHostDestroy");
        reactApplicationContext.removeLifecycleEventListener(this);
        mIsBridgeReady = false;
        distroyInterstitialAd();
    }

    private void distroyInterstitialAd() {
        mPendingEvents.clear();
        if (interstitialAd != null) {
            MainThreadDispatcher.runOnUiThread(() -> {
                interstitialAd.destroy();
                interstitialAd = null;
            });
        }
    }

    private void sendEvent(ReactContext reactContext, @Nullable WritableMap params) {
        if (mIsBridgeReady && reactContext.hasActiveCatalystInstance()) {
            // Log.d(TAG, "sendEvent: Bridge is Ready");
            sendEventImmediately(reactContext, params);
        } else {
            BlueStackLogger.debug(TAG, "sendEvent: Bridge is not Ready, adding to queue");
            mPendingEvents.add(new Event(EVENT_NAME, params));
        }
    }

    private void sendEventImmediately(ReactContext reactContext, @Nullable WritableMap params) {
        MainThreadDispatcher.runOnUiThread(() -> {
            try {
                reactContext
                        .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                        .emit(EVENT_NAME, params);
            } catch (Exception e) {
                BlueStackLogger.error(TAG, "sendEventImmediately failed: " + e.getMessage());
                mPendingEvents.add(new Event(EVENT_NAME, params));
            }
        });
    }

    private void flushEvents() {
        while (!mPendingEvents.isEmpty()) {
            Event event = mPendingEvents.poll();
            // Log.d(TAG, "flushEvents: " + event.name);
            sendEventImmediately(getContext(), event.params);
        }
    }

    // TODO: Implement these methods to manage the lifecycle of the module and
    // manage listeners or background tasks when listeners are added or removed
    @ReactMethod
    public void addListener(String eventName) {
        // Set up any upstream listeners or background tasks as necessary
    }

    @ReactMethod
    public void removeListeners(Integer count) {
        // Remove upstream listeners, stop unnecessary background tasks
    }

    private void startEventEmitter() {
        // Initialize event sources (e.g., sensors, timers)
        BlueStackLogger.debug("MyModule", "Starting event emitter");
    }
    
    private void stopEventEmitter() {
        // Clean up resources (e.g., stop sensors, timers)
        BlueStackLogger.debug("MyModule", "Stopping event emitter");
    }

}
