using System;
using System.Threading.Tasks;

public class PostBidBannerAdController : IBannerAdController
{
    private IBannerAdProvider FirstAdProvider { get; }
    private IBannerAdProvider SecondAdProvider { get; }

    private readonly int _refreshInterval;

    private readonly double _defaultPriceFloor;

    private readonly PriceFloorIncrementStrategy _strategy;
    private readonly double _step;

    private bool _shouldShowBanner;

    private int _bannerAdLoadRetryAttempt;

    private bool _isBannerShowing;
    private AdProvider _activeBannerAd;

    private double _firstAdProviderEcpm;
    private double _secondAdProviderEcpm;

    private bool _isFirstAdProviderLoaded;
    private bool _isSecondAdProviderLoaded;

    public PostBidBannerAdController(IBannerAdProvider firstProvider, IBannerAdProvider secondProvider)
    {
        AdHelper.Log("[PostBidBannerAdController] [Constructor] PostBidBannerAdController()");

        FirstAdProvider = firstProvider;
        SecondAdProvider = secondProvider;

        _refreshInterval = AdConfig.BannerAdRefreshInterval;

        _defaultPriceFloor = AdConfig.DefaultBannerAdPriceFloor;

        _strategy = AdConfig.BannerAdPriceFloorIncrementStrategy;
        _step = AdConfig.BannerAdPriceFloorIncrementStep;

        SubscribeToAdEvents();

        FirstAdProvider?.LoadBannerAd();
    }

    public void ShowBanner()
    {
        if (_shouldShowBanner) return;

        AdHelper.Log("[PostBidBannerAdController] [Method] ShowBanner()");

        _shouldShowBanner = true;
        ShowBannerAd();
    }

    public void HideBanner()
    {
        if (!_shouldShowBanner) return;

        AdHelper.Log("[PostBidBannerAdController] [Method] HideBanner()");

        _shouldShowBanner = false;
        HideBannerAd();
    }

    private void ShowBannerAd()
    {
        AdHelper.Log("[PostBidBannerAdController] [Method] ShowBannerAd()");

        if (!_isFirstAdProviderLoaded && !_isSecondAdProviderLoaded)
        {
            AdHelper.Log($"[PostBidBannerAdController] {FirstAdProvider?.Name} is NOT loaded, {SecondAdProvider?.Name} is NOT loaded, Skip Show");
        }
        else if (_isFirstAdProviderLoaded && !_isSecondAdProviderLoaded)
        {
            AdHelper.Log($"[PostBidBannerAdController] {FirstAdProvider?.Name} is loaded, {SecondAdProvider?.Name} is NOT loaded, Show {FirstAdProvider?.Name}");

            FirstAdProvider?.NotifyWin();
            SecondAdProvider?.NotifyLoss(FirstAdProvider?.Name, _firstAdProviderEcpm);

            FirstAdProvider?.ShowBannerAd();
            RefreshBanner(_refreshInterval);

            _isBannerShowing = true;
            _activeBannerAd = AdProvider.First;
        }
        else if (!_isFirstAdProviderLoaded && _isSecondAdProviderLoaded)
        {
            AdHelper.Log($"[PostBidBannerAdController] {FirstAdProvider?.Name} is NOT loaded, {SecondAdProvider?.Name} is loaded, Show {SecondAdProvider?.Name}");

            FirstAdProvider?.NotifyLoss(SecondAdProvider?.Name, _secondAdProviderEcpm);
            SecondAdProvider?.NotifyWin();

            SecondAdProvider?.ShowBannerAd();
            RefreshBanner(_refreshInterval);

            _isBannerShowing = true;
            _activeBannerAd = AdProvider.Second;
        }
        else
        {
            if (_firstAdProviderEcpm > _secondAdProviderEcpm)
            {
                AdHelper.Log($"[PostBidBannerAdController] {FirstAdProvider?.Name} is loaded(ecpm: {_firstAdProviderEcpm}), {SecondAdProvider?.Name} is loaded(ecpm: {_secondAdProviderEcpm}), Show {FirstAdProvider?.Name}");

                FirstAdProvider?.NotifyWin();
                SecondAdProvider?.NotifyLoss(FirstAdProvider?.Name, _firstAdProviderEcpm);

                FirstAdProvider?.ShowBannerAd();
                RefreshBanner(_refreshInterval);

                _isBannerShowing = true;
                _activeBannerAd = AdProvider.First;
            }
            else
            {
                AdHelper.Log($"[PostBidBannerAdController] {FirstAdProvider?.Name} is loaded(ecpm: {_firstAdProviderEcpm}), {SecondAdProvider?.Name} is loaded(ecpm: {_secondAdProviderEcpm}), Show {SecondAdProvider?.Name}");

                FirstAdProvider?.NotifyLoss(SecondAdProvider?.Name, _secondAdProviderEcpm);
                SecondAdProvider?.NotifyWin();

                SecondAdProvider?.ShowBannerAd();
                RefreshBanner(_refreshInterval);

                _isBannerShowing = true;
                _activeBannerAd = AdProvider.Second;
            }
        }
    }

    private void HideBannerAd()
    {
        if (!_isBannerShowing) return;

        AdHelper.Log("[PostBidBannerAdController] [Method] HideBannerAd()");

        switch (_activeBannerAd)
        {
            case AdProvider.First:
                FirstAdProvider?.HideBannerAd();
                break;
            case AdProvider.Second:
                SecondAdProvider?.HideBannerAd();
                break;
            default:
                AdHelper.LogError("[PostBidBannerAdController] AdProvider variable is out of range");
                break;
        }

        _firstAdProviderEcpm = 0d;
        _isFirstAdProviderLoaded = false;
        FirstAdProvider?.InstantiateBannerAd();

        _secondAdProviderEcpm = 0d;
        _isSecondAdProviderLoaded = false;
        SecondAdProvider?.InstantiateBannerAd();

        FirstAdProvider?.LoadBannerAd();

        _isBannerShowing = false;
    }

    private async void RefreshBanner(int refreshInterval)
    {
        await Task.Delay(refreshInterval * 1000);

        AdHelper.Log("[PostBidBannerAdController] [Method] RefreshBanner()");

        HideBannerAd();
    }

    private void SubscribeToAdEvents()
    {
        AdHelper.Log("[PostBidBannerAdController] [Method] SubscribeToAdEvents()");

        if (FirstAdProvider == null) return;
        FirstAdProvider.OnAdLoaded += OnFirstAdProviderLoaded;
        FirstAdProvider.OnAdLoadFailed += OnFirstAdProviderLoadFailed;
        FirstAdProvider.OnAdShown += OnFirstAdProviderShown;
        FirstAdProvider.OnAdRevenueReceived += (sender, args) => AdHelper.LogAdRevenue(args.Info);

        if (SecondAdProvider == null) return;
        SecondAdProvider.OnAdLoaded += OnSecondAdProviderLoaded;
        SecondAdProvider.OnAdLoadFailed += OnSecondAdProviderLoadFailed;
        SecondAdProvider.OnAdShown += OnSecondAdProviderShown;
        SecondAdProvider.OnAdRevenueReceived += (sender, args) => AdHelper.LogAdRevenue(args.Info);
    }

    private async void LoadFirstAdProviderAfterDelay(int time)
    {
        AdHelper.Log($"[PostBidBannerAdController] [Method] LoadFirstAdProviderAfterDelay(time: {time})");

        await Task.Delay(time * 1000);
        FirstAdProvider?.LoadBannerAd();
    }

    private void OnFirstAdProviderLoaded(object sender, double ecpm)
    {
        AdHelper.Log($"[PostBidBannerAdController] [Callback] OnFirstAdProviderLoaded(ecpm: {ecpm})");

        _firstAdProviderEcpm = ecpm;
        _isFirstAdProviderLoaded = true;
        _bannerAdLoadRetryAttempt = 0;

        SecondAdProvider?.LoadBannerAd(AdHelper.GetIncrementedPriceFloor(_firstAdProviderEcpm, _defaultPriceFloor, _strategy, _step));
    }

    private void OnSecondAdProviderLoaded(object sender, double ecpm)
    {
        AdHelper.Log($"[PostBidBannerAdController] [Callback] OnSecondAdProviderLoaded(ecpm: {ecpm})");

        _secondAdProviderEcpm = ecpm;
        _isSecondAdProviderLoaded = true;
        _bannerAdLoadRetryAttempt = 0;

        if (_shouldShowBanner && !_isBannerShowing)
        {
            ShowBannerAd();
        }
    }

    private void OnFirstAdProviderLoadFailed(object sender, string cause)
    {
        AdHelper.Log($"[PostBidBannerAdController] [Callback] OnFirstAdProviderLoadFailed(cause: {cause})");

        _firstAdProviderEcpm = 0d;
        _isFirstAdProviderLoaded = false;

        SecondAdProvider?.LoadBannerAd(_defaultPriceFloor);
    }

    private void OnSecondAdProviderLoadFailed(object sender, string cause)
    {
        AdHelper.Log($"[PostBidBannerAdController] [Callback] OnSecondAdProviderLoadFailed(cause: {cause})");

        _secondAdProviderEcpm = 0d;
        _isSecondAdProviderLoaded = false;

        if (_isFirstAdProviderLoaded)
        {
            if (_shouldShowBanner && !_isBannerShowing)
            {
                ShowBannerAd();
            }
            return;
        }

        _bannerAdLoadRetryAttempt++;
        int retryDelay = (int)Math.Pow(2, Math.Min(6, _bannerAdLoadRetryAttempt));

        LoadFirstAdProviderAfterDelay(retryDelay);
    }

    private void OnFirstAdProviderShown(object sender, EventArgs eventArgs)
    {
        AdHelper.Log("[PostBidBannerAdController] [Callback] OnFirstAdProviderShown()");
    }

    private void OnSecondAdProviderShown(object sender, EventArgs eventArgs)
    {
        AdHelper.Log("[PostBidBannerAdController] [Callback] OnSecondAdProviderShown()");
    }
}
