package com.anythink.reactnative.banner;

import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;

import com.thinkup.banner.api.TUBannerExListener;
import com.thinkup.banner.api.TUBannerView;
import com.thinkup.core.api.TUAdInfo;
import com.thinkup.core.api.TUAdSourceStatusListener;
import com.thinkup.core.api.TUAdStatusInfo;
import com.thinkup.core.api.TUNetworkConfirmInfo;
import com.thinkup.core.api.TUAdRequest;
import com.thinkup.core.api.AdError;
import com.thinkup.core.api.TUShowConfig;
import com.anythink.reactnative.event.ATReactNativeEventEmitter;
import com.anythink.reactnative.listener.AdListenerFactory;
import com.anythink.reactnative.utils.BridgeJsonMapUtil;
import com.anythink.reactnative.utils.Const;
import com.anythink.reactnative.utils.MsgTools;
import com.anythink.reactnative.utils.Utils;

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

/**
* Banner Helper for single placementId.
* Support native TUBannerView + Listener; the callback forwards BannerCall via {@link ATReactNativeEventEmitter}.
* Views share the same Helper/TUBannerView as imperative (pooled by placementId).
 */
public class ATBannerHelper {

    private final Context mAppContext;
    private final Handler mMain = new Handler(Looper.getMainLooper());
    private TUBannerView mBannerView;
    private String mPlacementId;
    private Map<String, Object> mLocalExtra;
    private Map<String, Object> mTkExtra;
    private String mShowScenarioId;
    private String mShowCustomExt;

    /** Run on main thread (run directly if already on main). View ops must be on main thread or "Only the original thread..." crash. */
    private void runOnMain(Runnable r) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            r.run();
        } else {
            mMain.post(r);
        }
    }

    public ATBannerHelper(Context appContext, String placementId) {
        mAppContext = appContext;
        mPlacementId = placementId;
    }

    private void emit(String callbackKey, TUAdInfo adInfo, String errMsg, Map<String, Object> extra) {
        Map<String, Object> info = null;
        if (adInfo != null) {
            try {
                info = Utils.jsonStrToMap(adInfo.toString());
            } catch (Throwable ignore) {
            }
        }
        ATReactNativeEventEmitter.getInstance().sendCallback(
                Const.CallbackMethodCall.BannerCall, callbackKey, mPlacementId, info, errMsg, extra);
    }

    public void setLocalExtra(Map<String, Object> map) {
        if (map == null || map.isEmpty()) {
            return;
        }
        if (mLocalExtra == null) {
            mLocalExtra = new HashMap<>();
        }
        mLocalExtra.putAll(map);
        if (mBannerView != null) {
            mBannerView.setLocalExtra(mLocalExtra);
        }
    }

    public void setShowConfig(Map<String, Object> map) {
        if (map == null || map.isEmpty()) {
            mShowScenarioId = "";
            mShowCustomExt = "";
            applyShowConfig();
            return;
        }
        Object scenario = map.get("scenarioId");
        Object ext = map.get("showCustomExt");
        mShowScenarioId = scenario != null ? String.valueOf(scenario) : "";
        mShowCustomExt = ext != null ? String.valueOf(ext) : "";
        applyShowConfig();
    }

    private void applyShowConfig() {
        if (mBannerView == null) {
            return;
        }
        TUShowConfig.Builder builder = new TUShowConfig.Builder();
        if (mShowScenarioId != null && mShowScenarioId.length() > 0) {
            builder.scenarioId(mShowScenarioId);
        }
        if (mShowCustomExt != null && mShowCustomExt.length() > 0) {
            builder.showCustomExt(mShowCustomExt);
        }
        mBannerView.setShowConfig(builder.build());
    }

    public void setTKExtra(Map<String, Object> map) {
        mTkExtra = map;
        if (mBannerView != null && map != null) {
            mBannerView.setTKExtra(map);
        }
    }

    /** Get/create native banner view (shared by Fabric ViewManager and imperative API). */
    public TUBannerView getOrCreateBannerView(Activity activity) {
        if (mBannerView == null) {
            initBanner(activity);
        }
        return mBannerView;
    }

    private void initBanner(Activity activity) {
        Context ctx = activity != null ? activity : mAppContext;
        mBannerView = new TUBannerView(ctx);
        mBannerView.setPlacementId(mPlacementId);
        if (mLocalExtra != null) {
            mBannerView.setLocalExtra(mLocalExtra);
        }
        if (mTkExtra != null) {
            mBannerView.setTKExtra(mTkExtra);
        }
        applyShowConfig();

        mBannerView.setBannerAdListener(new TUBannerExListener() {
            @Override
            public void onBannerLoaded() {
                MsgTools.printCallback("Banner", "ATBannerHelper", "onBannerLoaded",
                        "placementId=" + mPlacementId);
                emit(Const.BannerCallback.LoadedCallbackKey, null, null, null);
            }

            @Override
            public void onBannerFailed(AdError adError) {
                MsgTools.printCallback("Banner", "ATBannerHelper", "onBannerFailed",
                        "placementId=" + mPlacementId + "--error=" + adError.getFullErrorInfo());
                emit(Const.BannerCallback.LoadFailCallbackKey, null, adError.getFullErrorInfo(), null);
            }

            @Override
            public void onBannerClicked(TUAdInfo atAdInfo) {
                MsgTools.printCallback("Banner", "ATBannerHelper", "onBannerClicked",
                        "placementId=" + mPlacementId + "--adInfo=" + atAdInfo);
                emit(Const.BannerCallback.ClickCallbackKey, atAdInfo, null, null);
            }

            @Override
            public void onBannerShow(TUAdInfo atAdInfo) {
                MsgTools.printCallback("Banner", "ATBannerHelper", "onBannerShow",
                        "placementId=" + mPlacementId + "--adInfo=" + atAdInfo);
                emit(Const.BannerCallback.ShowCallbackKey, atAdInfo, null, null);
            }

            @Override
            public void onBannerClose(TUAdInfo atAdInfo) {
                MsgTools.printCallback("Banner", "ATBannerHelper", "onBannerClose",
                        "placementId=" + mPlacementId + "--adInfo=" + atAdInfo);
                emit(Const.BannerCallback.CloseCallbackKey, atAdInfo, null, null);
            }

            @Override
            public void onBannerAutoRefreshed(TUAdInfo atAdInfo) {
                MsgTools.printCallback("Banner", "ATBannerHelper", "onBannerAutoRefreshed",
                        "placementId=" + mPlacementId + "--adInfo=" + atAdInfo);
                emit(Const.BannerCallback.RefreshCallbackKey, atAdInfo, null, null);
            }

            @Override
            public void onBannerAutoRefreshFail(AdError adError) {
                MsgTools.printCallback("Banner", "ATBannerHelper", "onBannerAutoRefreshFail",
                        "placementId=" + mPlacementId + "--error=" + adError.getFullErrorInfo());
                emit(Const.BannerCallback.RefreshFailCallbackKey, null, adError.getFullErrorInfo(), null);
            }

            @Override
            public void onDeeplinkCallback(boolean isRefresh, TUAdInfo atAdInfo, boolean isSuccess) {
                MsgTools.printCallback("Banner", "ATBannerHelper", "onDeeplinkCallback",
                        "placementId=" + mPlacementId + "--isRefresh=" + isRefresh + "--adInfo=" + atAdInfo
                                + "--isSuccess=" + isSuccess);
                Map<String, Object> extra = new HashMap<>();
                extra.put(Const.CallbackKey.isDeeplinkSuccess, isSuccess);
                emit(Const.BannerCallback.DeeplinkCallbackKey, atAdInfo, null, extra);
            }

            @Override
            public void onDownloadConfirm(Context context, TUAdInfo atAdInfo, TUNetworkConfirmInfo atNetworkConfirmInfo) {
                MsgTools.printCallback("Banner", "ATBannerHelper", "onDownloadConfirm",
                        "placementId=" + mPlacementId + "--adInfo=" + atAdInfo);
                emit(Const.BannerCallback.DownloadConfirmCallbackKey, atAdInfo, null, null);
            }
        });

        mBannerView.setAdRevenueListener(AdListenerFactory.revenueListener());
        mBannerView.setAdMultipleLoadedListener(AdListenerFactory.multipleLoaded(
                Const.CallbackMethodCall.BannerCall,
                Const.BannerCallback.MultipleLoadedCallbackKey, mPlacementId));
        mBannerView.setAdDownloadListener(AdListenerFactory.downloadListener());

        mBannerView.setAdSourceStatusListener(new TUAdSourceStatusListener() {
            @Override
            public void onAdSourceBiddingAttempt(TUAdInfo adInfo) {
                MsgTools.printAdSource("ADSource--Bidding--Attempt", "ATBannerHelper", "onAdSourceBiddingAttempt",
                        MsgTools.adSourceParams(mPlacementId, adInfo));
                emit(Const.BannerCallback.AdSourceBiddingAttemptCallbackKey, adInfo, null, null);
            }

            @Override
            public void onAdSourceBiddingFilled(TUAdInfo adInfo) {
                MsgTools.printAdSource("ADSource--Bidding--Filled", "ATBannerHelper", "onAdSourceBiddingFilled",
                        MsgTools.adSourceParams(mPlacementId, adInfo));
                emit(Const.BannerCallback.AdSourceBiddingFilledCallbackKey, adInfo, null, null);
            }

            @Override
            public void onAdSourceBiddingFail(TUAdInfo adInfo, AdError adError) {
                MsgTools.printAdSource("ADSource--Bidding--Fail", "ATBannerHelper", "onAdSourceBiddingFail",
                        MsgTools.adSourceErrorParams(mPlacementId, adInfo, adError));
                emit(Const.BannerCallback.AdSourceBiddingFailCallbackKey, adInfo,
                        adError != null ? adError.getFullErrorInfo() : null, null);
            }

            @Override
            public void onAdSourceAttempt(TUAdInfo adInfo) {
                MsgTools.printAdSource("ADSource--AD--Start", "ATBannerHelper", "onAdSourceAttempt",
                        MsgTools.adSourceParams(mPlacementId, adInfo));
                emit(Const.BannerCallback.AdSourceAttemptCallbackKey, adInfo, null, null);
            }

            @Override
            public void onAdSourceLoadFilled(TUAdInfo adInfo) {
                MsgTools.printAdSource("ADSource--AD--Load--Success", "ATBannerHelper", "onAdSourceLoadFilled",
                        MsgTools.adSourceParams(mPlacementId, adInfo));
                emit(Const.BannerCallback.AdSourceLoadFilledCallbackKey, adInfo, null, null);
            }

            @Override
            public void onAdSourceLoadFail(TUAdInfo adInfo, AdError adError) {
                MsgTools.printAdSource("ADSource--AD--Load--Fail", "ATBannerHelper", "onAdSourceLoadFail",
                        MsgTools.adSourceErrorParams(mPlacementId, adInfo, adError));
                emit(Const.BannerCallback.AdSourceLoadFailCallbackKey, adInfo,
                        adError != null ? adError.getFullErrorInfo() : null, null);
            }
        });
    }

    public void load(Map<String, Object> settings, Activity activity) {
        getOrCreateBannerView(activity);
        if (mBannerView == null) {
            return;
        }
        TUAdRequest atAdRequest = null;
        try {
            atAdRequest = BridgeJsonMapUtil.atAdRequestFromLoadExtraDic(settings);
        } catch (Throwable ignore) {
        }
        if (atAdRequest != null) {
            mBannerView.loadAd(atAdRequest);
        } else {
            mBannerView.loadAd();
        }
    }

    public Map<String, Object> checkAdStatus() {
        Map<String, Object> map = new HashMap<>(5);
        if (mBannerView != null) {
            TUAdStatusInfo info = mBannerView.checkAdStatus();
            map.put("isLoading", info.isLoading());
            map.put("isReady", info.isReady());
            TUAdInfo top = info.getTUTopAdInfo();
            if (top != null) {
                map.put("adInfo", top.toString());
            }
            return map;
        }
        map.put("isLoading", false);
        map.put("isReady", false);
        return map;
    }

    public List<TUAdInfo> validAdCaches() {
        return mBannerView != null ? mBannerView.checkValidAdCaches() : null;
    }

    /** Fabric unmount: detach view from container, keep loaded ad in Helper pool (full destroy via destroyBanner bridge). */
    public void detachFromContainer() {
        MsgTools.printMsg("banner detachFromContainer: " + mPlacementId);
        final TUBannerView view = mBannerView;
        if (view == null) {
            return;
        }
// removeView touches the view level and must be on the main thread (the JS bridge may be transferred here in the RN bridge thread).
        runOnMain(new Runnable() {
            @Override
            public void run() {
                if (view.getParent() instanceof android.view.ViewGroup) {
                    ((android.view.ViewGroup) view.getParent()).removeView(view);
                }
            }
        });
    }

    /** Unmount/destroy: full release (stop refresh + release ad + detach view). */
    public void destroy() {
        MsgTools.printMsg("banner destroy: " + mPlacementId + ", hasView=" + (mBannerView != null));
        final TUBannerView view = mBannerView;
        mBannerView = null;
        if (view == null) {
            return;
        }
// removeView/destroy touches the view level and must be on the main thread, otherwise "Only the original thread..." will crash.
        runOnMain(new Runnable() {
            @Override
            public void run() {
                if (view.getParent() instanceof android.view.ViewGroup) {
                    ((android.view.ViewGroup) view.getParent()).removeView(view);
                }
                view.destroy();
            }
        });
    }
}
