using System.Collections.Generic;
using System.Linq;
using DG.Tweening;
using BingoBrain.Core;
using BingoBrain.HotFix;
using Spine.Unity;
using UnityEngine;

namespace BingoBrain
{
    public class CardBoardEntity
    {
        public GameCell GoCell = new();

        public CardBoardData data = new();
        public List<CardEntity> cardList = new();
        public bool isFinish;
        public bool isBingoing;

        public SkeletonAnimation bingGoSke;
        private ParticleSystem[] idleSys = new ParticleSystem[3];
        private ParticleSystem[] enterSys = new ParticleSystem[3];
        private ParticleSystem exitSys;

        private Vector3 originPos = new(-3.38f, 2.96f);
        private float offset = 1.685f;
        private Transform finishMask;

        private List<int> needShowBingoList = new();
        private Gsss showBingoSequence = new();

        private bool isDelayCheckBingo;

        public List<int> bingoedList = new();

        public void SetData()
        {
            InitGameObj();
            InitCardList();
            InitReward();
        }

        void InitReward()
        {
            var coinCount = Random.Range(BingoCell.MinCoinCount, BingoCell.MaxCoinCount + 1);
            var cashCount = Random.Range(BingoCell.MinCashCount, BingoCell.MaxCashCount + 1);

            var list = GlobalHarmony.GetRandomList(
                cardList.FindAll(tmp => !tmp.data.isSelect && !tmp.data.isCoin && tmp.data.type == CardPropType.none),
                coinCount);
            if (list != null)
            {
                foreach (var card in list)
                {
                    card.data.type = CardPropType.coin;
                }
            }

            list = GlobalHarmony.GetRandomList(
                cardList.FindAll(tmp => !tmp.data.isSelect && !tmp.data.isCoin && tmp.data.type == CardPropType.none),
                cashCount);
            if (list != null)
            {
                foreach (var card in list)
                {
                    card.data.type = CardPropType.cash;
                }
            }

            foreach (var card in cardList)
            {
                card.SetReward();
            }
        }

        private void InitGameObj()
        {
            if (GoCell.gameObject == null)
            {
                GoCell.InitByPath("Prefab.Game.Card", "CardBoard", () =>
                {
                    GoCell.transform.SetParent(BingoCell.root, false);
                    GoCell.gameObject.SetActive(false);
                    finishMask = GoCell.transform.Find("Finish");
                    DoSomething();
                });
            }
            else
            {
                DoSomething();
            }
        }

        private void DoSomething()
        {
            if (!bingGoSke)
            {
                FX.Instance.GetFx<SkeletonAnimation>(Fx_Type.spine_bingo, sk =>
                {
                    bingGoSke = sk;
                    bingGoSke.transform.SetParent(GoCell.transform);
                    bingGoSke.transform.localPosition = Vector3.zero;
                    idleSys[0] = bingGoSke.transform.Find("fx_lizi_idle01").GetComponent<ParticleSystem>();
                    idleSys[1] = bingGoSke.transform.Find("fx_lizi_idle02").GetComponent<ParticleSystem>();
                    idleSys[2] = bingGoSke.transform.Find("fx_lizi_idle03").GetComponent<ParticleSystem>();
                    enterSys[0] = bingGoSke.transform.Find("fx_lizi_enter01").GetComponent<ParticleSystem>();
                    enterSys[1] = bingGoSke.transform.Find("fx_lizi_enter02").GetComponent<ParticleSystem>();
                    enterSys[2] = bingGoSke.transform.Find("fx_lizi_enter03").GetComponent<ParticleSystem>();
                    for (var i = 0; i < 3; i++)
                    {
                        idleSys[i].gameObject.SetActive(false);
                        enterSys[i].gameObject.SetActive(false);
                    }

                    exitSys = bingGoSke.transform.Find("fx_lizi_exit").GetComponent<ParticleSystem>();
                    bingGoSke.gameObject.SetActive(false);
                    GoCell.gameObject.SetActive(true);
                    finishMask.gameObject.SetActive(false);
                });
            }
            else
            {
                bingGoSke.gameObject.SetActive(false);
                GoCell.gameObject.SetActive(true);
                finishMask.gameObject.SetActive(false);
            }
        }

        private void InitCardList()
        {
            var index = 0;
            var isFirst = cardList.Count == 0;
            foreach (var cardData in data.cardNumList)
            {
                CardEntity cardEntity;
                if (isFirst)
                {
                    cardEntity = new CardEntity();
                    cardEntity.InitGameObj(() =>
                    {
                        cardEntity.data.index = index;
                        cardEntity.data.cardBoard = this;
                        cardList.Add(cardEntity);
                        SetByIndex(cardEntity.Game.transform, index);
                        cardEntity.Game.transform.SetParent(GoCell.transform, false);
                        index++;

                        cardEntity.data.isSelect = false;
                        cardEntity.data.isCoin = false;
                        cardEntity.data.isDelaySelect = false;
                        cardEntity.data.type = CardPropType.none;
                        cardEntity.data.num = cardData;
                        cardEntity.SetByData();
                    });
                }
                else
                {
                    cardEntity = cardList[index];
                    cardEntity.InitGameObj(() =>
                    {
                        cardEntity.data.isSelect = false;
                        cardEntity.data.isCoin = false;
                        cardEntity.data.isDelaySelect = false;
                        cardEntity.data.type = CardPropType.none;
                        cardEntity.data.num = cardData;
                        cardEntity.SetByData();
                    });
                    index++;
                }

                if (index == data.cardNumList.Count)
                {
                    finishMask.SetAsLastSibling();
                }
            }
        }

        private void SetByIndex(Transform gameObjTransform, int index)
        {
            if (gameObjTransform != null)
            {
                gameObjTransform.localPosition = originPos + new Vector3(index / BingoCell.bingoCount * offset,
                    index % BingoCell.bingoCount * -(offset + 0.02f));
            }
            else
            {
                Debug.Log("[ BingoBrain ] gameObjTransform is null");
            }
        }

        public void OnReset()
        {
            needShowBingoList.Clear();
            bingoedList.Clear();
            data.selectList.Clear();
            data.cardNumList.Clear();
            isBingoing = false;
            isFinish = false;
            foreach (var card in cardList)
            {
                card.OnReset();
            }

            for (var i = 0; i < 3; i++)
            {
                idleSys[i].gameObject.SetActive(false);
                enterSys[i].gameObject.SetActive(false);
            }

            exitSys.gameObject.SetActive(false);

            showBingoSequence?.Cancel();
        }

        public void DelayCheckBingo()
        {
            if (BingoCell.isProcedure)
            {
                isDelayCheckBingo = true;
            }
            else
            {
                CheckBingo();
            }
        }

        public void CheckBingo()
        {
            if (BingoCell.isProcedure)
            {

                return;
            }

            if (BingoCell.bingoCardboardIndex != -1 && BingoCell.bingoCardboardIndex != data.index)
            {

                return;
            }

            var bingoType = IsBingo(data.selectList);
            while (bingoType != -1)
            {
                BingoCell.bingoCardboardIndex = data.index;
                isBingoing = true;
                bingoedList.Add(bingoType);
                PreferencesMgr.Instance.BingoSum += 1;

                needShowBingoList.Add(bingoType);
                bingoType = IsBingo(data.selectList);
            }

            showBingoSequence = new Gsss();
            if (needShowBingoList.Count != 0)
            {

                showBingoSequence.Add(obj =>
                {
                    var delay = 0.2f;
                    foreach (var tmpType in needShowBingoList)
                    {
                        var count = BingoCell.bingoDic[tmpType].Count;

                        for (var j = 0; j < count; j++)
                        {
                            var index = BingoCell.bingoDic[tmpType][j];
                            if (cardList[index].data.isCoin)
                            {
                                continue;
                            }

                            cardList[index].SetCoin(delay);
                        }
                    }

                    DOVirtual.DelayedCall(delay, obj.InvokeComplete).SetAutoKill();
                });


                showBingoSequence.Add((obj) =>
                {
                    Audio.Instance.PlayDynamicEffect("Bingo");

                    int bingoingNumber = Mathf.Clamp(bingoedList.Count, 0, 3);
                    NAAVsa.SetSkin(bingGoSke, "default");
                    this.bingGoSke.gameObject.SetActive(true);
                    switch (bingoingNumber)
                    {
                        case 1:
                            this.bingGoSke.state.SetAnimation(0, "single", false);
                            break;
                        case 2:
                            this.bingGoSke.state.SetAnimation(0, "double", false);
                            break;
                        case 3:
                            this.bingGoSke.state.SetAnimation(0, "multiple", false);
                            break;
                    }


                    enterSys[bingoingNumber - 1].gameObject.SetActive(true);
                    idleSys[bingoingNumber - 1].gameObject.SetActive(true);
                    finishMask.gameObject.SetActive(true);
                    if (bingoedList.Count >= 3 && !isDelayCheckBingo)
                    {
                        DOVirtual.DelayedCall(1.8f, () =>
                        {
                            NAAVsa.SetSkin(bingGoSke, "default");
                            idleSys[bingoingNumber - 1].gameObject.SetActive(true);
                            enterSys[bingoingNumber - 1].gameObject.SetActive(false);
                            obj.InvokeComplete();
                        }).SetAutoKill();
                    }
                    else
                    {
                        DOVirtual.DelayedCall(1.5f, () =>
                        {
                            exitSys.gameObject.SetActive(true);
                            idleSys[bingoingNumber - 1].gameObject.SetActive(false);
                            enterSys[bingoingNumber - 1].gameObject.SetActive(false);

                            DOVirtual.DelayedCall(0.8f, (TweenCallback)(() =>
                            {
                                finishMask.gameObject.SetActive(false);
                                this.bingGoSke.gameObject.SetActive(false);
                                exitSys.gameObject.SetActive(false);

                                obj.InvokeComplete();
                            }));
                        }).SetAutoKill();
                    }
                });


                if (needShowBingoList.Count > 1)
                {

                    showBingoSequence.Add(true, obj =>
                    {
                        UICtrlDispatcher.Instance.Dispatch(SkinInfo.ChestUI_Open);
                        UICtrlDispatcher.Instance.AddOnceListener(SkinInfo.ChestUI_Close,
                            _ => { obj.InvokeComplete(); });
                    });
                }
            }

            if (showBingoSequence.Count != 0)
            {

                showBingoSequence.Add(obj =>
                {
                    if (BingoCell.KeyCardSum >= 3)
                    {

                        UICtrlDispatcher.Instance.AddOnceListener(SkinInfo.BingoCardUI_Close, o =>
                        {

                            obj.InvokeComplete();
                        });
                        BingoCell.OpenActivityUI();
                    }
                    else
                    {
                        obj.InvokeComplete();
                    }
                });
            }
            else
            {
                if (BingoCell.KeyCardSum >= 3)
                {
                    showBingoSequence.Add(obj =>
                    {
                        if (BingoCell.KeyCardSum >= 3)
                        {

                            UICtrlDispatcher.Instance.AddOnceListener(SkinInfo.BingoCardUI_Close, o =>
                            {

                                obj.InvokeComplete();
                            });
                            BingoCell.OpenActivityUI();
                        }
                        else
                        {
                            obj.InvokeComplete();
                        }
                    });
                }
            }


            if (showBingoSequence.Count != 0)
            {
                BingoCell.isProcedure = true;
                GameDispatcher.Instance.Dispatch(BingoInfo.AddPause, 1);
                showBingoSequence.onFinish = OnFinish;
                showBingoSequence.Run();

            }
            else
            {
                isBingoing = false;
                BingoCell.isProcedure = false;
                showBingoSequence.onFinish = null;


                CheckFinish();
            }
        }

        private void OnFinish()
        {
            GameDispatcher.Instance.Dispatch(BingoInfo.AddPause, -1);
            needShowBingoList.Clear();
            BingoCell.bingoCardboardIndex = -1;

            BingoCell.isProcedure = false;
            if (isDelayCheckBingo)
            {

                isDelayCheckBingo = false;
                CheckBingo();
                GameDispatcher.Instance.Dispatch(BingoInfo.CheckBingo, data.index);
            }
            else
            {

                if (isBingoing)
                {
                    isBingoing = false;
                    CheckFinish();
                    GameDispatcher.Instance.Dispatch(BingoInfo.CheckBingo, data.index);
                }
            }
        }


        void CheckFinish()
        {
            if (bingoedList.Count >= 3)
            {

                isFinish = true;

                if (!bingGoSke.gameObject.activeInHierarchy)
                {
                    bingGoSke.gameObject.SetActive(true);
                    NAAVsa.SetSkin(bingGoSke, "default");

                    idleSys[2].gameObject.SetActive(true);
                    finishMask.gameObject.SetActive(true);
                }

                if (!BingoCell.IsInGame)
                {

                    return;
                }

                GameDispatcher.Instance.Dispatch(BingoInfo.FinishOneCardBoard, this);
            }
        }

        public int IsBingo(List<int> selectList)
        {
            foreach (var kv in from kv in BingoCell.bingoDic
                               where !bingoedList.Contains(kv.Key)
                               let isAllMatch = kv.Value.All(selectList.Contains)
                               where isAllMatch
                               select kv)
            {
                return kv.Key;
            }

            return -1;
        }
    }
}