using System;
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem.UI;
#endif

namespace Davanci.Nexus
{
    // Simple "Do you like the game?" prompt. Tapping Yes asks the OS for its native in-app
    // review sheet (SKStoreReviewController on iOS via NexusReview) — never a write-review web
    // link. Tapping No just says thanks and closes. Nothing is sent anywhere.
    [DisallowMultipleComponent]
    public class NexusReviewPopup : MonoBehaviour
    {
        public const string ResourcePath = "NexusReviewPopup";
        const string KeyRating = "Davanci.Nexus.Review.Rating";
        const float PopDuration = 0.30f;
        const float PopOutDuration = 0.22f;
        const float StageSwap = 0.15f;
        const float BackdropAlpha = 0.6f;
        const float PartStagger = 0.06f;
        const float PartDuration = 0.30f;

        [Header("Animation Roots")]
        public CanvasGroup root;
        public CanvasGroup backdrop;
        public RectTransform panel;

        [Header("Stages")]
        public GameObject askStage;
        public GameObject thanksStage;

        [Header("Buttons")]
        public Button yesButton;
        public Button noButton;
        public Button closeButton;
        public Button backdropButton;

        [Header("Text")]
        public TMP_Text titleLabel;
        public TMP_Text subtitleLabel;
        public TMP_Text thanksLabel;

        [Header("Theme")]
        public Image characterImage;

        [Header("Sequence")]
        public Transform[] popParts;

        [Header("Copy")]
        public string title = "Do you like the game?";
        public string subtitle = "";
        public string thanksYes = "Thanks a lot!";
        public string thanksNo = "Thanks for the feedback!";

        int _result; // 1 = liked (Yes), 0 = declined / dismissed
        bool _closing;
        Action<int> _onClosed;

        // The player tapped "Yes" once and the native review sheet was requested. Gates repeat prompts.
        public static bool HasRated => PlayerPrefs.GetInt(KeyRating, 0) > 0;
        public static int LastRating => PlayerPrefs.GetInt(KeyRating, 0);

        // Clears the saved state so the prompt can appear again (e.g. after an update, or for testing).
        public static void ResetRating()
        {
            PlayerPrefs.DeleteKey(KeyRating);
            PlayerPrefs.Save();
        }

        static NexusReviewPopup Show(Action<int> onClosed = null)
        {
            var prefab = Resources.Load<NexusReviewPopup>(ResourcePath);
            if (prefab == null)
            {
                Debug.LogWarning("[Nexus] NexusReviewPopup prefab missing. Run Davanci Nexus → Create Review Popup Prefab.");
                onClosed?.Invoke(0);
                return null;
            }
            EnsureEventSystem();
            var inst = Instantiate(prefab);
            inst.name = prefab.name;
            inst._onClosed = onClosed;
            return inst;
        }

        // Shows only until the player has tapped "Yes" once. Call it from as many places as you like.
        // The callback receives 1 if the player tapped Yes, 0 otherwise.
        public static bool TryShow(Action<int> onClosed = null)
        {
            if (HasRated) { onClosed?.Invoke(LastRating); return false; }
            return Show(onClosed) != null;
        }

        void Awake()
        {
            if (root) root.alpha = 0f;
            if (backdrop) backdrop.alpha = 0f;
            if (panel) panel.localScale = Vector3.one * 0.9f;
            if (popParts != null) foreach (var p in popParts) if (p) p.localScale = Vector3.zero;

            if (titleLabel && !string.IsNullOrEmpty(title)) titleLabel.text = title;
            if (subtitleLabel && !string.IsNullOrEmpty(subtitle)) subtitleLabel.text = subtitle;
            if (characterImage && !characterImage.sprite) characterImage.gameObject.SetActive(false);

            if (askStage) askStage.SetActive(true);
            if (thanksStage) thanksStage.SetActive(false);

            if (yesButton) yesButton.onClick.AddListener(OnYes);
            if (noButton) noButton.onClick.AddListener(OnNo);
            if (closeButton) closeButton.onClick.AddListener(Close);
            if (backdropButton) backdropButton.onClick.AddListener(Close);
        }

        void OnEnable()
        {
            StartCoroutine(PopIn());
            if (popParts != null)
                for (int i = 0; i < popParts.Length; i++)
                    if (popParts[i]) StartCoroutine(PopPart(popParts[i], i * PartStagger));
        }

        // "Yes": persist that we asked, then request the OS-native in-app review sheet
        // (Device.RequestStoreReview() on iOS). We never open the write-review web page here.
        void OnYes()
        {
            if (_closing) return;
            _result = 1;
            PlayerPrefs.SetInt(KeyRating, 1);
            PlayerPrefs.Save();
            DavanciNexus.Custom("review_rating", ("liked", "1"));
            NexusReview.RequestNow();
            StartCoroutine(ThanksThenClose(thanksYes));
        }

        // "No": acknowledge and close. Not persisted, so a later call site can still ask again.
        void OnNo()
        {
            if (_closing) return;
            _result = 0;
            DavanciNexus.Custom("review_rating", ("liked", "0"));
            StartCoroutine(ThanksThenClose(thanksNo));
        }

        IEnumerator ThanksThenClose(string message)
        {
            if (thanksLabel) thanksLabel.text = message;
            yield return SwapStage(askStage, thanksStage);
            yield return new WaitForSecondsRealtime(1.1f);
            Close();
        }

        public void Close()
        {
            if (_closing) return;
            _closing = true;
            StartCoroutine(PopOut());
        }

        IEnumerator PopIn()
        {
            float t = 0f;
            while (t < PopDuration)
            {
                t += Time.unscaledDeltaTime;
                float k = Mathf.Clamp01(t / PopDuration);
                if (panel) panel.localScale = Vector3.one * Mathf.LerpUnclamped(0.9f, 1f, EaseOutBack(k));
                if (root) root.alpha = EaseOutCubic(k);
                if (backdrop) backdrop.alpha = BackdropAlpha * EaseOutCubic(k);
                yield return null;
            }
            if (panel) panel.localScale = Vector3.one;
            if (root) root.alpha = 1f;
            if (backdrop) backdrop.alpha = BackdropAlpha;
        }

        IEnumerator PopPart(Transform part, float delay)
        {
            if (delay > 0f) yield return new WaitForSecondsRealtime(delay);
            float t = 0f;
            while (t < PartDuration)
            {
                t += Time.unscaledDeltaTime;
                part.localScale = Vector3.one * EaseOutBack(t / PartDuration);
                yield return null;
            }
            part.localScale = Vector3.one;
        }

        IEnumerator PopOut()
        {
            float t = 0f;
            float a0 = root ? root.alpha : 1f, d0 = backdrop ? backdrop.alpha : BackdropAlpha;
            while (t < PopOutDuration)
            {
                t += Time.unscaledDeltaTime;
                float k = Mathf.Clamp01(t / PopOutDuration);
                if (panel) panel.localScale = Vector3.one * Mathf.Lerp(1f, 0.9f, EaseInCubic(k));
                if (root) root.alpha = Mathf.Lerp(a0, 0f, k);
                if (backdrop) backdrop.alpha = Mathf.Lerp(d0, 0f, k);
                yield return null;
            }
            var cb = _onClosed; _onClosed = null;
            cb?.Invoke(_result);
            Destroy(gameObject);
        }

        IEnumerator SwapStage(GameObject hide, GameObject show)
        {
            float t = 0f;
            while (t < StageSwap)
            {
                t += Time.unscaledDeltaTime;
                if (panel) panel.localScale = Vector3.one * Mathf.Lerp(1f, 0.94f, t / StageSwap);
                yield return null;
            }
            if (show) show.SetActive(true);
            if (hide && hide != show) hide.SetActive(false);
            t = 0f;
            while (t < StageSwap)
            {
                t += Time.unscaledDeltaTime;
                if (panel) panel.localScale = Vector3.one * Mathf.LerpUnclamped(0.94f, 1f, EaseOutBack(t / StageSwap));
                yield return null;
            }
            if (panel) panel.localScale = Vector3.one;
        }

        static void EnsureEventSystem()
        {
            if (FindFirstObjectByType<EventSystem>()) return;
            var go = new GameObject("EventSystem", typeof(EventSystem));
            DontDestroyOnLoad(go);
#if ENABLE_INPUT_SYSTEM
            go.AddComponent<InputSystemUIInputModule>();
#else
            go.AddComponent<StandaloneInputModule>();
#endif
        }

        static float EaseOutCubic(float x) => 1f - Mathf.Pow(1f - x, 3f);
        static float EaseInCubic(float x) => x * x * x;
        static float EaseOutBack(float x)
        {
            const float c1 = 1.70158f, c3 = c1 + 1f;
            return 1f + c3 * Mathf.Pow(x - 1f, 3f) + c1 * Mathf.Pow(x - 1f, 2f);
        }
    }
}
