using System;
using System.Threading.Tasks;

public class PostBidRewardedAdController : IRewardedAdController
{
    public event EventHandler OnAdShowFailed;
    public event EventHandler<bool> OnAdClosed;

    private IRewardedAdProvider FirstAdProvider { get; }
    private IRewardedAdProvider SecondAdProvider { get; }

    private readonly double _defaultPriceFloor;

    private readonly PriceFloorIncrementStrategy _strategy;
    private readonly double _step;

    private int _rewardedAdLoadRetryAttempt;

    private double _firstAdProviderEcpm;
    private double _secondAdProviderEcpm;

    private bool _isFirstAdProviderLoaded;
    private bool _isSecondAdProviderLoaded;

    public PostBidRewardedAdController(IRewardedAdProvider firstProvider, IRewardedAdProvider secondProvider)
    {
        AdHelper.Log("[PostBidRewardedAdController] [Constructor] PostBidRewardedAdController()");

        FirstAdProvider = firstProvider;
        SecondAdProvider = secondProvider;

        _defaultPriceFloor = AdConfig.DefaultRewardedAdPriceFloor;

        _strategy = AdConfig.RewardedAdPriceFloorIncrementStrategy;
        _step = AdConfig.RewardedAdPriceFloorIncrementStep;

        SubscribeToAdEvents();

        FirstAdProvider?.LoadRewardedAd();
    }

    public bool IsLoaded() => _isFirstAdProviderLoaded || _isSecondAdProviderLoaded;

    public void ShowRewarded(string placementName)
    {
        AdHelper.Log($"[PostBidRewardedAdController] [Method] ShowRewarded(placement: {placementName})");

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

            FirstAdProvider?.NotifyWin();
            SecondAdProvider?.NotifyLoss(FirstAdProvider?.Name, _firstAdProviderEcpm);
            _isSecondAdProviderLoaded = false;
            _secondAdProviderEcpm = 0d;

            FirstAdProvider?.ShowRewardedAd(placementName);
        }
        else if (!_isFirstAdProviderLoaded && _isSecondAdProviderLoaded)
        {
            AdHelper.Log($"[PostBidRewardedAdController] {FirstAdProvider?.Name} is NOT loaded, {SecondAdProvider?.Name} is loaded, Show {SecondAdProvider?.Name} with placement: {placementName}");

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

            SecondAdProvider?.ShowRewardedAd(placementName);
        }
        else
        {
            if (_firstAdProviderEcpm > _secondAdProviderEcpm)
            {
                AdHelper.Log($"[PostBidRewardedAdController] {FirstAdProvider?.Name} is loaded(ecpm: {_firstAdProviderEcpm}), {SecondAdProvider?.Name} is loaded(ecpm: {_secondAdProviderEcpm}), Show {FirstAdProvider?.Name} with placement: {placementName}");

                FirstAdProvider?.NotifyWin();
                SecondAdProvider?.NotifyLoss(FirstAdProvider?.Name, _firstAdProviderEcpm);
                _isSecondAdProviderLoaded = false;
                _secondAdProviderEcpm = 0d;

                FirstAdProvider?.ShowRewardedAd(placementName);
            }
            else
            {
                AdHelper.Log($"[PostBidRewardedAdController] {FirstAdProvider?.Name} is loaded(ecpm: {_firstAdProviderEcpm}), {SecondAdProvider?.Name} is loaded(ecpm: {_secondAdProviderEcpm}), Show {SecondAdProvider?.Name} with placement: {placementName}");

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

                SecondAdProvider?.ShowRewardedAd(placementName);
            }
        }
    }

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

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

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

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

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

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

        _firstAdProviderEcpm = ecpm;
        _isFirstAdProviderLoaded = true;
        _rewardedAdLoadRetryAttempt = 0;

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

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

        _secondAdProviderEcpm = ecpm;
        _isSecondAdProviderLoaded = true;
        _rewardedAdLoadRetryAttempt = 0;
    }

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

        _firstAdProviderEcpm = 0d;
        _isFirstAdProviderLoaded = false;

        SecondAdProvider?.LoadRewardedAd(_defaultPriceFloor);
    }

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

        _secondAdProviderEcpm = 0d;
        _isSecondAdProviderLoaded = false;

        if (_isFirstAdProviderLoaded) return;

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

        LoadFirstAdProviderAfterDelay(retryDelay);
    }

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

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

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

        _firstAdProviderEcpm = 0d;
        _isFirstAdProviderLoaded = false;

        OnAdShowFailed?.Invoke(this, EventArgs.Empty);

        FirstAdProvider?.LoadRewardedAd();
    }

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

        _isSecondAdProviderLoaded = false;
        _secondAdProviderEcpm = 0d;

        OnAdShowFailed?.Invoke(this, EventArgs.Empty);

        if (_isFirstAdProviderLoaded)
        {
            SecondAdProvider?.LoadRewardedAd(AdHelper.GetIncrementedPriceFloor(_firstAdProviderEcpm, _defaultPriceFloor, _strategy, _step));
        }
        else
        {
            FirstAdProvider?.LoadRewardedAd();
        }
    }

    private void OnFirstAdProviderClosed(object sender, bool shouldReward)
    {
        AdHelper.Log("[PostBidRewardedAdController] [Callback] OnFirstAdProviderClosed()");

        _firstAdProviderEcpm = 0d;
        _isFirstAdProviderLoaded = false;

        OnAdClosed?.Invoke(this, shouldReward);

        FirstAdProvider?.LoadRewardedAd();
    }

    private void OnSecondAdProviderClosed(object sender, bool shouldReward)
    {
        AdHelper.Log("[PostBidRewardedAdController] [Callback] OnSecondAdProviderClosed()");

        _isSecondAdProviderLoaded = false;
        _secondAdProviderEcpm = 0d;

        OnAdClosed?.Invoke(this, shouldReward);

        if (_isFirstAdProviderLoaded)
        {
            SecondAdProvider?.LoadRewardedAd(AdHelper.GetIncrementedPriceFloor(_firstAdProviderEcpm, _defaultPriceFloor, _strategy, _step));
        }
        else
        {
            FirstAdProvider?.LoadRewardedAd();
        }
    }
}
