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 MarketView marketView;
    [SerializeField] private GameObject closeSign;
    private ShopStockView stockView;
    [SerializeField] private MarketCartView cartView;
    [SerializeField] private BrotherBlueView blueView;
    [SerializeField] private IncreaseCustomerButtonView rewardButtonView;
    [SerializeField] private VipCustomerButtonView vipCustomerButtonView;
    [SerializeField] private MarketSignBoardView signBoardView;
    [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<Unit> customerRewardTimerUpdateSubject = new Subject<Unit>();
    private readonly Subject<Unit> vipTimerUpdateSubject = new Subject<Unit>();
    private readonly CompositeDisposable ShopClosedCompositeDisposable = new CompositeDisposable();

    private void Start()
    {
        ShopClosedCompositeDisposable.AddTo(this);
        customerRewardTimerUpdateSubject.AddTo(this);
        vipTimerUpdateSubject.AddTo(this);
        market = Market.Instance;
        
        SoundManager.Instance.PlayBGM("bgm_marketing");
        var gameData = GameDataManager.GameData;
        
        // カスタマイズ読み込み
        marketView.SetAllItem();
        stockView = marketView.GetTarget(ShopCustomizeCategory.Category4).GetComponentInChildren<ShopStockView>();
        stockView.SetTextActive(true);
        ShopCustomizeCoinManager.Instance.ChangeCoin(gameData.ShopCustomizeCoin);

        if (!gameData.FinishedFlags.HasFlag(TutorialFlag.FirstPlay))
        {
            if (TutorialManager.Instance.Index == 3)
            {
                // SetShopViewを使わず表示構築
                rewardButtonView.gameObject.SetActive(false);
                blueView.OpenAction();

                market.IsPause.Value = true;
                TutorialManager.Instance.ShowTutorialConversation(4, () =>
                {
                    market.IsPause.Value = false;
                    walkerMask.gameObject.SetActive(true);
                    // お客さん配置
                    var controller = market.SetTutorialCustomer();
                    walkerMask.SetTargetArrow(controller.transform, offset:50f);
                    controller.State
                        .FirstOrDefault(x => x == CustomerState.Wait)
                        .Subscribe(_ => { }, () =>
                        {
                            // マスクの透明度変更
                            walkerMask.SetBGAlpha(0f);
                            walkerMask.SetArrowActive(false);
                        }).AddTo(controller).AddTo(this);
                    controller.State
                        .FirstOrDefault(x => x == CustomerState.EatingLeave)
                        .Delay(TimeSpan.FromSeconds(3f))
                        .Subscribe(_ => { }, () =>
                        {
                            // チュートリアル終了
                            gameData.FinishTutorial();
                            GameDataManager.SaveGameData();
                            walkerMask.gameObject.SetActive(false);
                            customerRewardTimerUpdateSubject.OnNext(Unit.Default);
                            rewardButtonView.gameObject.SetActive(true);
                        }).AddTo(controller).AddTo(this);
                });
            }
        }

        CoinManager.Instance.ChangeCoin(gameData.Coin);
        HeartMeter.Instance.Initialize(gameData.ViewedShopLevel, gameData.Heart);
        
        // ハートゲージがフルかつダイアログが開いていない場合レベルアップ
        HeartMeter.Instance.FulledHeart
            .CombineLatest(market.IsPromotion.Delay(TimeSpan.FromSeconds(1f)), (fulled, isPromotion) => fulled && !isPromotion)
            .DistinctUntilChanged()
            .Where(x => x)
            .DelaySubscription(TimeSpan.FromSeconds(.4f))
            .Subscribe(_ =>
            {
                market.IsPause.Value = true;
                ShopLevelUp.ShowDialog(gameData.ViewedShopLevel + 1, () =>
                {
                    market.IsPause.Value = false;
                });
            }).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);
        
        // ポーズ中動作指定
        market.IsPause.Subscribe(isPause =>
        {
            vipCustomerButtonView.SetActive(isPause);
            // 兄弟非表示
            blueView.SetActive(!isPause);
            BrotherPinkView.Instance.SetActive(!isPause);
        }).AddTo(this);
        
        // 宣伝ボタン
        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
                gameData.AddUseAdWalker();
                GameDataManager.SaveGameData();
                customerRewardTimerUpdateSubject.OnNext(Unit.Default);
            });
        }).AddTo(this);
        
        // VIP宣伝ボタン
        vipCustomerButtonView.VipButton.ThrottleFirst(TimeSpan.FromSeconds(.3f)).Subscribe(_ =>
        {
            GetRewardDialog.ShowVipCustomerDialog(() =>
            {
                market.VipAction();
                // 現在時刻に設定して期限切れにする
                gameData.vipCustomerLimitTime = DateTime.UtcNow.ToBinary();
                vipTimerUpdateSubject.OnNext(Unit.Default);
            }, () =>
            {
                // 現在時刻に設定して期限切れにする
                gameData.vipCustomerLimitTime = DateTime.UtcNow.ToBinary();
                vipTimerUpdateSubject.OnNext(Unit.Default);
            });
        }).AddTo(this);

        // onNextをトリガーに実行
        customerRewardTimerUpdateSubject
            .Select(_ => (int) DateTime.FromBinary(gameData.increaseCustomerTime).Subtract(DateTime.UtcNow).TotalSeconds)
            .Subscribe(time =>
            {
                rewardButtonView.ResetTimer(time);
            }).AddTo(this);

        if (DateTime.FromBinary(gameData.vipCustomerLimitTime) >= DateTime.UtcNow)
        {
            vipCustomerButtonView.ShowButton(gameData.vipCustomerFirstOpen);
            gameData.vipCustomerFirstOpen = false;
            vipTimerUpdateSubject
                .Select(_ => (int) DateTime.FromBinary(gameData.vipCustomerLimitTime).Subtract(DateTime.UtcNow).TotalSeconds)
                .Subscribe(time =>
                {
                    vipCustomerButtonView.ResetTimer(time);
                }).AddTo(this);
        }

        customerRewardTimerUpdateSubject.OnNext(Unit.Default);
        vipTimerUpdateSubject.OnNext(Unit.Default);
        
        // アプリ復帰時に残り時間更新
        Observable.EveryApplicationPause()
            .Where(pause => !pause)
            .Subscribe(_ =>
            {
                customerRewardTimerUpdateSubject.OnNext(Unit.Default);
                vipTimerUpdateSubject.OnNext(Unit.Default);
            }).AddTo(this);
        
        market.IsPromotion.Subscribe(active =>
        {
            SoundManager.Instance.PlayBGM(active ? "bgm_publicity" : "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, pos =>
            {
                CoinManager.Instance.AddCoinWithEffect(coin, pos);
            });
            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);

        // 開閉店
        market.CurrentShopState
            .Subscribe(state =>
            {
                SetShopView(state);
            }).AddTo(this);

        // 試食看板はStart時非表示
        signBoardView.SetActiveTastingBoard(false);
        
        // 宣伝ボタン/試食表示切替
        var tastingComplete = new Subject<Unit>().AddTo(this);
        market.CurrentShopState
            .SkipWhile(_ => !gameData.FinishedFlags.HasFlag(TutorialFlag.FirstPlay))
            .CombineLatest(market.IsPromotion, tastingComplete, (shopState, isPromotion, _) => (shopState == ShopState.Open, isPromotion))
            .Subscribe(x =>
            {
                var (isOpen, isPromotion) = x;
                if (isOpen)
                {
                    if (isPromotion)
                    {
                        signBoardView.SetActiveTastingBoard(false);
                        // 宣伝ボタン表示
                        rewardButtonView.gameObject.SetActive(true);
                        BrotherPinkView.Instance.StartPromotion();
                    }
                    else if (gameData.TastingCount > 0)
                    {
                        rewardButtonView.gameObject.SetActive(false);
                        // 試食表示
                        signBoardView.SetActiveTastingBoard(true);
                        BrotherPinkView.Instance.StartTasting();
                        BrotherPinkView.Instance.SetTastingCount(gameData.TastingCount);
                        signBoardView.SetTimer(gameData.TastingCount * (int)market.TastingCustomerInterval, () =>
                        {
                            BrotherPinkView.Instance.SetTastingCount(gameData.TastingCount);
                        }, () =>
                        {
                            // 試食タイマーが終わったら表示更新トリガー
                            tastingComplete.OnNext(Unit.Default);
                            BrotherPinkView.Instance.StopTasting();
                        });
                    }
                    else // 宣伝も試食も未発動
                    {
                        BrotherPinkView.Instance.StopTasting();
                        rewardButtonView.gameObject.SetActive(true);
                    }
                }
                else // 閉店
                {
                    BrotherPinkView.Instance.StopTasting();
                    rewardButtonView.gameObject.SetActive(false);
                    signBoardView.CancelTasting();
                    
                    // VIP宣伝を非表示
                    gameData.vipCustomerLimitTime = DateTime.UtcNow.ToBinary();
                    vipTimerUpdateSubject.OnNext(Unit.Default);
                }
            }).AddTo(this);
        // CombineLatest動かすのに必要
        tastingComplete.OnNext(Unit.Default);
    }

    private void SetShopView(ShopState state)
    {
#if UNITY_EDITOR
        Debug.Log($"SetShopView");
#endif
        ShopClosedCompositeDisposable.Clear();
        switch (state)
        {
            case ShopState.Open:
                BrotherPinkView.Instance.SetNormal();
                blueView.OpenAction();
                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();
                    blueView.CloseAction();
                    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();
                            Observable.Timer(TimeSpan.FromSeconds(1f))
                                .Subscribe(x => blueView.CloseAction());
                            closeSign.SetActive(true);
                        }).AddTo(ShopClosedCompositeDisposable);
                }
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(state), state, null);
        }
    }

    private void CoinEffect(int count, Action<Vector3> onComplete = null)
    {
        var effect = Instantiate(coinPrefab, Vector3.zero, Quaternion.identity, rootTransform);
        effect.GetComponentInChildren<TextMeshProUGUI>().text = count.ToString();
        this.CallWaitForSeconds(1f, () =>
        {
            onComplete?.Invoke(effect.GetChild(0).position);
            Destroy(effect.gameObject);
        });
        
    }

    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);
        // pause処理
        market.IsPause.Subscribe(x => customerAnimator.IsPause.Value = x).AddTo(customerObject);
        controller.CurrentMovingType.Subscribe(x =>
        {
            customerAnimator.SetTrigger(x, controller.DurationDelta);
        }).AddTo(customerAnimator);
        controller.IsDefaultSide.Subscribe(x =>
        {
            customerAnimator.SetSide(x);
        }).AddTo(customerAnimator);
        // フレーバー設定
        controller.WantFlavor
            .FirstOrDefault()
            .Subscribe(wantData =>
            {
                if (productDataList.FirstOrDefault(data => data.id == wantData?.FlavorId)?.GetMarketPrefab() is MarketPopcornView prefab)
                {
                    customerAnimator.SetWantFlavor(prefab, wantData.Rarity, controller.OrderCount);
                }
            }).AddTo(customerAnimator);
        controller.WantFlavor
            .SkipLatestValueOnSubscribe()
            .Subscribe(wantData =>
            {
                if (productDataList.FirstOrDefault(data => data.id == wantData.FlavorId)?.GetMarketPrefab() is MarketPopcornView prefab)
                {
                    customerAnimator.SetWantFlavor(prefab, wantData.Rarity, controller.OrderCount);
                    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()
            .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();
                SoundManager.Instance.PlaySE("se_marketing_CustomerTap");
                VibrationManager.Instance.PlayVibrationOnce();
            }).AddTo(customerAnimator);
    }
}