using System;

namespace Davanci.Nexus
{
    // Ads seams for NexusAds, mirroring the IAP provider seam. The mediation layer (AppLovin
    // MAX) sits behind INexusAdsProvider so the whole rewarded / interstitial / banner flow can
    // be exercised in the editor with the FakeAdsProvider double — no SDK, no device, no build.

    public enum NexusAdFormat
    {
        Rewarded,
        Interstitial,
        Banner,      // in-game banner / MREC overlay
    }

    public enum NexusAdStatus
    {
        Rewarded,        // rewarded ad fully watched, reward earned
        Shown,           // interstitial / banner displayed
        Dismissed,       // closed before earning the reward
        NoFill,          // no ad available to load
        NotReady,        // asked to show before an ad finished loading
        Failed,          // display / network error
        NotInitialized,  // ads not ready yet
    }

    // The outcome of a show call. Rewarded is only ever true for a fully-watched rewarded ad —
    // grant the reward solely on that flag.
    public struct NexusAdResult
    {
        public bool Rewarded;
        public bool Shown;
        public NexusAdStatus Status;
        public string Placement;   // analytics label the game passed in
        public string Message;

        public static NexusAdResult Make(NexusAdStatus status, string placement, string message = "")
            => new NexusAdResult
            {
                Rewarded = status == NexusAdStatus.Rewarded,
                // Shown means an ad actually displayed. A Dismissed result (early close, or a
                // suppressed ad after "remove ads") was NOT a completed impression, so it's false.
                Shown = status == NexusAdStatus.Rewarded || status == NexusAdStatus.Shown,
                Status = status,
                Placement = placement,
                Message = message,
            };
    }

    // Resolved per-platform settings the core hands the provider at init time. Keeps the
    // provider free of NexusConfig / platform branching.
    public struct NexusAdsSettings
    {
        public string SdkKey;   // AppLovin MAX
        public string AppKey;   // LevelPlay / ironSource
        public string RewardedAdUnitId;
        public string InterstitialAdUnitId;
        public string BannerAdUnitId;
        public bool TestMode;
        // LevelPlay only: bring the SDK up in Test-Suite mode (sets the is_test_suite metadata
        // and skips loading real ads). MUST be off for normal serving — with it on, ironSource
        // puts the SDK in debug mode and every production load returns 509 "No fill".
        public bool TestSuiteMode;
        // iOS: set when requestATT ran and resolved. null = ATT not used (leave LevelPlay defaults).
        public bool? AttTrackingAuthorized;
    }

    // The mediation slot. Implementations: AppLovin MAX (NexusAdsProvider) and the editor/test fake.
    public interface INexusAdsProvider
    {
        bool IsInitialized { get; }
        void Initialize(NexusAdsSettings settings, Action<bool> onReady);

        bool IsRewardedReady { get; }
        void ShowRewarded(string placement, Action<NexusAdResult> onResult);

        bool IsInterstitialReady { get; }
        void ShowInterstitial(string placement, Action<NexusAdResult> onResult);

        void ShowBanner(string placement);
        void HideBanner();

        // Opens the mediation's on-device debugger / test suite (LevelPlay Test Suite, MAX
        // Mediation Debugger) for diagnosing per-network status. No-op in the editor fake.
        void LaunchTestSuite();
    }

    // Editor / test double: instant, deterministic, no SDK. Renders nothing itself — the sample
    // UI raises the popups — but drives the exact same call/callback flow a shipping game uses,
    // so rewarded grants, interstitials and banner show/hide are all testable in play mode.
    public sealed class FakeAdsProvider : INexusAdsProvider
    {
        public bool IsInitialized { get; private set; }
        public bool InitShouldSucceed { get; set; } = true;
        public bool RewardedAvailable { get; set; } = true;
        public bool InterstitialAvailable { get; set; } = true;
        public bool GrantRewardOnShow { get; set; } = true;   // false simulates an early dismiss
        public bool BannerVisible { get; private set; }
        public int RewardedShowCount { get; private set; }
        public int InterstitialShowCount { get; private set; }

        public void Initialize(NexusAdsSettings settings, Action<bool> onReady)
        {
            IsInitialized = InitShouldSucceed;
            onReady?.Invoke(IsInitialized);
        }

        public bool IsRewardedReady => IsInitialized && RewardedAvailable;

        public void ShowRewarded(string placement, Action<NexusAdResult> onResult)
        {
            RewardedShowCount++;
            if (!IsInitialized) { onResult?.Invoke(NexusAdResult.Make(NexusAdStatus.NotInitialized, placement)); return; }
            if (!RewardedAvailable) { onResult?.Invoke(NexusAdResult.Make(NexusAdStatus.NotReady, placement)); return; }
            onResult?.Invoke(NexusAdResult.Make(
                GrantRewardOnShow ? NexusAdStatus.Rewarded : NexusAdStatus.Dismissed, placement));
        }

        public bool IsInterstitialReady => IsInitialized && InterstitialAvailable;

        public void ShowInterstitial(string placement, Action<NexusAdResult> onResult)
        {
            InterstitialShowCount++;
            if (!IsInitialized) { onResult?.Invoke(NexusAdResult.Make(NexusAdStatus.NotInitialized, placement)); return; }
            if (!InterstitialAvailable) { onResult?.Invoke(NexusAdResult.Make(NexusAdStatus.NotReady, placement)); return; }
            onResult?.Invoke(NexusAdResult.Make(NexusAdStatus.Shown, placement));
        }

        public void ShowBanner(string placement) => BannerVisible = IsInitialized;
        public void HideBanner() => BannerVisible = false;
        public void LaunchTestSuite() => UnityEngine.Debug.Log("[Nexus] Ad test suite is a no-op in the editor; build to a device.");
    }
}
