using System;
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;

namespace Dirichlet.Mediation
{
    /// <summary>
    /// Platform handle for ad instances. This is a simple wrapper around the handle string
    /// returned from native bridge, matching the native SDK pattern where ad objects are
    /// returned directly from loadXXXAd() methods.
    /// </summary>
    public sealed class DirichletPlatformAdHandle
    {
        public string DebugId { get; }

        internal DirichletPlatformAdHandle(string debugId)
        {
            DebugId = debugId ?? throw new ArgumentNullException(nameof(debugId));
        }

        internal static DirichletPlatformAdHandle FromNative(string handleId)
        {
            if (string.IsNullOrEmpty(handleId))
            {
                handleId = Guid.NewGuid().ToString("N");
            }
            return new DirichletPlatformAdHandle(handleId);
        }

        internal static DirichletPlatformAdHandle CreateStub()
        {
            return new DirichletPlatformAdHandle(Guid.NewGuid().ToString("N"));
        }

        public override string ToString() => $"DirichletPlatformAdHandle(DebugId={DebugId})";
    }

    public enum DirichletAdType
    {
        RewardVideo = 0,
        Interstitial = 1,
        Banner = 2,
        Splash = 3,
        ExpressFeed = 4,
        NativeFeed = 5
    }

    /// <summary>
    /// Mirrors the Android-side DirichletAdManager to keep configuration state and create ad natives.
    /// </summary>
    public static class DirichletAdManager
    {
        private static readonly object StateLock = new object();
        private static DirichletAdConfig currentConfig;

        /// <summary>
        /// Returns the last configuration applied to the native SDK.
        /// </summary>
        public static DirichletAdConfig CurrentConfig
        {
            get
            {
                lock (StateLock)
                {
                    return currentConfig;
                }
            }
        }

        internal static void ApplyConfig(DirichletAdConfig config)
        {
            lock (StateLock)
            {
                currentConfig = config;
            }
        }

        internal static void Clear()
        {
            lock (StateLock)
            {
                currentConfig = null;
            }
        }

        /// <summary>
        /// Creates a Unity-side DirichletAdNative wrapper that mirrors the Android aggregator API.
        /// </summary>
        public static DirichletAdNative CreateAdNative()
        {
            return new DirichletAdNative(DirichletSdk.GetBridge());
        }

        internal static DirichletAdNative CreateAdNative(IDirichletPlatformBridge bridge)
        {
            return new DirichletAdNative(bridge);
        }
    }

    /// <summary>
    /// Thin wrapper around native ad handles provided by the platform bridge.
    /// </summary>

    public sealed class DirichletAdRequest
    {
        public long SpaceId { get; }
        public string Extra1 { get; }
        public string UserId { get; }
        public string RewardName { get; }
        public int? RewardAmount { get; }
        public string Query { get; }
        public int? ExpressViewWidth { get; }
        public int? ExpressViewHeight { get; }
        public int? ExpressImageWidth { get; }
        public int? ExpressImageHeight { get; }
        public long? MinaId { get; }

        /// <summary>
        /// Banner auto-ad slide rotation interval, in seconds. Native default is 30.
        /// </summary>
        public int? SlideInterval { get; }

        private DirichletAdRequest(Builder builder)
        {
            SpaceId = builder.spaceId;
            Extra1 = builder.extra1;
            UserId = builder.userId;
            RewardName = builder.rewardName;
            RewardAmount = builder.rewardAmount;
            Query = builder.query;
            ExpressViewWidth = builder.expressViewWidth;
            ExpressViewHeight = builder.expressViewHeight;
            ExpressImageWidth = builder.expressImageWidth;
            ExpressImageHeight = builder.expressImageHeight;
            MinaId = builder.minaId;
            SlideInterval = builder.slideInterval;
        }

        public Builder ToBuilder()
        {
            var builder = new Builder()
                .WithSpaceId(SpaceId)
                .WithExtra1(Extra1)
                .WithUserId(UserId)
                .WithRewardName(RewardName)
                .WithQuery(Query);

            if (RewardAmount.HasValue)
            {
                builder.WithRewardAmount(RewardAmount.Value);
            }

            if (ExpressViewWidth.HasValue || ExpressViewHeight.HasValue)
            {
                builder.WithExpressViewSize(ExpressViewWidth ?? -1, ExpressViewHeight ?? -1);
            }

            if (ExpressImageWidth.HasValue || ExpressImageHeight.HasValue)
            {
                builder.WithExpressImageSize(ExpressImageWidth ?? -1, ExpressImageHeight ?? -1);
            }

            if (MinaId.HasValue)
            {
                builder.WithMinaId(MinaId.Value);
            }

            if (SlideInterval.HasValue)
            {
                builder.WithSlideInterval(SlideInterval.Value);
            }

            return builder;
        }

        internal Dictionary<string, object> ToBridgePayload()
        {
            var payload = new Dictionary<string, object>(StringComparer.Ordinal)
            {
                ["space_id"] = SpaceId
            };

            if (!string.IsNullOrEmpty(Extra1))
            {
                payload["extra1"] = Extra1;
            }

            if (!string.IsNullOrEmpty(UserId))
            {
                payload["user_id"] = UserId;
            }

            if (!string.IsNullOrEmpty(RewardName))
            {
                payload["reward_name"] = RewardName;
            }

            if (RewardAmount.HasValue)
            {
                payload["reward_amount"] = RewardAmount.Value;
            }

            if (!string.IsNullOrEmpty(Query))
            {
                payload["query"] = Query;
            }

            if (ExpressViewWidth.HasValue)
            {
                payload["express_width"] = ExpressViewWidth.Value;
            }

            if (ExpressViewHeight.HasValue)
            {
                payload["express_height"] = ExpressViewHeight.Value;
            }

            if (ExpressImageWidth.HasValue)
            {
                payload["express_image_width"] = ExpressImageWidth.Value;
            }

            if (ExpressImageHeight.HasValue)
            {
                payload["express_image_height"] = ExpressImageHeight.Value;
            }

            if (MinaId.HasValue)
            {
                payload["mina_id"] = MinaId.Value;
            }

            if (SlideInterval.HasValue)
            {
                // Native Java API is `withSlideInternal` (spelled "Internal" by upstream);
                // keep the wire key matching the native method name.
                payload["slide_internal"] = SlideInterval.Value;
            }

            return payload;
        }

        public sealed class Builder
        {
            internal long spaceId;
            internal string extra1;
            internal string userId;
            internal string rewardName;
            internal int? rewardAmount;
            internal string query;
            internal int? expressViewWidth;
            internal int? expressViewHeight;
            internal int? expressImageWidth;
            internal int? expressImageHeight;
            internal long? minaId;
            internal int? slideInterval;

            public Builder WithSpaceId(long value)
            {
                if (value <= 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(value), "SpaceId must be greater than zero.");
                }

                spaceId = value;
                return this;
            }

            public Builder WithExtra1(string value)
            {
                extra1 = value;
                return this;
            }

            public Builder WithUserId(string value)
            {
                userId = value;
                return this;
            }

            public Builder WithRewardName(string value)
            {
                rewardName = value;
                return this;
            }

            public Builder WithRewardAmount(int value)
            {
                rewardAmount = value;
                return this;
            }

            public Builder WithQuery(string value)
            {
                query = value;
                return this;
            }

            public Builder WithExpressViewSize(int width, int height)
            {
                expressViewWidth = width;
                expressViewHeight = height;
                return this;
            }

            public Builder WithExpressImageSize(int width, int height)
            {
                expressImageWidth = width;
                expressImageHeight = height;
                return this;
            }

            public Builder WithMinaId(long value)
            {
                minaId = value;
                return this;
            }

            /// <summary>
            /// Sets banner auto-ad slide rotation interval, in seconds (recommended 30–120, native default 30).
            /// </summary>
            public Builder WithSlideInterval(int seconds)
            {
                slideInterval = seconds;
                return this;
            }

            public DirichletAdRequest Build()
            {
                if (spaceId <= 0)
                {
                    throw new InvalidOperationException("SpaceId must be set to a value greater than zero before building the request.");
                }

                return new DirichletAdRequest(this);
            }
        }
    }

    public enum DirichletBannerAlignment
    {
        Top = 0,
        Bottom = 1
    }

    public sealed class DirichletAdShowOptions
    {
        public DirichletBannerAlignment BannerAlignment { get; set; } = DirichletBannerAlignment.Bottom;
        public int BannerOffset { get; set; }

        internal Dictionary<string, object> ToBridgePayload()
        {
            var payload = new Dictionary<string, object>(StringComparer.Ordinal);

            // Banner options are always included if set, Bridge will handle based on ad type
            payload["banner_baseline"] = (int)BannerAlignment;
            payload["banner_offset"] = BannerOffset;

            return payload;
        }
    }

    public sealed class DirichletAdNative
    {
        private readonly IDirichletPlatformBridge bridge;
        private static readonly object RewardAutoSessionLock = new object();
        private static readonly Dictionary<string, AutoRewardVideoSession> RewardAutoSessions = new Dictionary<string, AutoRewardVideoSession>(StringComparer.Ordinal);

        public static DirichletAdNative Create() => DirichletAdManager.CreateAdNative();

        internal DirichletAdNative(IDirichletPlatformBridge bridge)
        {
            this.bridge = bridge ?? throw new ArgumentNullException(nameof(bridge));
        }

        public void LoadRewardVideoAd(DirichletAdRequest request, Action<DirichletRewardVideoAd> onLoaded, Action<DirichletError> onFailure)
        {
        if (!ValidateRequest(request, onFailure))
        {
            return;
        }

        bridge.LoadRewardVideoAd(request,
            handle =>
            {
                var ad = DirichletRewardVideoAd.Create(bridge, handle, request.SpaceId);
                ad.MarkLoaded();
                onLoaded?.Invoke(ad);
            },
            onFailure);
        }

        public void LoadInterstitialAd(DirichletAdRequest request, Action<DirichletInterstitialAd> onLoaded, Action<DirichletError> onFailure)
        {
        if (!ValidateRequest(request, onFailure))
        {
            return;
        }

        bridge.LoadInterstitialAd(request,
            handle =>
            {
                var ad = DirichletInterstitialAd.Create(bridge, handle, request.SpaceId);
                ad.MarkLoaded();
                onLoaded?.Invoke(ad);
            },
            onFailure);
        }

        public void LoadBannerAd(DirichletAdRequest request, Action<DirichletBannerAd> onLoaded, Action<DirichletError> onFailure)
        {
        if (!ValidateRequest(request, onFailure))
        {
            return;
        }

        bridge.LoadBannerAd(request,
            handle =>
            {
                var ad = DirichletBannerAd.Create(bridge, handle, request.SpaceId);
                ad.MarkLoaded();
                onLoaded?.Invoke(ad);
            },
            onFailure);
        }

        public void LoadSplashAd(DirichletAdRequest request, Action<DirichletSplashAd> onLoaded, Action<DirichletError> onFailure)
        {
        if (!ValidateRequest(request, onFailure))
        {
            return;
        }

        bridge.LoadSplashAd(request,
            handle =>
            {
                var ad = DirichletSplashAd.Create(bridge, handle, request.SpaceId);
                ad.MarkLoaded();
                onLoaded?.Invoke(ad);
            },
            onFailure);
        }

        public void LoadExpressFeedAd(DirichletAdRequest request, Action<IReadOnlyList<DirichletAd>> onLoaded, Action<DirichletError> onFailure)
        {
            NotifyNotSupported("express_feed", onFailure);
        }

        public void LoadNativeFeedAd(DirichletAdRequest request, Action<IReadOnlyList<DirichletAd>> onLoaded, Action<DirichletError> onFailure)
        {
            NotifyNotSupported("native_feed", onFailure);
        }

        /// <summary>
        /// Shows a reward video ad with automatic load-and-show logic.
        /// This method combines loading and showing into a single operation.
        ///
        /// - Android: 使用 native 侧的 AutoAd（可能包含缓存策略，保持原有行为）
        /// - iOS: 使用 Unity C# 侧的“load 成功后立刻 show”的简化方案（不做缓存）
        /// </summary>
        /// <param name="request">Ad request parameters</param>
        /// <param name="listener">Listener for all ad events (show/close/reward/click/error)</param>
        public void ShowRewardVideoAutoAd(DirichletAdRequest request, IDirichletRewardVideoAutoAdListener listener)
        {
            if (!ValidateRequest(request, error => listener?.OnError(error)))
            {
                return;
            }

#if UNITY_ANDROID && !UNITY_EDITOR
            bridge.ShowRewardVideoAutoAd(request, listener);
#else
            // iOS/Editor/other platforms: use simplified load+show implementation (no cache).
            ShowRewardVideoLoadAndShowInternal(request, listener);
#endif
        }

        /// <summary>
        /// Shows an interstitial ad with automatic load-and-show logic.
        /// Android only - iOS receives OnError with not_supported.
        /// </summary>
        public void ShowInterstitialAutoAd(DirichletAdRequest request, IDirichletInterstitialAutoAdListener listener)
        {
            if (!ValidateRequest(request, error => listener?.OnError(error)))
            {
                return;
            }

            bridge.ShowInterstitialAutoAd(request, listener);
        }

        /// <summary>
        /// Shows a banner ad with automatic load + rotation logic. Container is created internally and
        /// anchored at the bottom of the screen by default. Use <see cref="DirichletAdRequest.Builder.WithSlideInterval"/>
        /// to control rotation interval. Android only - iOS receives OnError with not_supported.
        /// </summary>
        public void ShowBannerAutoAd(DirichletAdRequest request, IDirichletBannerAutoAdListener listener)
        {
            ShowBannerAutoAd(request, null, listener);
        }

        /// <summary>
        /// Banner auto-ad with explicit alignment/offset overrides. Advanced overload —
        /// the standard form is <see cref="ShowBannerAutoAd(DirichletAdRequest, IDirichletBannerAutoAdListener)"/>.
        /// </summary>
        public void ShowBannerAutoAd(DirichletAdRequest request, DirichletAdShowOptions options, IDirichletBannerAutoAdListener listener)
        {
            if (!ValidateRequest(request, error => listener?.OnError(error)))
            {
                return;
            }

            bridge.ShowBannerAutoAd(request, options ?? new DirichletAdShowOptions(), listener);
        }

        /// <summary>
        /// Shows a splash ad with automatic load logic. Container is a fullscreen overlay created internally.
        /// Android only - iOS receives OnError with not_supported.
        /// </summary>
        public void ShowSplashAutoAd(DirichletAdRequest request, IDirichletSplashAutoAdListener listener)
        {
            ShowSplashAutoAd(request, null, listener);
        }

        /// <summary>
        /// Splash auto-ad advanced overload (currently no effective options for splash).
        /// </summary>
        public void ShowSplashAutoAd(DirichletAdRequest request, DirichletAdShowOptions options, IDirichletSplashAutoAdListener listener)
        {
            if (!ValidateRequest(request, error => listener?.OnError(error)))
            {
                return;
            }

            bridge.ShowSplashAutoAd(request, options ?? new DirichletAdShowOptions(), listener);
        }

        /// <summary>
        /// Preloads an ad for later auto-show. Currently only type=3 (reward video) is handled by the native SDK.
        /// Android only - iOS is a no-op.
        /// </summary>
        public void PreLoad(DirichletAdRequest request, int type)
        {
            if (!ValidateRequest(request, null))
            {
                return;
            }

            bridge.PreLoad(request, type);
        }

        private void ShowRewardVideoLoadAndShowInternal(DirichletAdRequest request, IDirichletRewardVideoAutoAdListener listener)
        {
            LoadRewardVideoAd(
                request,
                ad =>
                {
                    if (ad == null)
                    {
                        listener?.OnError(new DirichletError("invalid_ad", "Load callback returned null ad"));
                        return;
                    }

                    var sessionId = Guid.NewGuid().ToString("N");
                    var session = new AutoRewardVideoSession(sessionId, ad, listener);
                    RegisterRewardAutoSession(session);
                    ad.SetInteractionListener(session);

                    // Load succeeded; show immediately.
                    var shown = ad.Show();
                    if (!shown)
                    {
                        session.FailAndDispose(new DirichletError("show_failed", "ShowRewardVideoAd returned false"));
                    }
                },
                error =>
                {
                    listener?.OnError(error ?? new DirichletError("load_failed", "LoadRewardVideoAd failed"));
                });
        }

    private static bool ValidateRequest(DirichletAdRequest request, Action<DirichletError> onFailure)
    {
        if (request == null)
        {
            onFailure?.Invoke(new DirichletError("invalid_request", "DirichletAdRequest cannot be null"));
            return false;
        }

        if (request.SpaceId <= 0)
        {
            onFailure?.Invoke(new DirichletError("invalid_space_id", "DirichletAdRequest.SpaceId must be greater than zero"));
            return false;
        }

        return true;
    }

        private void NotifyNotSupported(string feature, Action<DirichletError> onFailure)
        {
            onFailure?.Invoke(new DirichletError("not_supported", $"Dirichlet Unity bridge does not yet support {feature} ads."));
        }

        private static void RegisterRewardAutoSession(AutoRewardVideoSession session)
        {
            if (session == null)
            {
                return;
            }

            lock (RewardAutoSessionLock)
            {
                RewardAutoSessions[session.SessionId] = session;
            }
        }

        private static void UnregisterRewardAutoSession(string sessionId)
        {
            if (string.IsNullOrEmpty(sessionId))
            {
                return;
            }

            lock (RewardAutoSessionLock)
            {
                RewardAutoSessions.Remove(sessionId);
            }
        }

        private sealed class AutoRewardVideoSession : IDirichletRewardAdInteractionListener
        {
            public string SessionId { get; }

            private readonly DirichletRewardVideoAd ad;
            private readonly IDirichletRewardVideoAutoAdListener listener;
            private bool disposed;

            public AutoRewardVideoSession(string sessionId, DirichletRewardVideoAd ad, IDirichletRewardVideoAutoAdListener listener)
            {
                SessionId = string.IsNullOrEmpty(sessionId) ? Guid.NewGuid().ToString("N") : sessionId;
                this.ad = ad ?? throw new ArgumentNullException(nameof(ad));
                this.listener = listener;
            }

            public void OnAdShow()
            {
                if (disposed)
                {
                    return;
                }

                listener?.OnAdShow();
            }

            public void OnAdClick()
            {
                if (disposed)
                {
                    return;
                }

                listener?.OnAdClick();
            }

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

                listener?.OnAdClose();
                Dispose();
            }

            public void OnRewardVerify(DirichletRewardVerificationEventArgs args)
            {
                if (disposed)
                {
                    return;
                }

                listener?.OnRewardVerify(args);
            }

            public void FailAndDispose(DirichletError error)
            {
                if (disposed)
                {
                    return;
                }

                listener?.OnError(error ?? new DirichletError("unknown_error", "Unknown error"));
                Dispose();
            }

            private void Dispose()
            {
                if (disposed)
                {
                    return;
                }

                disposed = true;
                UnregisterRewardAutoSession(SessionId);

                try
                {
                    ad?.Destroy();
                }
                catch (Exception ex)
                {
                    Debug.LogWarning($"[Dirichlet] Auto reward ad Destroy failed: {ex.Message}");
                }
            }
        }
    }

    /// <summary>
    /// Base Unity-side ad handle. Responsible for keeping track of lifecycle state.
    /// Matches the native SDK pattern where ad objects are returned from loadXXXAd() methods.
    /// </summary>
    public abstract class DirichletAd
    {
        protected readonly DirichletPlatformAdHandle PlatformHandle;
        private readonly IDirichletPlatformBridge bridge;
        private readonly long spaceId;

        public string SlotId => spaceId > 0 ? spaceId.ToString(CultureInfo.InvariantCulture) : string.Empty;
        public bool IsLoaded { get; protected set; }

        /// <summary>
        /// Returns whether the ad is still valid and can be shown.
        /// This checks the native ad object's validity, which may expire after some time.
        /// Always call this before Show() to ensure the ad hasn't expired.
        /// </summary>
        public bool IsValid => bridge?.IsAdValid(PlatformHandle) ?? false;

        /// <summary>
        /// Raised when the native layer confirms the ad was shown to the user.
        /// </summary>
        public event Action Shown;

        /// <summary>
        /// Raised when the user clicks the ad.
        /// </summary>
        public event Action Clicked;

        /// <summary>
        /// Raised when the ad is closed (either by user action or channel logic).
        /// </summary>
        public event Action Closed;

        /// <summary>
        /// Raised when the native layer reports that a loaded ad failed to show.
        /// </summary>
        public event Action<DirichletError> ShowFailed;

        internal DirichletAd(IDirichletPlatformBridge bridge, DirichletPlatformAdHandle platformHandle, long spaceId)
        {
            this.bridge = bridge ?? throw new ArgumentNullException(nameof(bridge));
            PlatformHandle = platformHandle ?? throw new ArgumentNullException(nameof(platformHandle));
            this.spaceId = spaceId;
            if (!string.IsNullOrEmpty(PlatformHandle.DebugId))
            {
                DirichletAdEventRouter.Register(PlatformHandle.DebugId, this);
            }
        }

        internal IDirichletPlatformBridge Bridge => bridge;

        public virtual bool Show()
        {
            if (!IsLoaded)
            {
                Debug.LogWarning($"[Dirichlet] Attempted to show ad before load success. Slot={SlotId}");
            }

            var shown = ShowInternal(null);
            if (shown)
            {
                IsLoaded = false;
            }
            return shown;
        }

        public void Destroy()
        {
            if (!string.IsNullOrEmpty(PlatformHandle?.DebugId))
            {
                DirichletAdEventRouter.Unregister(PlatformHandle.DebugId);
            }
            DestroyInternal();
            IsLoaded = false;
        }

        internal void MarkLoaded()
        {
            IsLoaded = true;
        }

        protected abstract bool ShowInternal(DirichletAdShowOptions options);

        protected abstract void DestroyInternal();

        internal virtual void HandleNativeEvent(DirichletNativeEvent nativeEvent)
        {
            switch (nativeEvent.EventName)
            {
                case DirichletNativeEventNames.Show:
                    OnShown();
                    break;
                case DirichletNativeEventNames.Click:
                    OnClicked();
                    break;
                case DirichletNativeEventNames.Close:
                    OnClosed();
                    break;
                case DirichletNativeEventNames.ShowError:
                    OnShowFailed(CreateShowError(nativeEvent.Data));
                    break;
            }
        }

        protected virtual void OnShown()
        {
            Shown?.Invoke();
        }

        protected virtual void OnClicked()
        {
            Clicked?.Invoke();
        }

        protected virtual void OnClosed()
        {
            Closed?.Invoke();
        }

        protected virtual void OnShowFailed(DirichletError error)
        {
            ShowFailed?.Invoke(error ?? new DirichletError("show_error", "Ad failed to show"));
        }

        private static DirichletError CreateShowError(DirichletNativeEventData data)
        {
            var code = data != null && data.Code != 0 ? data.Code.ToString(CultureInfo.InvariantCulture) : "show_error";
            var message = data?.Message;
            return new DirichletError(code, string.IsNullOrEmpty(message) ? "Ad failed to show" : message);
        }
    }

    public abstract class DirichletRewardAdBase : DirichletAd
    {
        public event Action<DirichletRewardVerificationEventArgs> RewardVerified;
        private IDirichletRewardAdInteractionListener interactionListener;

        internal DirichletRewardAdBase(IDirichletPlatformBridge bridge, DirichletPlatformAdHandle handle, long spaceId)
            : base(bridge, handle, spaceId)
        {
        }

        public void SetInteractionListener(IDirichletRewardAdInteractionListener listener)
        {
            interactionListener = listener;
        }

        public void SetRewardAdInteractionListener(IDirichletRewardAdInteractionListener listener)
        {
            SetInteractionListener(listener);
        }

        protected override void OnShown()
        {
            base.OnShown();
            interactionListener?.OnAdShow();
        }

        protected override void OnClicked()
        {
            base.OnClicked();
            interactionListener?.OnAdClick();
        }

        protected override void OnClosed()
        {
            base.OnClosed();
            interactionListener?.OnAdClose();
        }

        internal override void HandleNativeEvent(DirichletNativeEvent nativeEvent)
        {
            base.HandleNativeEvent(nativeEvent);

            if (nativeEvent.EventName == DirichletNativeEventNames.Reward && nativeEvent.Data != null)
            {
                var data = nativeEvent.Data;
                var args = new DirichletRewardVerificationEventArgs(
                    data.RewardVerify,
                    data.RewardAmount,
                    data.RewardName,
                    data.Code,
                    data.Message);
                RewardVerified?.Invoke(args);
                interactionListener?.OnRewardVerify(args);
            }
        }

        protected override bool ShowInternal(DirichletAdShowOptions options)
        {
            return Bridge.ShowRewardVideoAd(PlatformHandle);
        }

        protected override void DestroyInternal()
        {
            Bridge.DestroyAd(PlatformHandle);
        }
    }

    public sealed class DirichletRewardVideoAd : DirichletRewardAdBase
    {
        private DirichletRewardVideoAd(IDirichletPlatformBridge bridge, DirichletPlatformAdHandle handle, long spaceId)
            : base(bridge, handle, spaceId)
        {
        }

        internal static DirichletRewardVideoAd Create(IDirichletPlatformBridge bridge, DirichletPlatformAdHandle handle, long spaceId)
        {
            return new DirichletRewardVideoAd(bridge, handle, spaceId);
        }
    }

    [Obsolete("Use DirichletRewardVideoAd instead.")]
    public sealed class DirichletRewardAd : DirichletRewardAdBase
    {
        internal DirichletRewardAd(IDirichletPlatformBridge bridge, DirichletPlatformAdHandle handle, long spaceId) : base(bridge, handle, spaceId)
        {
        }
    }

    public sealed class DirichletInterstitialAd : DirichletAd
    {
        private IDirichletInterstitialAdInteractionListener interactionListener;

        private DirichletInterstitialAd(IDirichletPlatformBridge bridge, DirichletPlatformAdHandle handle, long spaceId)
            : base(bridge, handle, spaceId)
        {
        }

        public void SetInteractionListener(IDirichletInterstitialAdInteractionListener listener)
        {
            interactionListener = listener;
        }

        protected override void OnShown()
        {
            base.OnShown();
            interactionListener?.OnAdShow();
        }

        protected override void OnClicked()
        {
            base.OnClicked();
            interactionListener?.OnAdClick();
        }

        protected override void OnClosed()
        {
            base.OnClosed();
            interactionListener?.OnAdClose();
        }

        protected override bool ShowInternal(DirichletAdShowOptions options)
        {
            return Bridge.ShowInterstitialAd(PlatformHandle);
        }

        protected override void DestroyInternal()
        {
            Bridge.DestroyAd(PlatformHandle);
        }

        internal static DirichletInterstitialAd Create(IDirichletPlatformBridge bridge, DirichletPlatformAdHandle handle, long spaceId)
        {
            return new DirichletInterstitialAd(bridge, handle, spaceId);
        }
    }

    public sealed class DirichletBannerAd : DirichletAd
    {
        private IDirichletBannerAdInteractionListener interactionListener;

        private DirichletBannerAd(IDirichletPlatformBridge bridge, DirichletPlatformAdHandle handle, long spaceId)
            : base(bridge, handle, spaceId)
        {
        }

        public void SetInteractionListener(IDirichletBannerAdInteractionListener listener)
        {
            interactionListener = listener;
        }

        public bool Show(DirichletAdShowOptions options)
        {
            var showOptions = options ?? new DirichletAdShowOptions();
            var shown = ShowInternal(showOptions);
            if (shown)
            {
                IsLoaded = false;
            }
            return shown;
        }

        public override bool Show()
        {
            return Show(null);
        }

        protected override void OnShown()
        {
            base.OnShown();
            interactionListener?.OnAdShow();
        }

        protected override void OnClicked()
        {
            base.OnClicked();
            interactionListener?.OnAdClick();
        }

        protected override void OnClosed()
        {
            base.OnClosed();
            interactionListener?.OnAdClose();
        }

        protected override bool ShowInternal(DirichletAdShowOptions options)
        {
            return Bridge.ShowBannerAd(PlatformHandle, options ?? new DirichletAdShowOptions());
        }

        protected override void DestroyInternal()
        {
            Bridge.DestroyAd(PlatformHandle);
        }

        internal static DirichletBannerAd Create(IDirichletPlatformBridge bridge, DirichletPlatformAdHandle handle, long spaceId)
        {
            return new DirichletBannerAd(bridge, handle, spaceId);
        }
    }

    public sealed class DirichletSplashAd : DirichletAd
    {
        private IDirichletSplashAdInteractionListener interactionListener;

        private DirichletSplashAd(IDirichletPlatformBridge bridge, DirichletPlatformAdHandle handle, long spaceId)
            : base(bridge, handle, spaceId)
        {
        }

        public void SetInteractionListener(IDirichletSplashAdInteractionListener listener)
        {
            interactionListener = listener;
        }

        protected override void OnShown()
        {
            base.OnShown();
            interactionListener?.OnAdShow();
        }

        protected override void OnClicked()
        {
            base.OnClicked();
            interactionListener?.OnAdClick();
        }

        protected override void OnClosed()
        {
            base.OnClosed();
            interactionListener?.OnAdClose();
        }

        protected override bool ShowInternal(DirichletAdShowOptions options)
        {
            return Bridge.ShowSplashAd(PlatformHandle, options ?? new DirichletAdShowOptions());
        }

        protected override void DestroyInternal()
        {
            Bridge.DestroyAd(PlatformHandle);
        }

        internal static DirichletSplashAd Create(IDirichletPlatformBridge bridge, DirichletPlatformAdHandle handle, long spaceId)
        {
            return new DirichletSplashAd(bridge, handle, spaceId);
        }
    }

    internal static class DirichletNativeEventNames
    {
        internal const string Show = "show";
        internal const string Click = "click";
        internal const string Close = "close";
        internal const string Reward = "reward";
        internal const string ShowError = "show_error";
    }

    internal readonly struct DirichletNativeEvent
    {
        public string EventName { get; }
        public string AdType { get; }
        public DirichletNativeEventData Data { get; }

        public DirichletNativeEvent(string eventName, string adType, DirichletNativeEventData data)
        {
            EventName = string.IsNullOrEmpty(eventName) ? string.Empty : eventName;
            AdType = string.IsNullOrEmpty(adType) ? string.Empty : adType;
            Data = data;
        }
    }

    internal sealed class DirichletNativeEventData
    {
        public bool RewardVerify { get; }
        public int RewardAmount { get; }
        public string RewardName { get; }
        public int Code { get; }
        public string Message { get; }

        public DirichletNativeEventData(bool rewardVerify, int rewardAmount, string rewardName, int code, string message)
        {
            RewardVerify = rewardVerify;
            RewardAmount = rewardAmount;
            RewardName = string.IsNullOrEmpty(rewardName) ? string.Empty : rewardName;
            Code = code;
            Message = message ?? string.Empty;
        }
    }

    public sealed class DirichletRewardVerificationEventArgs : EventArgs
    {
        public bool IsVerified { get; }
        public int RewardAmount { get; }
        public string RewardName { get; }
        public int Code { get; }
        public string Message { get; }

        internal DirichletRewardVerificationEventArgs(bool rewardVerified, int rewardAmount, string rewardName, int code, string message)
        {
            IsVerified = rewardVerified;
            RewardAmount = rewardAmount;
            RewardName = string.IsNullOrEmpty(rewardName) ? string.Empty : rewardName;
            Code = code;
            Message = message ?? string.Empty;
        }
    }

    public interface IDirichletRewardAdInteractionListener
    {
        void OnAdShow();
        void OnAdClick();
        void OnAdClose();
        void OnRewardVerify(DirichletRewardVerificationEventArgs args);
    }

    public interface IDirichletInterstitialAdInteractionListener
    {
        void OnAdShow();
        void OnAdClick();
        void OnAdClose();
    }

    public interface IDirichletBannerAdInteractionListener
    {
        void OnAdShow();
        void OnAdClick();
        void OnAdClose();
    }

    public interface IDirichletSplashAdInteractionListener
    {
        void OnAdShow();
        void OnAdClick();
        void OnAdClose();
    }

    /// <summary>
    /// Listener interface for auto interstitial ad callbacks.
    /// Android only - iOS will receive OnError with not_supported error.
    /// </summary>
    public interface IDirichletInterstitialAutoAdListener
    {
        void OnError(DirichletError error);
        void OnAdShow();
        void OnAdClose();
        void OnAdClick();
    }

    /// <summary>
    /// Listener interface for auto banner ad callbacks.
    /// Android only - iOS will receive OnError with not_supported error.
    /// </summary>
    public interface IDirichletBannerAutoAdListener
    {
        void OnError(DirichletError error);
        void OnAdShow();
        void OnAdClose();
        void OnAdClick();
    }

    /// <summary>
    /// Listener interface for auto splash ad callbacks.
    /// Android only - iOS will receive OnError with not_supported error.
    /// </summary>
    public interface IDirichletSplashAutoAdListener
    {
        void OnError(DirichletError error);
        void OnAdShow();
        void OnAdClose();
        void OnAdClick();
    }

    /// <summary>
    /// Listener interface for auto reward video ad callbacks.
    /// Used with ShowRewardVideoAutoAd which combines load and show into one operation.
    /// Android only - iOS will receive OnError with not_supported error.
    /// </summary>
    public interface IDirichletRewardVideoAutoAdListener
    {
        /// <summary>
        /// Called when ad fails to load or show.
        /// </summary>
        void OnError(DirichletError error);

        /// <summary>
        /// Called when ad is shown to the user.
        /// </summary>
        void OnAdShow();

        /// <summary>
        /// Called when ad is closed.
        /// </summary>
        void OnAdClose();

        /// <summary>
        /// Called when reward verification is completed.
        /// </summary>
        void OnRewardVerify(DirichletRewardVerificationEventArgs args);

        /// <summary>
        /// Called when ad is clicked.
        /// </summary>
        void OnAdClick();
    }

    internal static class DirichletAdEventRouter
    {
        private const string CallbackObjectName = "DirichletMediationEventReceiver";
        private static readonly Dictionary<string, WeakReference> Ads = new Dictionary<string, WeakReference>(StringComparer.Ordinal);
        private static bool receiverInitialized;

        internal static void Register(string handleId, DirichletAd ad)
        {
            if (string.IsNullOrEmpty(handleId) || ad == null)
            {
                return;
            }

            EnsureReceiver();
            Ads[handleId] = new WeakReference(ad);
        }

        internal static void Unregister(string handleId)
        {
            if (string.IsNullOrEmpty(handleId))
            {
                return;
            }

            Ads.Remove(handleId);
        }

        private static void EnsureReceiver()
        {
            if (receiverInitialized)
            {
                return;
            }

            if (!DirichletSdk.IsUnityThread)
            {
                DirichletSdk.DispatchToUnityThread(EnsureReceiver);
                return;
            }

            var existing = GameObject.Find(CallbackObjectName);
            if (existing == null)
            {
                var host = new GameObject(CallbackObjectName)
                {
                    hideFlags = HideFlags.HideAndDontSave
                };
                UnityEngine.Object.DontDestroyOnLoad(host);
                host.AddComponent<DirichletAdEventReceiver>();
            }

            receiverInitialized = true;
        }

        internal static void HandleNativeEvent(string payload)
        {
            if (string.IsNullOrEmpty(payload))
            {
                return;
            }

            NativeEventPayload message;
            try
            {
                message = JsonUtility.FromJson<NativeEventPayload>(payload);
            }
            catch (Exception ex)
            {
                Debug.LogWarning($"[Dirichlet] Failed to parse native ad event: {ex.Message}\n{payload}");
                return;
            }

            if (message == null || string.IsNullOrEmpty(message.handle) || string.IsNullOrEmpty(message.eventName))
            {
                return;
            }

            if (!Ads.TryGetValue(message.handle, out var weakReference))
            {
                return;
            }

            if (!(weakReference.Target is DirichletAd ad) || ad == null)
            {
                Ads.Remove(message.handle);
                return;
            }

            var data = message.data != null
                ? new DirichletNativeEventData(
                    message.data.rewardVerify,
                    message.data.rewardAmount,
                    message.data.rewardName,
                    message.data.code,
                    message.data.message)
                : null;

            var nativeEvent = new DirichletNativeEvent(message.eventName, message.adType, data);

            if (DirichletSdk.IsUnityThread)
            {
                ad.HandleNativeEvent(nativeEvent);
            }
            else
            {
                DirichletSdk.DispatchToUnityThread(() => ad.HandleNativeEvent(nativeEvent));
            }
        }

        [Serializable]
        private class NativeEventPayload
        {
            public string handle;
            public string eventName;
            public string adType;
            public NativeEventPayloadData data;
        }

        [Serializable]
        private class NativeEventPayloadData
        {
            public bool rewardVerify;
            public int rewardAmount;
            public string rewardName;
            public int code;
            public string message;
        }

        private sealed class DirichletAdEventReceiver : MonoBehaviour
        {
            public void OnNativeEvent(string payload)
            {
                HandleNativeEvent(payload);
            }
        }
    }
}
