using System;
using System.Threading.Tasks;
using UnityEngine;

namespace IndieExchange
{
    /// <summary>Call on Unity's main thread. No paid-ad SDK is required or called.</summary>
    public static class IndieAds
    {
        internal static IndieAdsRunner Runner;
        public static event Action<string> InterstitialOpened;
        public static event Action<string> InterstitialClosed;
        public static event Action<string, IndiePlacementStats> StatisticsChanged;
        public static bool IsInitialized => Runner != null;
        public static bool IsConfigurationReady => Runner != null && Runner.ConfigurationReady;

        public static bool Initialize(string gameId, bool testMode = false)
        {
            var settings = ScriptableObject.CreateInstance<IndieAdsSettings>();
            settings.gameId = gameId; settings.testMode = testMode;
            var result = Initialize(settings);
            UnityEngine.Object.Destroy(settings);
            return result;
        }
        public static Task<bool> TryShowInterstitialAsync() => TryShowInterstitialAsync("interstitial");
        public static bool TryShowBanner() => TryShowBanner("banner");
        public static Task<bool> TryShowRewardedAsync() => Task.FromResult(false);
        public static bool IsReady(IndieAdFormat format) => IsReady(FormatId(format));
        public static void Preload(IndieAdFormat format) => Preload(FormatId(format));
        public static IndiePlacementStats GetStatistics(IndieAdFormat format) => GetStatistics(FormatId(format));
        private static string FormatId(IndieAdFormat format) => format == IndieAdFormat.Banner ? "banner" : format == IndieAdFormat.Interstitial ? "interstitial" : "rewarded";
        public static bool IsShowingInterstitial => Runner != null && Runner.IsShowing;

        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
        private static void ResetStatics()
        {
            Runner = null;
            IndieAdsRunner.Launches.Clear();
            InterstitialOpened = null;
            InterstitialClosed = null;
            StatisticsChanged = null;
        }

        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
        private static void AutoStart()
        {
            var settings = Resources.Load<IndieAdsSettings>("IndieAdsSettings");
            if (settings != null && settings.autoInitialize) Initialize(settings);
        }

        /// <summary>Call once after the host's consent/age flow permits ad requests. Starts background preloads.</summary>
        public static bool Initialize(IndieAdsSettings settings = null)
        {
            if (Runner != null) return true;
            settings = settings != null ? settings : Resources.Load<IndieAdsSettings>("IndieAdsSettings");
            if (settings == null) { Debug.LogWarning("Indie Ads: create settings through Tools > Indie Exchange > Setup."); return false; }
            var go = new GameObject("Indie Exchange");
            UnityEngine.Object.DontDestroyOnLoad(go);
            Runner = go.AddComponent<IndieAdsRunner>();
            if (Runner.Configure(settings)) return true;
            Shutdown();
            return false;
        }

        public static bool IsReady(string placement) => Runner != null && Runner.IsReady(placement);

        /// <summary>Preload without counting an opportunity. Calls are coalesced and retries are bounded.</summary>
        public static void Preload(string placement) { if (Runner != null) Runner.Preload(placement); }

        /// <summary>
        /// Counts one eligible opportunity. False completes immediately without network work.
        /// True completes after a displayed ad closes. Repeated calls while an interstitial is active
        /// share its task and do not count again or trigger a second ad. Await before resuming gameplay.
        /// </summary>
        public static Task<bool> TryShowInterstitialAsync(string placement, bool bypassPercentage = false)
            => Runner != null ? Runner.TryShow(placement, bypassPercentage) : Task.FromResult(false);

        /// <summary>Shows a preloaded banner. Keep the host's banner hidden until this returns false.</summary>
        public static bool TryShowBanner(string placement, bool bypassPercentage = false)
            => Runner != null && Runner.TryBanner(placement, bypassPercentage);

        public static void HideBanner() { if (Runner != null) Runner.HideBanner(); }

        /// <summary>Rewarded exchange delivery is not supported by the current backend. Never grants a reward.</summary>
        public static Task<bool> TryShowRewardedAsync(string placement) => Task.FromResult(false);

        public static IndiePlacementStats GetStatistics(string placement)
            => Runner != null ? Runner.GetStatistics(placement) : new IndiePlacementStats();

        public static void Shutdown()
        {
            if (Runner == null) return;
            var runner = Runner;
            Runner = null;
            runner.Stop();
            UnityEngine.Object.Destroy(runner.gameObject);
        }

        internal static void Opened(string placement) => Safe(() => InterstitialOpened?.Invoke(placement));
        internal static void Closed(string placement) => Safe(() => InterstitialClosed?.Invoke(placement));
        internal static void Stats(string placement, IndiePlacementStats stats) => Safe(() => StatisticsChanged?.Invoke(placement, stats.Copy()));
        private static void Safe(Action callback)
        {
            try { callback(); } catch (Exception ex) { Debug.LogException(ex); }
        }
    }
}

