using System;
using System.Diagnostics;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using Debug = UnityEngine.Debug;
using Random = UnityEngine.Random;

namespace MyGame.Scenes.Main.Scripts
{
    public class BulkOrderAchievedEffect : MonoBehaviour
    {
        [SerializeField] private Transform coinPrefab;
        [SerializeField] private Transform heartPrefab;
        [SerializeField] private int coinCount = 5;
        [SerializeField] private int heartCount = 5;
        [SerializeField] private float minCurve = 100f;
        [SerializeField] private float maxCurve = 400f;
        [SerializeField] private float waitTime = .3f;
        [SerializeField] private float curveTime = .5f;
        [SerializeField] private float linerTime = .5f;
        private readonly Subject<Unit> effectCompleteSubject = new Subject<Unit>();

        private void Start()
        {
            SetupDebugEffect();
        }

        public void PlayCoinEffect(Vector3 beginPos, Vector3 goalPos, Action onComplete = null)
        {
            // 最後のeffectが終わった後１度だけonComplete
            effectCompleteSubject.Take(coinCount).Subscribe(_ => { }, () =>
            {
                onComplete?.Invoke();
            }).AddTo(this);
            for (int i = 0; i < coinCount; i++)
            {
                this.CallWaitForSeconds(.1f * i, () =>
                {
                    var coin = Instantiate(coinPrefab, beginPos, Quaternion.identity, transform); 
                    coin.GetComponent<Animator>()?.Play("BulkOrde_EarnedCoins");
                    Observable.NextFrame().Subscribe(_ =>
                    {
                        Effect(coin, goalPos, () => effectCompleteSubject.OnNext(Unit.Default));
                    }).AddTo(this);
                });
            }
        }
        
        public void PlayHeartEffect(Vector3 beginPos, Vector3 goalPos, Action onComplete = null)
        {
            // 最後のeffect終了時か、このオブジェクトが破棄された時に１度だけonCompleteが実行される
            effectCompleteSubject.Take(heartCount).Last()
                .Amb(this.OnDestroyAsObservable())
                .Subscribe(_ => { }, () =>
                {
                    onComplete?.Invoke();
                });
            for (int i = 0; i < heartCount; i++)
            {
                this.CallWaitForSeconds(.1f * i, () =>
                {
                    var heart = Instantiate(heartPrefab, beginPos, Quaternion.identity, transform); 
                    Effect(heart, goalPos, () => effectCompleteSubject.OnNext(Unit.Default));
                });
            }
        }

        private void Effect(Transform target, Vector3 goalPos, Action onComplete = null)
        {
            var animator = target.GetComponent<Animator>();
            var nameHash = animator.GetCurrentAnimatorStateInfo(0).shortNameHash;
            var clip = animator.GetCurrentAnimatorClipInfo(0)[0].clip;
            var beginDuration = 37 / clip.frameRate;
            var beginNormalizedTime = beginDuration / clip.length;
            var endNormalizedTime = 52 / clip.frameRate / clip.length;
            var endDuration = (1 - endNormalizedTime) * clip.length;
            
            var direction = Random.value <= .5f ? -1 : 1;
            var depth = Random.Range(minCurve, maxCurve);
            this.CallWaitForSeconds(waitTime, () =>
            {
                animator.Play(nameHash, 0, beginNormalizedTime);
                animator.speed = 0f;
                var initPos = target.localPosition;
                this.CallLerp(curveTime, f =>
                {
                    var x = Mathf.Lerp(-1, 1, f) * direction;
                    target.localPosition = initPos + new Vector3(f * direction * depth, x * x * depth - depth);
                }, () =>
                { 
                    var pos = target.position;
                    this.CallLerp(linerTime, f =>
                    {
                        target.position = Vector2.Lerp(pos, goalPos, f);
                    }, () =>
                    {
                        animator.speed = 1f;
                        animator.Play(nameHash, 0, endNormalizedTime);
                        this.CallWaitForSeconds(endDuration, () =>
                        {
                            onComplete?.Invoke();
                            Destroy(target.gameObject);
                        });
                    });
                });
            });
        }

        [Conditional("UNITY_EDITOR")]
        private void SetupDebugEffect()
        {
            var beginPos = Vector3.zero;
            this.UpdateAsObservable()
                .Where(_ => Input.GetKey(KeyCode.E))
                .ThrottleFirst(TimeSpan.FromSeconds(.5f))
                .Subscribe(
                    _ =>
                    {
                        CoinManager.Instance.AddCoinForBulkOrder(100, beginPos);
                        GameDataManager.GameData.WaitAddHeart += 0;
                        HeartMeter.Instance.AddHeartForBulkOrder(beginPos, () =>
                        {
                            GameDataManager.GameData.MoveHeart(0);
                        });
                    }).AddTo(this);
        }
    }
}