using System.Threading;
using Cysharp.Threading.Tasks;
using Services;
using Structs;
using UnityEngine;
using Utils;

namespace Views {
    public class GemView : MonoBehaviour {
        [SerializeField]
        private SpriteRenderer childSpriteRenderer;
        
        private Gem gem;
        public Gem Gem => this.gem;
        
        private bool isFalling;
        private const float SPAWN_SCALE_FROM = 0f;
        private const float SPAWN_SCALE_DURATION = 0.12f;

        private CancellationTokenSource spawnScaleCts;

        public void Bind(Gem gem, GemTypeValues gemValue, bool isBomb = false) {
            this.gem = gem;
            this.gameObject.SetActive(true);

            SetupGem(isBomb, gemValue.gemSprite);
            PlaySpawnScale();
        }

        private void SetupGem(bool isBomb, Sprite gemSprite = null) {
            if (!isBomb)
                return;

            if (this.childSpriteRenderer is null)
                return;

            if (gemSprite != null) {
                this.childSpriteRenderer.enabled = true;
                this.childSpriteRenderer.sprite = gemSprite;
            } else {
                this.childSpriteRenderer.enabled = false;
                this.childSpriteRenderer.color = Color.white;
            }
            
        }

        public void Unbind() {
            this.spawnScaleCts?.Cancel();
            this.spawnScaleCts?.Dispose();
            this.spawnScaleCts = null;

            this.gem = null;
            this.gameObject.SetActive(false);
            this.isFalling = false;
        }

        private void PlaySpawnScale() {
            this.spawnScaleCts?.Cancel();
            this.spawnScaleCts?.Dispose();
            this.spawnScaleCts = new CancellationTokenSource();

            this.transform.localScale = Vector3.one * SPAWN_SCALE_FROM;
            AnimateSpawnScaleAsync(this.spawnScaleCts.Token).Forget();
        }

        private async UniTask AnimateSpawnScaleAsync(CancellationToken ct) {
            float timer = 0f;
            while (timer < SPAWN_SCALE_DURATION) {
                ct.ThrowIfCancellationRequested();

                timer += Time.deltaTime;
                float scale = Mathf.Clamp01(timer / SPAWN_SCALE_DURATION);

                scale = Mathf.SmoothStep(0f, 1f, scale);

                this.transform.localScale = Vector3.LerpUnclamped(
                    Vector3.one * SPAWN_SCALE_FROM,
                    Vector3.one,
                    scale
                );

                await UniTask.Yield(PlayerLoopTiming.Update, ct);
            }

            this.transform.localScale = Vector3.one;
        }

        private void FallDelay() {
            this.isFalling = true;
        }
        
        public void UpdatePosition(Vector2Int positionBasedOnIndex, float gemSpeed) {
            if (!this.isFalling) {
                FallDelay();
            }

            if (!this.isFalling)
                return;
            
            if (Vector2.Distance(this.transform.position, positionBasedOnIndex.ToVector2()) > 0.01f) {
                this.transform.position = Vector2.Lerp(this.transform.position, positionBasedOnIndex.ToVector2(), gemSpeed * Time.deltaTime);
            }
        }
    }
}