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.rewarded.RewardedAdListener;
import com.azerion.bluestack.rewarded.RewardedAd;
import com.azerion.bluestack.rewarded.Reward;

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

public class BluestackRewardedAdManager extends ReactNativeModule
        implements LifecycleEventListener, RewardedAdListener {

    private static final String TAG = "RewardedAd Bridge";
    private static final String REACT_CLASS = "BluestackRewardedAdManager";
    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_REWARD_EARNED = "onRewardEarned";
    private static final String EVENT_REWARD_TYPE_KEY = "rewardType";
    private static final String EVENT_REWARD_AMOUNT_KEY = "rewardAmount";
    private static final String EVENT_AD_ERROR = "onAdFailedError";
    private static final String EVENT_AD_MESSAGE_ERROR_KEY = "errorMessage";

    private static final String EVENT_KEY = "rewardedEvent";
    private static final String EVENT_NAME = "RewardedAdEvent";

    private final ReactApplicationContext reactApplicationContext;
    private boolean mIsBridgeReady = true;

    private RewardedAd rewardedAd;
    private RequestOptions mRequestOptions;

    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 BluestackRewardedAdManager(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 rewarded ad
        distroyRewardedAd();

        MainThreadDispatcher.runOnUiThread(() -> {
            // instantiate Ads Factory
            if (rewardedAd == null) {
                rewardedAd = new RewardedAd(getActivity(), placementId);
            }

            // set event listener
            rewardedAd.setRewardedAdListener(this);

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

            try {
                rewardedAd.load(mRequestOptions);
            } catch (Exception e) {
                onAdFailedToLoad(new Exception("Failed to load Rewarded Ad: " + 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() {
        MainThreadDispatcher.runOnUiThread(() -> {
            if (rewardedAd != null && rewardedAd.isReady()) {
                try {
                    rewardedAd.show();
                } catch (Exception e) {
                    onAdFailedToDisplay(new Exception("Failed to display Rewarded Ad: " + e));
                }
            } else {
                onAdFailedToDisplay(new Exception("Rewarded Ad is not loaded"));
                // WritableMap params = Arguments.createMap();
                // params.putString(EVENT_KEY, EVENT_AD_ERROR);
                // params.putString(EVENT_AD_MESSAGE_ERROR_KEY, "Rewarded 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);
    }

    @Override
    public void onAdFailedToLoad(Exception e) {
        BlueStackLogger.error(TAG, "RewardedVideoError: " + e.getMessage());
        WritableMap params = Arguments.createMap();
        params.putString(EVENT_KEY, EVENT_AD_ERROR);
        params.putString(EVENT_AD_MESSAGE_ERROR_KEY, "Rewarded Ad 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, "Rewarded Ad failed to display: error code = " + e.hashCode()
                + ", message = " + e.getMessage());
        sendEvent(getContext(), params);
    }

    @Override
    public void onEarnedReward(@Nullable Reward reward) {
        if (reward != null) {
            BlueStackLogger.debug(TAG, "onEarnedReward" + reward.getType() + " " + reward.getAmount());
            WritableMap params = Arguments.createMap();
            params.putString(EVENT_KEY, EVENT_REWARD_EARNED);
            params.putString(EVENT_REWARD_TYPE_KEY, reward.getType());
            params.putDouble(EVENT_REWARD_AMOUNT_KEY, reward.getAmount());
            sendEvent(getContext(), params);
        } else {
            BlueStackLogger.debug(TAG, "onEarnedReward with no reward object");
        }
    }
    
    @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;
        distroyRewardedAd();
    }

    private void distroyRewardedAd() {
        mPendingEvents.clear();
        if (rewardedAd != null) {
            MainThreadDispatcher.runOnUiThread(() -> {
                rewardedAd.destroy();
                rewardedAd = 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");
    }

}
