using System;
using Nox.Core;
using UnityEngine;
using UnityEngine.UI;

namespace Nox.Game.UI {
    public class ScreenFadeTransition : MonoBehaviour, ISceneTransition {
        private enum FadeState {
            Idle,
            FadingOut,
            FadingIn
        }

        private const float DefaultFadeDuration = 0.3f;
        private const int CanvasSortOrder = 999;

        private CanvasGroup canvasGroup;
        private FadeState fadeState = FadeState.Idle;
        private float fadeTimer;
        private float fadeDuration = DefaultFadeDuration;
        private Action onFadeComplete;

        public bool IsTransitioning => fadeState != FadeState.Idle;

        public void Awake() {
            DontDestroyOnLoad(gameObject);
            CreateFadeCanvas();
            canvasGroup.alpha = 0f;
            canvasGroup.blocksRaycasts = false;
        }

        public void FadeOut(Action onComplete = null) {
            if(fadeState == FadeState.FadingOut) {
                return;
            }
            onFadeComplete = onComplete;
            fadeState = FadeState.FadingOut;
            fadeTimer = 0f;
            canvasGroup.blocksRaycasts = true;
        }

        public void FadeIn(Action onComplete = null) {
            if(fadeState == FadeState.FadingIn) {
                return;
            }
            onFadeComplete = onComplete;
            fadeState = FadeState.FadingIn;
            fadeTimer = 0f;
        }

        public void Update() {
            if(fadeState == FadeState.Idle) {
                return;
            }

            fadeTimer += Time.unscaledDeltaTime;
            float t = Mathf.Clamp01(fadeTimer / fadeDuration);

            switch(fadeState) {
                case FadeState.FadingOut:
                    canvasGroup.alpha = t;
                    if(t >= 1f) {
                        CompleteFade();
                    }
                    break;
                case FadeState.FadingIn:
                    canvasGroup.alpha = 1f - t;
                    if(t >= 1f) {
                        canvasGroup.blocksRaycasts = false;
                        CompleteFade();
                    }
                    break;
            }
        }

        private void CompleteFade() {
            fadeState = FadeState.Idle;
            fadeTimer = 0f;
            var callback = onFadeComplete;
            onFadeComplete = null;
            callback?.Invoke();
        }

        private void CreateFadeCanvas() {
            var canvasObject = new GameObject("FadeCanvas");
            canvasObject.transform.SetParent(transform);

            var canvas = canvasObject.AddComponent<Canvas>();
            canvas.renderMode = RenderMode.ScreenSpaceOverlay;
            canvas.sortingOrder = CanvasSortOrder;

            canvasObject.AddComponent<CanvasScaler>();

            canvasGroup = canvasObject.AddComponent<CanvasGroup>();

            var imageObject = new GameObject("FadeImage");
            imageObject.transform.SetParent(canvasObject.transform, false);

            var image = imageObject.AddComponent<Image>();
            image.color = Color.black;
            image.raycastTarget = true;

            var rectTransform = image.rectTransform;
            rectTransform.anchorMin = Vector2.zero;
            rectTransform.anchorMax = Vector2.one;
            rectTransform.offsetMin = Vector2.zero;
            rectTransform.offsetMax = Vector2.zero;
        }
    }
}
