using System;
using System.Collections.Generic;
using System.Linq;
using MyGame.Scenes.marketing.Scripts;
using MyGame.Scripts;
using TMPro;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using UnityEngine.UI;

public class MarketManager : MonoBehaviour
{
    private static readonly double refreshWaitTime = 300;
    [SerializeField] private GameObject closeSign;
    [SerializeField] private ShopStockView stockView;
    [SerializeField] private MarketCartView cartView;
    [SerializeField] private BrotherBlueView blueView;
    [SerializeField] private IncreaseCustomerButtonView rewardButtonView;
    [SerializeField] private Transform pinkTarget;
    [SerializeField] private Transform coinPrefab;
    [SerializeField] private Transform rootTransform;
    [SerializeField] private TutorialObjectMask walkerMask;
    private Market market;
    List<ProductData> productDataList;
    
    private readonly Subject<int> resetRefreshTimer = new Subject<int>();

    private void Start()
    {
        resetRefreshTimer.AddTo(this);
        market = Market.Instance;
        
        SoundManager.Instance.PlayBGM("bgm_marketing");
        var gameData = GameDataManager.GameData;

        if (gameData.isFirstPlay)
        {
            if (TutorialManager.Instance.Index == 10)
            {
                rewardButtonView.gameObject.SetActive(false);
                TutorialManager.Instance.ShowTutorialConversation(11, () =>
                {
                    walkerMask.gameObject.SetActive(true);
                    // お客さん配置
                    var controller = market.SetTutorialCustomer();
                    walkerMask.SetTargetArrow(controller.transform);
                    controller.State
                        .Where(x => x == CustomerState.Wait)
                        .Take(1)
                        .Subscribe(state => { }, () =>
                        {
                            walkerMask.SetArrowActive(false);
                        }).AddTo(controller).AddTo(this);
                    walkerMask.SetTargetArrow(controller.transform);
                    controller.State
                        .Where(x => x == CustomerState.EatingLeave)
                        .Delay(TimeSpan.FromSeconds(3f))
                        .Take(1)
                        .Subscribe(_ =>
                        {
                            // チュートリアル終了
                            gameData.FinishTutorial();
                            GameDataManager.SaveGameData();
                            walkerMask.gameObject.SetActive(false);
                            resetRefreshTimer.OnNext((int) DateTime.FromBinary(gameData.increaseCustomerTime).Subtract(DateTime.UtcNow).TotalSeconds);
                            rewardButtonView.gameObject.SetActive(true);
                            TutorialManager.Instance.ShowTutorialConversation(12);
                        }).AddTo(controller).AddTo(this);
                });
            }
        }

        CoinManager.Instance.ChangeCoin(gameData.Coin);
        HeartMeter.Instance.Initialize(gameData.ViewedShopLevel, gameData.Heart);
        GameDataObserver.Instance.ShopLevelUpdate
            .Where(x => x && !market.IsPromotion.Value)
            .Subscribe(x =>
            {
                LocalCacheManager.Save(ShopLevelUp.DataTag, gameData.ViewedShopLevel + 1);
                TransitionManager.Instance.LoadSceneAdditive(GameScenes.StoreLevel);
            }).AddTo(this);
        
        productDataList = SpreadsheetDataManager.Instance.GetBaseDataList<ProductData>(Const.ProductDataSheet);
        stockView.SetStock(gameData.StorageTanks);
        List<(int, ProductStockData)> startStocks;
        try
        {
            startStocks = market.ShuffledOrder.Select(x => (x, market.DisplayFlavors[x])).ToList();
        }
        catch (Exception e)
        {
#if UNITY_EDITOR
            Debug.LogError($"{market.ShuffledOrder.Count}, {market.DisplayFlavors.Count}");
#endif
            /*
             * 例外握りつぶし
             */
            startStocks = market.ShuffledOrder
                .Where(x => x < market.DisplayFlavors.Count)
                .Select(x => (x, market.DisplayFlavors[x])).ToList();
        }
        cartView.Initialize();
        cartView.SetStock(startStocks, false);
        BrotherPinkView.Instance.SetBrotherView(pinkTarget);
        
        rewardButtonView.RewardButton.ThrottleFirst(TimeSpan.FromSeconds(.3f)).Subscribe(_ =>
        {
            GetRewardDialog.ShowIncreaseCustomerDialog(() =>
            {
                market.AdClickAction();
                gameData.increaseCustomerTime = DateTime.UtcNow.AddSeconds(refreshWaitTime).ToBinary();
#if UNITY_EDITOR
                gameData.increaseCustomerTime = DateTime.UtcNow.AddSeconds(10).ToBinary();
#endif
                GameDataManager.SaveGameData();
                resetRefreshTimer.OnNext((int) DateTime.FromBinary(gameData.increaseCustomerTime).Subtract(DateTime.UtcNow).TotalSeconds);
            });
        }).AddTo(this);
        
        IDisposable timerDisposable = null;
        resetRefreshTimer
            .Subscribe(time =>
            {
                timerDisposable?.Dispose();
                rewardButtonView.SetButtonActive(time <= 0);
                rewardButtonView.SetTime(time);
                timerDisposable = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1f))
                    .Select(x => (int)(time - x))
                    .TakeWhile(x => x > 0)
                    .Subscribe(x =>
                    {
                        rewardButtonView.SetTime(x);
                    }, () =>
                    {
                        rewardButtonView.SetButtonActive(true);
                    }).AddTo(this);
            }).AddTo(this);

        var remaining = (int) DateTime.FromBinary(gameData.increaseCustomerTime).Subtract(DateTime.UtcNow).TotalSeconds;
        resetRefreshTimer.OnNext(remaining);
        
        Observable.EveryApplicationPause()
            .Where(pause => !pause)
            .Subscribe(_ =>
            {
                resetRefreshTimer.OnNext((int) DateTime.FromBinary(gameData.increaseCustomerTime).Subtract(DateTime.UtcNow).TotalSeconds);
            }).AddTo(this);

        market.IsPromotion.Subscribe(active =>
        {
            if (active)
            {
                SoundManager.Instance.PlayBGM("bgm_publicity");
                BrotherPinkView.Instance.StartPromotion();
            }
            else
            {
                SoundManager.Instance.PlayBGM("bgm_marketing");
            }
        }).AddTo(this);
        

        // Customerの各アニメーション設定
        foreach (var controller in market.CustomerControllerList)
        {
            GenerateCustomer(controller);
        }
        market.CustomerControllerList.ObserveAdd().Subscribe(x =>
        {
            GenerateCustomer(x.Value);
        }).AddTo(this);

        // 販売
        market.SellObservable.Subscribe(coin =>
        {
            // コイン獲得エフェクト
            CoinEffect(coin);
            CoinManager.Instance.AddCoinWithEffect(coin, () => { });
            
            blueView.SellAction();
        }).AddTo(this);

        market.SellOrderSubject.Subscribe(orders =>
        {
            // 販売アニメーション
            foreach (var order in orders)
            {
                cartView.SellStock(order);
            }
        }).AddTo(this);

        market.RefillSubject.Subscribe(x =>
        {
            stockView.SetStock(gameData.StorageTanks);
            if (x.isReorder)
            {
                // 陳列表示更新(陳列13=650ms,7=350ms)
                var stocks = market.ShuffledOrder.Select(i => (i, market.DisplayFlavors[i])).ToList();
                cartView.SetStock(stocks, true);
            }
            else
            {
                // 補充したフレーバーのスキンを設定
                foreach (var order in x.refillList)
                {
                    try
                    {
                        cartView.Refill(order, market.DisplayFlavors[order]);
                    }
                    catch (Exception e)
                    {
                        Debug.LogError($"order:{order}, displayFlavors:{market.DisplayFlavors.Count}");
                        throw;
                    }
                }
            }
        }).AddTo(this);

        // 開閉店
        CompositeDisposable shopStateCompositeDisposable = new CompositeDisposable();
        shopStateCompositeDisposable.AddTo(this);
        market.CurrentShopState.Subscribe(state =>
        {
            shopStateCompositeDisposable.Clear();
            switch (state)
            {
                case ShopState.Open:
                    BrotherPinkView.Instance.SetNormal();
                    closeSign.SetActive(false);
                    break;
                case ShopState.Busy:
                    break;
                case ShopState.Close:
                    if (market.CustomerList.Count(x => x.State.Value == CustomerState.Order) == 0)
                    {
                        BrotherPinkView.Instance.SetSleepy();
                        closeSign.SetActive(true);
                    }
                    else
                    {
                        // お客さんがいなくなったあと、閉店の看板を出す
                        Observable.CombineLatest(
                                market.CustomerList
                                    .Select(x => x.State)
                                    .Where(x => x.Value == CustomerState.Order))
                            .FirstOrDefault(states => states.Count(x => x == CustomerState.Order) == 0)
                            .Delay(TimeSpan.FromSeconds(2f))
                            .Subscribe(_ =>
                            {
                                BrotherPinkView.Instance.SetSleepy();
                                closeSign.SetActive(true);
                            }).AddTo(shopStateCompositeDisposable);
                    }
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(state), state, null);
            }
        }).AddTo(this);
    }

    private void CoinEffect(int count)
    {
        var effect = Instantiate(coinPrefab, Vector3.zero, Quaternion.identity, rootTransform);
        effect.GetComponentInChildren<TextMeshProUGUI>().text = count.ToString();
        Destroy(effect.gameObject, 1f);
    }

    private void GenerateCustomer(CustomerController controller)
    {
        var customerObject = new GameObject();
        customerObject.transform.parent = transform;
        customerObject.transform.localPosition = controller.transform.localPosition;
        // controllerが破棄のタイミングでこちらも破棄
        controller.OnDestroyAsObservable().Subscribe(_ =>
        {
            Destroy(customerObject);
        }).AddTo(customerObject);
        // customer位置同期
        this.UpdateAsObservable().Subscribe(_ =>
        {
            customerObject.transform.localPosition = controller.transform.localPosition;
        }).AddTo(customerObject);
        
        var customerAnimator = Instantiate(controller.CustomerPrefab, customerObject.transform);
        controller.CurrentMovingType.Subscribe(x =>
        {
            customerAnimator.SetTrigger(x);
        }).AddTo(customerAnimator);
        controller.IsDefaultSide.Subscribe(x =>
        {
            customerAnimator.SetSide(x);
        }).AddTo(customerAnimator);
        // フレーバー設定
        if (productDataList.FirstOrDefault(data => data.id == (controller.WantFlavor.Value?.FlavorId ?? -1))?.GetMarketPrefab() is MarketPopcornView initialPrefab)
        {
            customerAnimator.SetWantFlavor(initialPrefab, controller.WantFlavor.Value.Rarity);
        }
        controller.WantFlavor
            .SkipLatestValueOnSubscribe()
            .Subscribe(wantData =>
            {
                if (productDataList.FirstOrDefault(data => data.id == wantData.FlavorId)?.GetMarketPrefab() is MarketPopcornView prefab)
                {
                    customerAnimator.SetWantFlavor(prefab, wantData.Rarity);
                    customerAnimator.ShowWantFlavor();
                }
            }).AddTo(customerAnimator);
        controller.IsComplain
            .SkipLatestValueOnSubscribe()
            .Subscribe(active =>
            {
                if (active)
                {
                    customerAnimator.ShowComplain();
                }
            }).AddTo(customerAnimator);
        controller.Purchased.Subscribe(_ =>
        {
            customerAnimator.HideOrderPopup();
            var heartAnimation = customerAnimator.GetComponent<HeartAnimation>();
            heartAnimation.GetHeart(HeartMeter.Instance.transform, () =>
            {
                HeartMeter.Instance.AddHeart(1);
            });
        }).AddTo(customerAnimator);
        var eventTrigger = customerAnimator.gameObject.AddComponent<ObservableEventTrigger>();
        eventTrigger.OnPointerClickAsObservable()
            .Where(_ => market.CurrentShopState.Value != ShopState.Close)
            .Take(1)
            .Subscribe(_ =>
            {
                if (customerAnimator.TryGetComponent(typeof(Collider2D), out var target))
                {
                    Destroy(target);
                }

                if (controller.State.Value != CustomerState.Walk &&
                    controller.State.Value != CustomerState.WalkShop &&
                    controller.State.Value != CustomerState.CenterStop)
                {
                    return;
                }
                controller.Tapped();
                customerAnimator.ShowTapReaction();
                VibrationManager.Instance.PlayVibrationOnce();
            }).AddTo(customerAnimator);
    }
}