using System;
using MyGame.Scenes.marketing.Scripts;
using UniRx;
using UniRx.Triggers;
using UnityEngine;

public class BrotherPinkView : SingletonMonoBehaviour<BrotherPinkView>
{
    private static readonly int Normal = Animator.StringToHash("Normal");
    private static readonly int Promotion = Animator.StringToHash("Promotion");
    private static readonly int Sleepy = Animator.StringToHash("Sleepy");
    private static readonly int Walk = Animator.StringToHash("Walk");
    private static readonly int Tasting = Animator.StringToHash("Tasting");
    private static readonly int TastingWalk = Animator.StringToHash("TastingWalk");
    private static readonly int Advertise = Animator.StringToHash("Advertise");
    private static readonly int AdvertiseWalk = Animator.StringToHash("AdvertiseWalk");
    private static readonly Vector2 RightPos = new Vector2(4.96f, -0.26f);
    private static readonly Vector2 LeftPos = new Vector2(-4.96f, -0.26f);
    
    [SerializeField] private Animator animatorPrefab;
    [SerializeField] private Transform pinkTransform;
    [SerializeField] private float walkSideBottomPos;
    [SerializeField] private float pinkWalkSpeed = 4f;
    [SerializeField] private float coolTime = 10f;
    [SerializeField] private int fewerBorder = 5;

    public int FewerBorder => fewerBorder;

    private Animator animator;
    private TastingBasketView basketView;
    private bool waitMove;
    private bool isMoveLocked;
    private bool isPromotion;
    private bool isEndingPromotion;
    private bool isTasting;
    private int nextId;
    private readonly (int trigger, Vector2 wayPoint, float wait)[] pinkMoves =
    {
        (Walk, LeftPos, 0f),
        (Promotion, LeftPos + Vector2.right * .03f, 5.35f),
        (Walk, RightPos, 0f),
        (Promotion, RightPos + Vector2.left * .03f, 5.35f),
    };
    private readonly (int trigger, Vector2 wayPoint, float wait)[] pinkTastingMoves =
    {
        (TastingWalk, LeftPos, 0f),
        (Tasting, LeftPos + Vector2.right * .03f, 5.35f),
        (TastingWalk, RightPos, 0f),
        (Tasting, RightPos + Vector2.left * .03f, 5.35f),
    };
    private readonly (int trigger, Vector2 wayPoint, float wait)[] pinkAdvertiseMoves =
    {
        (AdvertiseWalk, LeftPos, 0f),
        (Advertise, LeftPos + Vector2.right * .03f, 5.35f),
        (AdvertiseWalk, RightPos, 0f),
        (Advertise, RightPos + Vector2.left * .03f, 5.35f),
    };
    private int moveIndex;
    private Vector2 wayPoint;
    private float durationDelta;
    private float completedDuration;
    private (int trigger, Vector2 wayPoint, float wait)[] selectedMoves;
    private IDisposable moveLockDispose;
    private Subject<int> setTrigger = new Subject<int>();

    private void Awake()
    {
        setTrigger.AddTo(this);
        nextId = Normal;
        waitMove = true;
        selectedMoves = pinkMoves;
    }

    private void Start()
    {
        pinkTransform.localPosition = RightPos;
        /*
         * 1フレーム内で最後に受け取った値をSetする
         * アニメーター挙動考慮
         */
        setTrigger 
            .ThrottleFrame(1)
            .Subscribe(x =>
            {
                if (animator != null)
                {
                    animator.SetTrigger(x);
                }
            }).AddTo(this);
        this.UpdateAsObservable().Subscribe(_ =>
        {
            if (waitMove || isMoveLocked)
            {
                return;
            }
            var localPosition = pinkTransform.localPosition;
            localPosition = Vector2.MoveTowards(localPosition, wayPoint, pinkWalkSpeed * Time.deltaTime);
            localPosition = localPosition + Vector3.forward * (localPosition.y + Mathf.Abs(walkSideBottomPos));
            pinkTransform.localPosition = localPosition;
            if (Vector2.Distance(localPosition, wayPoint) < .01f)
            {
                if (durationDelta < completedDuration)
                {
                    durationDelta += Time.deltaTime;
                    return;
                }
                durationDelta = 0;
                moveIndex++;
                if (moveIndex == selectedMoves.Length)
                {
                    if (isTasting || isPromotion)
                    {
                        moveIndex = 0;
                        SetPinkMove();
                        return;
                    }
                    ResetMove();
                    LockMove(coolTime);
                    return;
                }
                SetPinkMove();
            }
        }).AddTo(this);
    }

    private void LockMove(float time)
    {
        isMoveLocked = true;
        moveLockDispose?.Dispose();
        moveLockDispose = Observable.Timer(TimeSpan.FromSeconds(time))
            .Subscribe(l => { }, () =>
            {
                isMoveLocked = false;
            }).AddTo(this);
    }

    private void ResetMove()
    {
        waitMove = true;
        moveIndex = 0;
        if (animator != null) animator.SetTrigger(nextId);
    }

    public void SetBrotherView(Transform target)
    {
        target.DestroyAllChildrens();
        animator = Instantiate(animatorPrefab, target);
        basketView = animator.GetComponent<TastingBasketView>();
        this.UpdateAsObservable().Subscribe(_ =>
        {
            target.localPosition = pinkTransform.localPosition;
            target.localRotation = pinkTransform.localRotation;
        }).AddTo(animator);
        if (waitMove || isPromotion || isTasting)
        {
            return;
        }
        SetPinkMove();
    }

    private void SetPinkMove()
    {
        var move = selectedMoves[moveIndex];
        wayPoint = move.wayPoint;
        completedDuration = move.wait;
        if (animator != null)
        {
            animator.ResetTrigger(Normal);
            setTrigger.OnNext(move.trigger);
        }
        if (Vector2.Distance(wayPoint, pinkTransform.localPosition.ToVector2()) < .01f)
        {
            // nop
        } else if ((wayPoint - pinkTransform.localPosition.ToVector2()).x > 0)
        {
            pinkTransform.localRotation = Quaternion.Euler(Vector3.up * 180);
        }
        else
        {
            pinkTransform.localRotation = Quaternion.Euler(Vector3.zero);
        }
    }

    // お客さんが多い場合に使用するアニメーション切替え
    private void SetTrigger(int id)
    {
        nextId = id;
        if (waitMove)
        {
            animator.SetTrigger(id);
        }
    }

    public void SetNormal()
    {
        SetTrigger(Normal);
    }

    public void SetSleepy()
    {
        animator.ResetTrigger(Normal);
        SetTrigger(Sleepy);
    }
    
    public void StartPromotion()
    {
        moveLockDispose?.Dispose();
        isMoveLocked = false;
        waitMove = false;
        
        isPromotion = true;
        selectedMoves = pinkAdvertiseMoves;
        SetPinkMove();
    }

    public void StopPromotion()
    {
        if (!isPromotion)
        {
            return;
        }
        isPromotion = false;
        selectedMoves = pinkMoves;
        SetPinkMove();
    }
    
    public void StartTasting()
    {
        // tasting発動
        moveLockDispose?.Dispose();
        isMoveLocked = false;
        waitMove = false;
        
        isTasting = true;
        selectedMoves = pinkTastingMoves;
        SetPinkMove();
    }

    public void StopTasting()
    {
        if (!isTasting)
        {
            return;
        }
        isTasting = false;
        selectedMoves = pinkMoves;
        SetPinkMove();
    }

    public void SetTastingCount(int count)
    {
        if (basketView != null)
        {
            basketView.SetItem(count);
        }
    }

    public void SetWalk()
    {
        if (!waitMove || isMoveLocked || isPromotion || isTasting)
        {
            return;
        }
        waitMove = false;
        SetPinkMove();
    }
    
    public void SetActive(bool active)
    {
        if (animator != null)
        {
            animator.speed = active ? 1f : 0f;
            animator.transform.localScale = active ? Vector3.one : Vector3.zero;
            animator.GetComponentInChildren<ParticleSystem>(true)?.transform.SetLocalScale(active ? 1f : 0f);
        }
    }
}
