using System;
using System.Collections.Generic;
using Dirichlet.Mediation;
using Runtime.ADAggregator;
using UnityEngine;

public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAutoAdListener
{
    private const float RewardCloseSettleDelaySeconds = 0.25f;

    private readonly Dictionary<string, RewardedSlotCache> _slotCaches = new Dictionary<string, RewardedSlotCache>(StringComparer.Ordinal);

    private DirichletAdNative _adNative;
    private string _defaultSlotId;
    private string _activeSlotId;
    private bool _rewardVerified;
    private bool _rewardVerifyReceived;
    private bool _closePendingRewardVerify;
    private bool _showSettled;
    private AdTimeHandler _rewardCloseSettleHandler;

    public override int MaxLoadAttempts => TapadnAdController.CurrentOptions?.RewardedMaxLoadAttempts ?? base.MaxLoadAttempts;
    public override float LoadRetryDelaySeconds => Math.Max(0f, (TapadnAdController.CurrentOptions?.RewardedLoadRetryDelayMs ?? 500) / 1000f);
    public override float ShowPendingTimeoutSeconds => Math.Max(1f, (TapadnAdController.CurrentOptions?.RewardedShowTimeoutMs ?? 20000) / 1000f);
    public override bool AutoPreloadOnInit => TapadnAdController.CurrentOptions?.RewardedPrewarmOnInit ?? false;

    public override void OnInit()
    {
        _defaultSlotId = Key;
        _adNative = DirichletAdManager.CreateAdNative();
    }

    public override bool IsReadly()
    {
        var slotId = ResolveCurrentSlotId();
        if (UseAutoLoad())
        {
            return TapadnAdRequestFactory.TryParseSlotId(slotId, out _);
        }

        var cache = GetCache(slotId);
        if (IsCacheReady(cache))
        {
            curState = 2;
            return true;
        }

        if (cache != null && cache.Loading)
        {
            curState = 1;
            return false;
        }

        if (cache != null)
        {
            RemoveCache(slotId);
        }

        curState = 0;
        return false;
    }

    public override void LoadAD()
    {
        var slotId = ResolveCurrentSlotId(out var mapped);
        Debug.Log($"[TapADN] Rewarded load requested. scene={NormalizeScenario(AdScene)}, slot={slotId}, source={GetSlotSource(mapped)}, auto={UseAutoLoad()}");
        if (!TapadnAdRequestFactory.TryParseSlotId(slotId, out _))
        {
            Debug.LogError($"[TapADN] Invalid rewarded slot id: {slotId}");
            curState = 0;
            return;
        }

        if (UseAutoLoad())
        {
            curState = 2;
            try
            {
                _adNative.PreLoad(TapadnAdRequestFactory.BuildRewarded(slotId, TapadnAdController.CurrentOptions), 3);
            }
            catch (Exception exception)
            {
                Debug.LogWarning($"[TapADN] Rewarded preload skipped: {exception.Message}");
            }
            return;
        }

        var cache = GetCache(slotId);
        if (cache != null && (cache.Loading || IsCacheReady(cache)))
        {
            curState = cache.Loading ? 1 : 2;
            return;
        }

        cache = new RewardedSlotCache { Loading = true };
        _slotCaches[slotId] = cache;
        curState = 1;
        TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.AwardVideo, AdScene, slotId);
        _adNative.LoadRewardVideoAd(
            TapadnAdRequestFactory.BuildRewarded(slotId, TapadnAdController.CurrentOptions),
            ad =>
            {
                TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, true, slotId);
                DestroyCachedAd(cache);
                cache.Ad = ad;
                cache.Loading = false;
                cache.LoadedUnix = GetNowUnixSeconds();
                RegisterManualEvents(slotId, cache.Ad);
                ScheduleCacheExpiration(slotId, cache);
                curState = string.Equals(slotId, ResolveCurrentSlotId(), StringComparison.Ordinal) ? 2 : curState;
                Debug.Log($"[TapADN] Rewarded loaded. scene={NormalizeScenario(AdScene)}, slot={slotId}");
            },
            error =>
            {
                TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, false, slotId);
                RemoveCache(slotId);
                curState = string.Equals(slotId, ResolveCurrentSlotId(), StringComparison.Ordinal) ? 0 : curState;
                Debug.LogError($"[TapADN] Rewarded load failed. slot={slotId}, code={error.Code}, message={error.Message}");
            });
    }

    public override void ShowAD(Action onClose, Action<bool> onVideoComplete)
    {
        adListener.onClose = onClose;
        adListener.onVideoComplete = onVideoComplete;
        _rewardVerified = false;
        _rewardVerifyReceived = false;
        _closePendingRewardVerify = false;
        _showSettled = false;
        _rewardCloseSettleHandler?.Kill();
        _rewardCloseSettleHandler = null;
        _activeSlotId = ResolveCurrentSlotId(out var mapped);
        Key = _activeSlotId;
        curState = 0;
        Debug.Log($"[TapADN] Rewarded show requested. scene={NormalizeScenario(AdScene)}, slot={_activeSlotId}, source={GetSlotSource(mapped)}, auto={UseAutoLoad()}");

        if (UseAutoLoad())
        {
            _adNative.ShowRewardVideoAutoAd(TapadnAdRequestFactory.BuildRewarded(_activeSlotId, TapadnAdController.CurrentOptions), this);
            return;
        }

        var cache = GetCache(_activeSlotId);
        if (!IsCacheReady(cache) || cache.Ad == null || !cache.Ad.Show())
        {
            RemoveCache(_activeSlotId);
            OnError(new DirichletError("show_failed", "ShowRewardVideoAd returned false"));
            return;
        }
    }

    public void OnError(DirichletError error)
    {
        if (_showSettled)
        {
            return;
        }

        _showSettled = true;
        _rewardCloseSettleHandler?.Kill();
        _rewardCloseSettleHandler = null;
        curState = 0;
        TapadnSmartLoadOrchestrator.OnShowError(AD_Type.AwardVideo, AdScene, _activeSlotId);
        Debug.LogError($"[TapADN] Rewarded show failed. slot={_activeSlotId}, code={error?.Code}, message={error?.Message}");
        adListener.OnShowError();
    }

    public void OnAdShow()
    {
        TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.AwardVideo, AdScene, _activeSlotId);
        NotifyShowStarted();
    }

    public void OnAdClose()
    {
        if (_showSettled)
        {
            return;
        }

        if (_rewardVerifyReceived)
        {
            CompleteRewardedClose();
            return;
        }

        _closePendingRewardVerify = true;
        _rewardCloseSettleHandler?.Kill();
        _rewardCloseSettleHandler = ADManager.Instance.CreateTimer(RewardCloseSettleDelaySeconds, CompleteRewardedClose);
    }

    public void OnRewardVerify(DirichletRewardVerificationEventArgs args)
    {
        _rewardVerifyReceived = true;
        _rewardVerified = args != null && args.IsVerified;
        if (_closePendingRewardVerify)
        {
            CompleteRewardedClose();
        }
    }

    public void OnAdClick()
    {
    }

    public override void OnPlayRequestStarted()
    {
        var slotId = ResolveCurrentSlotId(out var mapped);
        Key = slotId;
        Debug.Log($"[TapADN] Rewarded play request. scene={NormalizeScenario(AdScene)}, slot={slotId}, source={GetSlotSource(mapped)}, auto={UseAutoLoad()}");
        TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.AwardVideo, AdScene, !UseAutoLoad() && IsReadly(), slotId);
    }

    public override void EnterAdScenario(string scenario)
    {
        AdScene = NormalizeScenario(scenario);
        var slotId = ResolveCurrentSlotId(out var mapped);
        Key = slotId;
        Debug.Log($"[TapADN] Rewarded enter scene. scene={AdScene}, slot={slotId}, source={GetSlotSource(mapped)}");
        TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.AwardVideo, AdScene, slotId);
    }

    private string ResolveCurrentSlotId()
    {
        return ResolveCurrentSlotId(out _);
    }

    private string ResolveCurrentSlotId(out bool mapped)
    {
        if (TapadnAdController.CurrentOptions == null)
        {
            mapped = false;
            return string.IsNullOrWhiteSpace(_defaultSlotId) ? _defaultSlotId : _defaultSlotId.Trim();
        }

        var slotId = TapadnAdController.CurrentOptions.ResolveRewardedSlotId(_defaultSlotId, AdScene, out mapped);
        return string.IsNullOrWhiteSpace(slotId) ? _defaultSlotId : slotId.Trim();
    }

    private static string GetSlotSource(bool mapped)
    {
        return mapped ? "scene" : "default";
    }

    private bool UseAutoLoad()
    {
        return TapadnAdController.CurrentOptions?.RewardedAutoLoad ?? true;
    }

    private RewardedSlotCache GetCache(string slotId)
    {
        return !string.IsNullOrWhiteSpace(slotId) && _slotCaches.TryGetValue(slotId, out var cache) ? cache : null;
    }

    private bool IsCacheReady(RewardedSlotCache cache)
    {
        if (cache == null || cache.Loading || cache.Ad == null || !cache.Ad.IsLoaded || !cache.Ad.IsValid)
        {
            return false;
        }

        var maxAgeSeconds = TapadnAdController.CurrentOptions?.RewardedCacheMaxAgeSeconds ?? 0;
        return maxAgeSeconds <= 0 || GetNowUnixSeconds() - cache.LoadedUnix <= maxAgeSeconds;
    }

    private void RegisterManualEvents(string slotId, DirichletRewardVideoAd ad)
    {
        if (ad == null)
        {
            return;
        }

        ad.Shown += () => OnManualShown(slotId);
        ad.Clicked += OnManualClicked;
        ad.ShowFailed += error => OnManualShowFailed(slotId, error);
        ad.RewardVerified += OnManualRewardVerify;
        ad.Closed += () => OnManualClosed(slotId);
    }

    private void ScheduleCacheExpiration(string slotId, RewardedSlotCache cache)
    {
        cache.ExpireHandler?.Kill();
        cache.ExpireHandler = null;

        var maxAgeSeconds = TapadnAdController.CurrentOptions?.RewardedCacheMaxAgeSeconds ?? 600;
        if (maxAgeSeconds <= 0)
        {
            return;
        }

        cache.ExpireHandler = ADManager.Instance.CreateTimer(maxAgeSeconds, () =>
        {
            if (!ReferenceEquals(GetCache(slotId), cache))
            {
                return;
            }

            Debug.Log($"[TapADN] Rewarded cache expired. slot={slotId}, maxAgeSeconds={maxAgeSeconds}");
            RemoveCache(slotId);
            if (string.Equals(slotId, ResolveCurrentSlotId(), StringComparison.Ordinal))
            {
                curState = 0;
            }
        });
    }

    private void OnManualShown(string slotId)
    {
        _activeSlotId = slotId;
        OnAdShow();
    }

    private void OnManualClicked()
    {
    }

    private void OnManualShowFailed(string slotId, DirichletError error)
    {
        RemoveCache(slotId);
        OnError(error);
    }

    private void OnManualRewardVerify(DirichletRewardVerificationEventArgs args)
    {
        OnRewardVerify(args);
    }

    private void OnManualClosed(string slotId)
    {
        RemoveCache(slotId);
        OnAdClose();
    }

    private void RemoveCache(string slotId)
    {
        var cache = GetCache(slotId);
        if (cache == null)
        {
            return;
        }

        DestroyCachedAd(cache);
        _slotCaches.Remove(slotId);
    }

    private static void DestroyCachedAd(RewardedSlotCache cache)
    {
        if (cache?.Ad == null)
        {
            return;
        }

        try
        {
            cache.ExpireHandler?.Kill();
            cache.ExpireHandler = null;
            cache.Ad.Destroy();
        }
        catch (Exception exception)
        {
            Debug.LogWarning($"[TapADN] Rewarded Destroy failed: {exception.Message}");
        }
        finally
        {
            cache.Ad = null;
            cache.Loading = false;
            cache.LoadedUnix = 0;
        }
    }

    private void CompleteRewardedClose()
    {
        if (_showSettled)
        {
            return;
        }

        _showSettled = true;
        _closePendingRewardVerify = false;
        _rewardCloseSettleHandler?.Kill();
        _rewardCloseSettleHandler = null;
        adListener.OnRewardVerify(_rewardVerified, TapadnAdController.CurrentOptions?.RewardAmount ?? 1, TapadnAdController.CurrentOptions?.RewardName ?? string.Empty);
        adListener.OnAdClose();
    }

    private static string NormalizeScenario(string scenario)
    {
        return string.IsNullOrWhiteSpace(scenario) ? "__default__" : scenario.Trim();
    }

    private static long GetNowUnixSeconds()
    {
        return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
    }

    private sealed class RewardedSlotCache
    {
        public DirichletRewardVideoAd Ad;
        public bool Loading;
        public long LoadedUnix;
        public AdTimeHandler ExpireHandler;
    }
}
