#if DAVANCI_ADS
using System;
using UnityEngine;

namespace Davanci.Nexus
{
    // Real mediation slot backed by AppLovin MAX. Wraps MAX's load/show/callback model (set key
    // -> InitializeSdk -> Load* -> Is*Ready -> Show* with global MaxSdkCallbacks) behind the
    // simple INexusAdsProvider callbacks the rest of Nexus expects. Compiled only when the MAX
    // SDK is imported (DAVANCI_ADS); the editor and SDK-less builds use FakeAdsProvider.
    //
    // The next rewarded / interstitial is preloaded after each show (and after every load
    // failure, with a short backoff) so an ad is usually ready by the time the game asks.
    public sealed class NexusAdsProvider : INexusAdsProvider
    {
        string _rewardedId, _interstitialId, _bannerId;
        bool _bannerCreated;

        // Per-show state. MAX fires reward + hidden as separate events, so we remember whether the
        // reward landed before the ad closed.
        Action<NexusAdResult> _rewardedCb;
        string _rewardedPlacement;
        bool _rewardEarned;
        bool _rewardResolved;

        Action<NexusAdResult> _interstitialCb;
        string _interstitialPlacement;

        Action<bool> _onReady;

        public bool IsInitialized { get; private set; }

        public void Initialize(NexusAdsSettings settings, Action<bool> onReady)
        {
            _rewardedId = settings.RewardedAdUnitId;
            _interstitialId = settings.InterstitialAdUnitId;
            _bannerId = settings.BannerAdUnitId;
            _onReady = onReady;

            HookCallbacks();

            MaxSdkCallbacks.OnSdkInitializedEvent += config =>
            {
                IsInitialized = true;
                if (!string.IsNullOrEmpty(_rewardedId)) MaxSdk.LoadRewardedAd(_rewardedId);
                if (!string.IsNullOrEmpty(_interstitialId)) MaxSdk.LoadInterstitial(_interstitialId);
                CreateBannerIfNeeded();
                var cb = _onReady; _onReady = null;
                cb?.Invoke(true);
            };

            if (!string.IsNullOrEmpty(settings.SdkKey)) MaxSdk.SetSdkKey(settings.SdkKey);
            MaxSdk.InitializeSdk();
        }

        // ---- rewarded ------------------------------------------------------------------

        public bool IsRewardedReady =>
            IsInitialized && !string.IsNullOrEmpty(_rewardedId) && MaxSdk.IsRewardedAdReady(_rewardedId);

        public void ShowRewarded(string placement, Action<NexusAdResult> onResult)
        {
            if (!IsInitialized) { onResult?.Invoke(NexusAdResult.Make(NexusAdStatus.NotInitialized, placement)); return; }
            if (!IsRewardedReady)
            {
                if (!string.IsNullOrEmpty(_rewardedId)) MaxSdk.LoadRewardedAd(_rewardedId);
                onResult?.Invoke(NexusAdResult.Make(NexusAdStatus.NotReady, placement));
                return;
            }
            _rewardedCb = onResult;
            _rewardedPlacement = placement;
            _rewardEarned = false;
            _rewardResolved = false;
            MaxSdk.ShowRewardedAd(_rewardedId);
        }

        // ---- interstitial --------------------------------------------------------------

        public bool IsInterstitialReady =>
            IsInitialized && !string.IsNullOrEmpty(_interstitialId) && MaxSdk.IsInterstitialReady(_interstitialId);

        public void ShowInterstitial(string placement, Action<NexusAdResult> onResult)
        {
            if (!IsInitialized) { onResult?.Invoke(NexusAdResult.Make(NexusAdStatus.NotInitialized, placement)); return; }
            if (!IsInterstitialReady)
            {
                if (!string.IsNullOrEmpty(_interstitialId)) MaxSdk.LoadInterstitial(_interstitialId);
                onResult?.Invoke(NexusAdResult.Make(NexusAdStatus.NotReady, placement));
                return;
            }
            _interstitialCb = onResult;
            _interstitialPlacement = placement;
            MaxSdk.ShowInterstitial(_interstitialId);
        }

        // ---- banner / MREC -------------------------------------------------------------

        public void ShowBanner(string placement)
        {
            if (!IsInitialized || string.IsNullOrEmpty(_bannerId)) return;
            CreateBannerIfNeeded();
            MaxSdk.ShowBanner(_bannerId);
        }

        public void HideBanner()
        {
            if (_bannerCreated && !string.IsNullOrEmpty(_bannerId)) MaxSdk.HideBanner(_bannerId);
        }

        public void LaunchTestSuite() => MaxSdk.ShowMediationDebugger();

        void CreateBannerIfNeeded()
        {
            if (_bannerCreated || string.IsNullOrEmpty(_bannerId)) return;
            MaxSdk.CreateBanner(_bannerId, new MaxSdkBase.AdViewConfiguration(MaxSdkBase.AdViewPosition.BottomCenter));
            MaxSdk.SetBannerBackgroundColor(_bannerId, new Color(0.08f, 0.09f, 0.12f));
            _bannerCreated = true;
        }

        // ---- MAX callbacks (global; filtered by ad unit id) ----------------------------

        void HookCallbacks()
        {
            MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += (id, err) => { if (id == _rewardedId) Reload(_rewardedId, MaxSdk.LoadRewardedAd); };
            MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += (id, reward, info) => { if (id == _rewardedId) _rewardEarned = true; };
            MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += (id, err, info) =>
            {
                if (id != _rewardedId) return;
                ResolveRewarded(NexusAdStatus.Failed, err.Message);
                MaxSdk.LoadRewardedAd(_rewardedId);
            };
            MaxSdkCallbacks.Rewarded.OnAdHiddenEvent += (id, info) =>
            {
                if (id != _rewardedId) return;
                ResolveRewarded(_rewardEarned ? NexusAdStatus.Rewarded : NexusAdStatus.Dismissed);
                MaxSdk.LoadRewardedAd(_rewardedId);
            };

            MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += (id, err) => { if (id == _interstitialId) Reload(_interstitialId, MaxSdk.LoadInterstitial); };
            MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent += (id, err, info) =>
            {
                if (id != _interstitialId) return;
                ResolveInterstitial(NexusAdStatus.Failed, err.Message);
                MaxSdk.LoadInterstitial(_interstitialId);
            };
            MaxSdkCallbacks.Interstitial.OnAdHiddenEvent += (id, info) =>
            {
                if (id != _interstitialId) return;
                ResolveInterstitial(NexusAdStatus.Shown);
                MaxSdk.LoadInterstitial(_interstitialId);
            };

            // Impression-level ad revenue → analytics (Firebase ad LTV, AppsFlyer ROAS).
            // Attach the in-game trigger stored at show time (ad_placement); banners have none.
            MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent += (id, info) =>
                DavanciNexus.AdRevenue("AppLovin", info.NetworkName, "rewarded", info.Revenue, info.AdUnitIdentifier, "USD", _rewardedPlacement);
            MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent += (id, info) =>
                DavanciNexus.AdRevenue("AppLovin", info.NetworkName, "interstitial", info.Revenue, info.AdUnitIdentifier, "USD", _interstitialPlacement);
            MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent += (id, info) =>
                DavanciNexus.AdRevenue("AppLovin", info.NetworkName, "banner", info.Revenue, info.AdUnitIdentifier);
        }

        void ResolveRewarded(NexusAdStatus status, string message = "")
        {
            if (_rewardResolved || _rewardedCb == null) return;
            _rewardResolved = true;
            var cb = _rewardedCb;
            _rewardedCb = null;
            _rewardEarned = false;
            cb?.Invoke(NexusAdResult.Make(status, _rewardedPlacement, message));
        }

        void ResolveInterstitial(NexusAdStatus status, string message = "")
        {
            var cb = _interstitialCb; _interstitialCb = null;
            cb?.Invoke(NexusAdResult.Make(status, _interstitialPlacement, message));
        }

        // Reload after a short delay so a flapping network doesn't hammer the mediation stack.
        void Reload(string adUnitId, Action<string> load)
        {
            if (string.IsNullOrEmpty(adUnitId)) return;
            NexusAdsRetry.After(3f, () => load(adUnitId));
        }
    }
}
#endif
