using System;
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;

namespace FRVR
{
    public class FRVRSDKEditor : MonoBehaviour
    {
#if UNITY_EDITOR
        static FRVRSDKEditor instance;
        const float callbackDelay = 1f;

        [Tooltip("Should game be inactivated while ad is showing?")]
        public bool inactivateGameWhileAd;

        [Tooltip("Should the finish ad button be shown?  For faster development.")]
        public bool finishAdEnabled;

        [Tooltip("Should always throw ad errors for testing?")]
        public bool alwaysThrowAdError;

        [Tooltip("Should disable ad previews in editor?")]
        public bool disableAdPreviews;


        void Awake()
        {
            instance = this;
            DontDestroyOnLoad(this);
        }

        private static void Message(string msg)
        {
            Debug.Log("[FRVRSDK Editor] " + msg);
        }

        public static void ShowInterstitialAd(Action adStarted, Action<SdkError> adError, Action adFinished)
        {
            if (showAdTimeLeft > 0)
            {
                Message("ERROR: Ad already showing");
                adError?.Invoke(new SdkError { error = "Ad already showing" });
                return;
            }

            if (instance && instance.alwaysThrowAdError)
            {
                Message("Intentionally throwing ad error due to test configuration");
                adError?.Invoke(new SdkError { error = "Test configuration error" });
                return;
            }

            Message("ShowInterstitialAd starting");
            adStarted?.Invoke();

            ShowAd("Interstitial ad", () =>
            {
                Message("ShowInterstitialAd done");
                adFinished?.Invoke();
            },
            () =>
            {
                Message("ShowInterstitialAd skipped");
                adError?.Invoke(new SdkError { error = "Ad skipped" });
            });
        }

        public static void ShowRewardAd(Action adStarted, Action<SdkError> adError, Action adFinished)
        {
            if (showAdTimeLeft > 0)
            {
                Message("ERROR: Ad already showing");
                adError?.Invoke(new SdkError { error = "Ad already showing" });
                return;
            }

            if (instance && instance.alwaysThrowAdError)
            {
                Message("Intentionally throwing ad error due to test configuration");
                adError?.Invoke(new SdkError { error = "Test configuration error" });
                return;
            }

            Message("ShowRewardAd starting");
            adStarted?.Invoke();

            ShowAd("Reward ad", () =>
            {
                Message("ShowRewardAd done");
                adFinished?.Invoke();
            },
            () =>
            {
                Message("ShowRewardAd skipped");
                adError?.Invoke(new SdkError { error = "Ad skipped" });
            });
        }


        public static void SetLocalJson(string json, Action<int> callback)
        {
            Message("SetLocalJson: " + json);
            PlayerPrefs.SetString("FRVRSDKLocalJson", json);
            callback(1);
        }

        public static void GetLocalJson(Action<string> callback)
        {
            Message("GetLocalJson");
            CallDelayed(callbackDelay, () =>
            {
                string json = PlayerPrefs.GetString("FRVRSDKLocalJson", "{}");
                callback(json);
            });
        }

        public static void LogFTUE(int stepNumber, string stepName)
        {
            Message("LogFTUE: " + stepNumber + ", " + stepName);
        }

        public static void LogLevelStart(string levelId)
        {
            Message("LogLevelStart: " + levelId);
        }

        public static void LogLevelEnd(string levelId)
        {
            Message("LogLevelEnd: " + levelId);
        }

        public static void LogEvent(string eventName, string jsonDataString)
        {
            Message("LogEvent: " + eventName + ", " + jsonDataString);
        }

        public static void Share(string text, string image, string headline, string cta, Action<int> callback)
        {
            Message("Share starting: " + text + ", " + image.Length * sizeof(char) + " bytes, " + headline + ", " + cta);

            CallDelayed(callbackDelay, () =>
            {
                Message("Share done: " + text);
                callback(1);
            });
        }

        public static bool IapIsReady()
        {
            Message("IapIsReady");
            return true;
        }

        public static string IapGetCatalog()
        {
            Message("IapGetCatalog");

            var catalog = @"{
                'thanksgiving_bundle': {
                    'label': 'Thanksgiving bundle',
                    'price': '$1.99',
                    'priceValue': '1.99',
                    'currencyCode': 'USD',
                    'storeId': 'thanksgiving_bundle',
                    'trackingName': 'Thanksgiving bundle'
                },
                'halloween_bundle': {
                    'label': 'Halloween bundle',
                    'price': '$1.99',
                    'priceValue': '1.99',
                    'currencyCode': 'USD',
                    'storeId': 'halloween_bundle',
                    'trackingName': 'Halloween bundle'
                }
            }";

            return catalog.Replace("'", "\"");
        }

        public static string IapGetProduct(string productId)
        {
            Message("IapGetProduct: " + productId);

            var product = @"{
                'id': 'thanksgiving_bundle',
                'price': '$1.99',
                'priceValue': '1.99',
                'currencyCode': 'USD',
                'storeId': 'thanksgiving_bundle',
                'trackingName': 'Thanksgiving bundle'
            }";

            return product.Replace("'", "\"");
        }

        public static void IapPurchase(string productId, Action<int> callback)
        {
            Message("IapPurchase starting: " + productId);

            CallDelayed(callbackDelay, () =>
            {
                Message("IapPurchase done: " + productId);
                callback(1);
            });
        }

        public static void IapConsumePurchase(string purchaseJson, Action<string> callback)
        {
            Message("IapConsumePurchase starting: " + purchaseJson);

            CallDelayed(callbackDelay, () =>
            {
                Message("IapConsumePurchase done: " + purchaseJson);
                callback("testPurchaseId");
            });
        }

        public static void IapGetUnconsumedPurchases(Action<string> callback)
        {
            Message("IapGetUnconsumedPurchases starting");

            CallDelayed(callbackDelay, () =>
            {
                Message("IapGetUnconsumedPurchases done");
                string result = "[{\"productId\":\"testProductId\",\"purchaseId\":\"testPurchaseId\"}]";
                callback(result);
            });
        }

        public static void GetFeature(string featureId, Action<string> callback)
        {
            Message("GetFeature: " + featureId);
            CallDelayed(callbackDelay, () =>
            {
                string result = "{\"enabled\":true,\"featureId\":\"" + featureId + "\"}";
                callback(result);
            });
        }

        public static string AuthGetFRVRID()
        {
            Message("AuthGetFRVRID");
            return "00000000-0000-0000-0000-000000000000";
        }

        public static string AuthGetAccessToken()
        {
            Message("AuthGetAccessToken");
            return "editor_simulated_token";
        }

        public static bool AuthIsLoggedIn()
        {
            Message("AuthIsLoggedIn");
            return true;
        }

        // Tournament module editor simulations
        public static bool TournamentIsSupported()
        {
            Message("TournamentIsSupported");
            return true;
        }

        public static bool TournamentIsSupportedAPI(string api)
        {
            Message("TournamentIsSupportedAPI: " + api);
            return true;
        }

        public static void TournamentGetCurrentTournament(Action<string> onSuccess, Action<string> onError)
        {
            Message("TournamentGetCurrentTournament");
            CallDelayed(callbackDelay, () =>
            {
                string result = "{\"id\":\"editor-tournament-001\",\"contextID\":\"editor-context-001\",\"title\":\"Editor Test Tournament\",\"startTime\":1774883639834,\"endTime\":1775488437000}";
                onSuccess(result);
            });
        }

        public static void TournamentPostScore(string tournamentId, int score, Action onSuccess, Action<string> onError)
        {
            Message("TournamentPostScore: tournamentId=" + tournamentId + ", score=" + score);
            CallDelayed(callbackDelay, () =>
            {
                Message("TournamentPostScore done");
                onSuccess();
            });
        }

        public static void TournamentCreate(string payloadJson, string title, string sortOrder, long endTime, Action<string> onSuccess, Action<string> onError)
        {
            Message("TournamentCreate: title=" + title + ", sortOrder=" + sortOrder + ", endTime=" + endTime);
            CallDelayed(callbackDelay, () =>
            {
                string tournamentId = "editor-tournament-" + UnityEngine.Random.Range(1000, 9999);
                Message("TournamentCreate done: " + tournamentId);
                onSuccess(tournamentId);
            });
        }

        public static void TournamentGetMyTournaments(Action<string> onSuccess, Action<string> onError)
        {
            Message("TournamentGetMyTournaments");
            CallDelayed(callbackDelay, () =>
            {
                string result = "{\"tournaments\":[{\"id\":\"editor-tournament-001\",\"created\":1774883639834,\"startTime\":1774883639834,\"endTime\":1775488437000},{\"id\":\"editor-tournament-002\",\"created\":1774881981318,\"startTime\":1774881981318,\"endTime\":1775486779000}]}";
                onSuccess(result);
            });
        }

        public static void TournamentGetActiveTournaments(Action<string> onSuccess, Action<string> onError)
        {
            Message("TournamentGetActiveTournaments");
            CallDelayed(callbackDelay, () =>
            {
                string result = "{\"tournaments\":[{\"id\":\"editor-tournament-001\",\"contextID\":\"editor-context-001\",\"title\":\"Editor Tournament\",\"startTime\":1774901612973,\"endTime\":1775488437000,\"type\":\"TOURNAMENT\",\"platformType\":\"DEFAULT\",\"payload\":\"{}\",\"offset\":0,\"count\":0,\"players\":[]}]}";
                onSuccess(result);
            });
        }

        public static void TournamentCreateOrPostScore(string payloadJson, string title, string sortOrder, long endTime, int score, Action<string> onSuccess, Action<string> onError)
        {
            Message("TournamentCreateOrPostScore: title=" + title + ", score=" + score);
            CallDelayed(callbackDelay, () =>
            {
                string tournamentId = "editor-tournament-" + UnityEngine.Random.Range(1000, 9999);
                string result = "{\"action\":\"created\",\"tournamentId\":\"" + tournamentId + "\"}";
                Message("TournamentCreateOrPostScore done: " + result);
                onSuccess(result);
            });
        }

        private static void CallDelayed(float delaySeconds, Action callback)
        {
            if (instance == null)
            {
                // If we called before our Awake, we need to find the instance
                instance = FindFirstObjectByType<FRVRSDKEditor>();
                if (instance == null)
                {
                    Debug.LogError("FRVRSDKEditor: This mono behaviour needs be attached to a game object in the starting scene.");
                    return;
                }
            }

            instance.StartCoroutine(_CallDelayed(delaySeconds, callback));
        }

        private static IEnumerator _CallDelayed(float delaySeconds, Action callback)
        {
            yield return new WaitForSeconds(delaySeconds);
            callback?.Invoke();
        }

        private static string adTitle;
        private static Action onAdWatched;
        private static Action onAdSkipped;
        private static float showAdTimeLeft;

        private static GameObject eventSystemGameObject;

        public static void ShowAd(string _adTitle, Action _onAdWatched, Action _onAdSkipped)
        {
            if (instance && instance.disableAdPreviews)
            {
                Message("Ad preview disabled, simulating success");
                _onAdWatched?.Invoke();
                return;
            }

            adTitle = _adTitle;
            onAdWatched = _onAdWatched;
            onAdSkipped = _onAdSkipped;

            showAdTimeLeft = 5f;

            if (instance && instance.inactivateGameWhileAd)
            {
                // Pause game
                Time.timeScale = 0;

                // Disable eventsystem to prevent clicks in ui etc
                eventSystemGameObject = FindFirstObjectByType<EventSystem>()?.gameObject;
                eventSystemGameObject?.SetActive(false);
            }
        }

        void Update()
        {
            if (showAdTimeLeft <= 0) return;

            // Decrease time left 
            // Skip if unscaled delta time is too high as it often is when you've just pressed play
            if (Time.unscaledDeltaTime < .1f)
            {
                showAdTimeLeft -= Time.unscaledDeltaTime;
            }

            if (showAdTimeLeft <= 0)
            {
                CloseAd(onAdWatched);
            }
        }

        void OnGUI()
        {
            if (showAdTimeLeft <= 0) return;

            var margin = Screen.width * 0.01f;
            var width = Screen.width - margin * 2;
            var height = Screen.height - margin * 2;

            GUI.backgroundColor = new Color(0, 0, 0, 0.9f);

            GUILayout.BeginArea(new Rect(margin, margin, width, height));

            // Background (multiple layers to create a darker effect)
            GUI.Box(new Rect(0, 0, width, height), "");
            GUI.Box(new Rect(0, 0, width, height), "");
            GUI.Box(new Rect(0, 0, width, height), "");
            GUI.Box(new Rect(0, 0, width, height), "");

            GUILayout.Space(10); // Add vertical spacing

            // TITLE
            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            GUILayout.Label("FRVR SDK  ---  " + adTitle, GUILayout.Height(50));
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();

            GUILayout.FlexibleSpace(); // vertical space

            // Time left
            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            GUILayout.Label("Ad time left: " + Mathf.Ceil(showAdTimeLeft), GUILayout.Height(50));
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();

            GUILayout.FlexibleSpace(); // vertical space

            // Help text
            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            GUILayout.Label("Ad is showing.  Make sure your game is paused and muted.", GUILayout.ExpandWidth(true), GUILayout.Height(50));
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();

            GUILayout.FlexibleSpace(); // vertical space

            // BUTTONS
            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace(); // Add flexible space to center the buttons

            if (onAdSkipped != null)
            {
                if (GUILayout.Button("Cancel watch ad", GUILayout.Width(150), GUILayout.Height(50)))
                {
                    CloseAd(onAdSkipped);
                }

                GUILayout.FlexibleSpace();
            }

            if (instance && instance.finishAdEnabled)
            {
                if (GUILayout.Button("Finish watch ad", GUILayout.Width(150), GUILayout.Height(50)))
                {
                    CloseAd(onAdWatched);
                }

                GUILayout.FlexibleSpace();
            }

            GUILayout.EndHorizontal();

            GUILayout.Space(10); // Add vertical spacing

            GUILayout.EndArea();
        }

        static void CloseAd(Action callBack)
        {
            if (instance && instance.inactivateGameWhileAd)
            {
                eventSystemGameObject?.SetActive(true);
                Time.timeScale = 1;
            }

            onAdSkipped = null;
            onAdWatched = null;
            eventSystemGameObject = null;
            adTitle = null;
            showAdTimeLeft = 0;

            callBack?.Invoke();
        }

#endif
    }
}


