package com.azerion.bluestack.react;

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

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

import android.view.View;
import android.widget.RelativeLayout;
import android.graphics.Color;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.events.RCTEventEmitter;

import com.azerion.bluestack.RequestOptions;
import com.azerion.bluestack.banner.BannerAdSize;
import com.azerion.bluestack.banner.BannerView;
import com.azerion.bluestack.banner.BannerViewListener;
import com.azerion.bluestack.util.Size;

public class BluestackBannerView extends RelativeLayout
        implements LifecycleEventListener, BannerViewListener {

    private static final String TAG = "BannerView Bridge";
    private static final String EVENT_INIT_FAILED = "SDK_FAILED_TO_INIT";

    private final ReactContext mReactContext;
    private boolean mIsBridgeReady = true;
    private final Queue<Event> mPendingEvents = new LinkedList<>();

    private View myAdView;
    private String mPlacementId;
    private String mPreference;
    private String mAdSize;
    private int mWidthDP = -1;
    private int mHeightDP = -1;
    private boolean mShouldLoadWhenReady;
    private boolean propsChanged;

    private final RCTEventEmitter mEventEmitter;
    private BannerView bannerView;

    public BluestackBannerView(ThemedReactContext context) {
        super(context);
        mReactContext = context;
        mReactContext.addLifecycleEventListener(this);
        mEventEmitter = mReactContext.getJSModule(RCTEventEmitter.class);
    }

    public int getParentId() {
        try {
            return ((View) getParent()).getId();
        } catch (Exception e) {
            return -1;
        }
    }

    public void setPlacementId(String placementId) {
        mPlacementId = placementId;
        // Log.d(TAG, "setPlacementId : " + mPlacementId);
    }

    public String getPlacementId() {
        return mPlacementId;
    }

    public void setPreference(String preference) {
        mPreference = preference;
    }

    public String getPreference() {
        return mPreference;
    }

    public void setAdSize(String adSize) {
        BlueStackLogger.debug(TAG, "setAdSize: " + adSize);
        mAdSize = adSize;
    }

    public void setWidth(int widthDP) {
        mWidthDP = widthDP;
        // Log.d(TAG, "setHeight: mWidthDP: " + mWidthDP);
    }

    public void setHeight(int heightDP) {
        mHeightDP = heightDP;
        // Log.d(TAG, "setHeight: mHeightDP: " + mHeightDP);
    }

    public void setShouldLoadWhenReady(boolean shouldLoadWhenReady) {
        mShouldLoadWhenReady = shouldLoadWhenReady;
    }

    public boolean getShouldLoadWhenReady() {
        return mShouldLoadWhenReady;
    }

    public void setVisible(boolean visible) {
        BlueStackLogger.debug(TAG, "setVisible: " + visible);
        MainThreadDispatcher.runOnUiThread(() -> {
            setVisibility(visible ? View.VISIBLE : View.GONE);
        });
    }

    @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");
        mIsBridgeReady = false;
        mPendingEvents.clear();
        mReactContext.removeLifecycleEventListener(this);
        destroy();
    }

    public void createAdViewIfCan() {
        if (bannerView == null &&
                mPlacementId != null &&
                mPreference != null &&
                mAdSize != null) {
            loadBannerAd(mPreference);
        }
    }

    public void setPropsChanged(boolean propsChanged) {
        this.propsChanged = propsChanged;
    }

    public boolean getPropsChanged() {
        return this.propsChanged;
    }

    public void loadBannerAd(String optionsJSON) {
        destroy();

        // check if SDK is initialized
        if (!MobileAds.isInitialized()) {
            tryInitializeSDK();
            return;
        }

        // Prepare final variable for lambda
        final String finalOptionsJSON = (optionsJSON == null || optionsJSON.isEmpty()) ? "{}" : optionsJSON;

        MainThreadDispatcher.runOnUiThread(() -> {
            // instantiate BannerView
            bannerView = new BannerView(mReactContext);

            // set placement Id
            bannerView.setPlacementId(mPlacementId);

            // set listeners
            bannerView.setBannerViewListener(this);

            // set ad size
            if (mAdSize != null) {
                BlueStackLogger.debug(TAG, "getAdSize: " + getAdSize(mAdSize));
                // set ad size
                bannerView.setAdSize(getAdSize(mAdSize));
            }

            RequestOptions mRequestOptions = BluestackAdsCommon.getRequestOptions(finalOptionsJSON, mReactContext);
            bannerView.load(mRequestOptions);
            bannerView.startAutoRefresh();
        });
    }

    private void tryInitializeSDK() {
        // call the init method to initialize the SDK
        BluestackManager.getInstance().initializeInternally(mPlacementId, new BluestackManager.InitializationCallback() {
            @Override
            public void onSuccess() {
                BlueStackLogger.debug(TAG, "SDK initialized successfully");
                loadBannerAd(mPreference);
            }

            @Override
            public void onFailure(String code, String message) {
                BlueStackLogger.error(TAG, "SDK is not initialized : " + message);
                WritableMap event = Arguments.createMap();
                event.putString("message", message);
                event.putInt("code", code.hashCode());
                emitEvent(Events.EVENT_FAIL_TO_LOAD.toString(), event);
            }
        });
    }

    @Override
    public void onAdLoad(final int preferredHeightDP) {
        BlueStackLogger.debug(TAG, "Banner did load preferred Height " + preferredHeightDP + "dp");

        // Ensure callback handling is done on main thread
        MainThreadDispatcher.runOnUiThread(() -> {
            // this.mHeightDP = preferredHeightDP;

            if (bannerView != null) {
                bannerView.setBackgroundColor(Color.TRANSPARENT);
                addView(this.bannerView);
                // setupLayout(this);
                WritableMap event = Arguments.createMap();
                event.putInt("size", preferredHeightDP);
                emitEvent(Events.EVENT_LOAD_BANNER.toString(), event);
            } else {
                BlueStackLogger.error(TAG, "BannerView is null");
                Exception exception = new Exception("BannerView is not initialized");
                onAdFailToLoad(exception);
            }
        });
    }

    // private void setupLayout(BluestackBannerView view) {
    //     Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
    //         @Override
    //         public void doFrame(long frameTimeNanos) {
    //             manuallyLayoutChildren(view);
    //             view.getViewTreeObserver().dispatchOnGlobalLayout();
    //             Choreographer.getInstance().postFrameCallback(this);
    //         }
    //     });
    // }

    // public void manuallyLayoutChildren(BluestackBannerView view) {
    //     // propWidth and propHeight coming from react-native props
    //     int width = dpToPx(view.getAdWidth(), view.getContext());
    //     int height = dpToPx(view.getAdHeight(), view.getContext());
    //     Log.d(TAG, "Banner manuallyLayoutChildren Height: "
    //     + view.getAdHeight() + "dp, " + height + "px");
    //     Log.d(TAG, "Banner manuallyLayoutChildren Width: "
    //     + view.getAdWidth() + "dp, " + width + "px");

    //     view.measure(
    //             View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
    //             View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));

    //     view.layout(0, 0, width, height);
    // }

    @Override
    public void onAdFailToLoad(Exception e) {
        // Ensure callback handling is done on main thread
        MainThreadDispatcher.runOnUiThread(() -> {
            WritableMap event = Arguments.createMap();
            BlueStackLogger.error(TAG, "Banner did fail : " + e.getMessage() + " error code = " + e.hashCode());
            event.putString("message", e.getMessage());
            event.putInt("code", e.hashCode());
            emitEvent(Events.EVENT_FAIL_TO_LOAD.toString(), event);
        });
    }

    @Override
    public void onResize(Size size) {
        // Ensure callback handling is done on main thread
        MainThreadDispatcher.runOnUiThread(() -> {
            BlueStackLogger.debug(TAG, "Banner did resize to " + size.getHeight() + "x" + size.getWidth());
            this.mHeightDP = size.getHeight();
            this.mWidthDP = size.getWidth();
            // Handle banner resize if needed
        });
    }

    @Override
    public void onAdRefresh() {
        // Ensure callback handling is done on main thread
        MainThreadDispatcher.runOnUiThread(() -> {
            BlueStackLogger.debug(TAG, "onAdRefresh");
            emitEvent(Events.EVENT_REFRESHED_BANNER.toString(), null);
        });
    }

    @Override
    public void onAdFailToRefresh(Exception e) {
        // Ensure callback handling is done on main thread
        MainThreadDispatcher.runOnUiThread(() -> {
            WritableMap event = Arguments.createMap();
            BlueStackLogger.error(TAG, "onAdFailToRefresh : " + e.getMessage() + " error code = " + e.hashCode());
            event.putString("message", e.getMessage());
            event.putInt("code", e.hashCode());
            emitEvent(Events.EVENT_FAIL_TO_REFRESH.toString(), event);
        });
    }

    @Override
    public void onAdClick() {
        // Ensure callback handling is done on main thread
        MainThreadDispatcher.runOnUiThread(() -> {
            BlueStackLogger.debug(TAG, "onAdClick");
            emitEvent(Events.EVENT_AD_CLICKED.toString(), null);
        });
    }

    public void emitErrorEvent(WritableMap errorDetails) {
        emitEvent(Events.EVENT_FAIL_TO_LOAD.toString(), errorDetails);
    }

    private void emitEvent(String eventName, WritableMap params) {
        if (mIsBridgeReady && mReactContext.hasActiveReactInstance()) {
            // Log.d(TAG, "emitEvent: Bridge is Ready");
            sendEventImmediately(eventName, params);
        } else {
            BlueStackLogger.debug(TAG, "emitEvent: Bridge is not Ready, put in Queue");
            mPendingEvents.add(new Event(eventName, params));
        }
    }

    private void sendEventImmediately(String eventName, WritableMap params) {
        try {
            mEventEmitter.receiveEvent(getParentId(), eventName, params);
        } catch (Exception e) {
            BlueStackLogger.debug(TAG, "sendEventImmediately: failed" + e.getMessage());
            mPendingEvents.add(new Event(eventName, params));
        }
    }

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

    public void create(String optionsJSON) {
        BlueStackLogger.debug(TAG, "create banner");
        loadBannerAd(optionsJSON);
    }

    public void destroy() {
        BlueStackLogger.debug(TAG, "destroy banner");
        if (bannerView != null) {
            MainThreadDispatcher.runOnUiThread(() -> {
                removeAllViews();
                bannerView.destroy();
                bannerView = null;
            });
        }
    }

    public void refresh(boolean enable) {
        BlueStackLogger.debug(TAG, "refresh enable: " + enable);
        if (bannerView != null) {
            MainThreadDispatcher.runOnUiThread(() -> {
                if (enable) {
                    bannerView.startAutoRefresh();
                } else {
                    bannerView.stopAutoRefresh();
                }
            });
        }
    }

    public int getAdWidth() {
        return mWidthDP;
    }

    public int getAdHeight() {
        return mHeightDP;
    }

    public enum Events {
        EVENT_LOAD_BANNER("onAdLoaded"),
        EVENT_FAIL_TO_LOAD("onAdFailedToLoad"),
        EVENT_REFRESHED_BANNER("onAdRefreshed"),
        EVENT_FAIL_TO_REFRESH("onAdFailedToRefresh"),
        EVENT_AD_CLICKED("onAdClicked");

        private final String mName;

        Events(final String name) {
            mName = name;
        }

        @Override
        public String toString() {
            return mName;
        }
    }

    private BannerAdSize getAdSize(String size) {
        switch (size) {
            case "standard":
                return BannerAdSize.BANNER;
            case "large":
                return BannerAdSize.LARGE_BANNER;
            case "mediumRectangle":
                return BannerAdSize.MEDIUM_RECTANGLE;
            case "full":
                return BannerAdSize.FULL_BANNER;
            case "leaderboard":
                return BannerAdSize.LEADERBOARD;
            case "dynamic":
                return BannerAdSize.DYNAMIC_BANNER;
            case "dynamicLeaderboard":
                return BannerAdSize.DYNAMIC_LEADERBOARD;
            default:
                throw new IllegalArgumentException("Invalid size: " + size);
        }
    }

    private static class Event {
        String name;
        WritableMap params;

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