using System;
using System.Collections.Generic;
using System.Linq;
using MyGame.Scripts;
using UniRx;
using UnityEngine;
using UnityEngine.UI;

public enum ManagementState
{
    None,
    Replacing,
    Tasting,
}
public class ProductManagement : MonoBehaviour
{
    [SerializeField] private Button tastingButton;
    [SerializeField] private GameObject tastingDisabledObject;
    [SerializeField] private ProductManagementView view;
    private readonly ReactiveProperty<ManagementState> state = new ReactiveProperty<ManagementState>();
    
    // tank price list
    private static readonly List<int> PriceList = new List<int>
    {
        0,
        500,
        1000,
        3000,
        5000,
        7000,
        9000
    };
    
    private void Start()
    {
        state.AddTo(this);
        state.Value = ManagementState.None;

        Market.Instance.IsPause.Value = true;
        var gameData = GameDataManager.GameData;
        
        if (!gameData.FinishedFlags.HasFlag(TutorialFlag.Management))
        {
            TutorialManager.Instance.ShowTutorialConversation(9, () =>
            {
                gameData.FinishedFlags |= TutorialFlag.Management;
                GameDataManager.SaveGameData();
            });
        }
        
        CoinManager.Instance.ChangeCoin(gameData.Coin);
        HeartMeter.Instance.Initialize(gameData.ViewedShopLevel, gameData.Heart);
        
        // ハートゲージがフルかつダイアログが開いていない場合レベルアップ
        HeartMeter.Instance.FulledHeart
            .DelaySubscription(TimeSpan.FromSeconds(.4f))
            .Subscribe(_ =>
            {
                ShopLevelUp.ShowDialog(gameData.ViewedShopLevel + 1);
            }).AddTo(this);

        GameDataObserver.Instance.HeartObserver
            .DistinctUntilChanged()
            .Pairwise()
            .Subscribe(pair =>
            {
                HeartMeter.Instance.AddHeart(pair.Current - pair.Previous);
            }).AddTo(this);

        // 補充方法設定
        view.SetRefillType(gameData.RefillMode);

        // タンク並び替え
        gameData.StorageTanks = gameData.StorageTanks.OrderBy(tank => tank.IsEmpty).ToList();

        // タンク設定
        var tankList = view.SetTanks(PriceList);
        var rarityDataList = SpreadsheetDataManager.Instance.GetBaseDataList<RarityData>(Const.RarityDataSheet);
        var productList = SpreadsheetDataManager.Instance.GetBaseDataList<ProductData>(Const.ProductDataSheet);
        var shopDataList = SpreadsheetDataManager.Instance.GetBaseDataList<ShopData>(Const.ShopDataSheet);
        foreach (var tankView in tankList)
        {
            tankView.SetArrow(tankView.TankNumber != 4 && tankView.TankNumber != 7 && tankView.TankNumber != gameData.StorageTanks.Count);
            if (tankView.TankNumber > gameData.StorageTanks.Count)
            {
                // 価格の一番小さいタンクのみ購入できるようにする
                var isSmallest = tankView.TankNumber == gameData.StorageTanks.Count + 1;
                tankView.SetState(isSmallest ? TankState.Lock : TankState.Unavailable);
                // タンク購入
                var shopData = shopDataList.First(data => data.id == Const.ShopTankId);
                var price = PriceList[tankView.TankNumber - 1];
                
                // 購入可否
                tankView.SetCanBuy(price <= CoinManager.Instance.OwnCoin);
                
                tankView.PurchaseButtonObservable
                    .Where(_ => price <= CoinManager.Instance.OwnCoin)
                    .TakeWhile(_ => tankView.State.Value == TankState.Lock || tankView.State.Value == TankState.Unavailable)
                    .Subscribe(_ =>
                    {
                        void PurchaseAction(int amount)
                        {
                            // タンク追加
                            gameData.StorageTanks.Add(new StorageTank { Capacity = Const.TankCapacity });
                            tankView.SetData(gameData.StorageTanks[tankView.TankNumber - 1]);
                            tankView.SetState(TankState.Unlock);
                            CoinManager.Instance.SubCoin(price);
                            gameData.Coin = CoinManager.Instance.OwnCoin;
                            GameDataManager.SaveGameData();
                            // 次に大きいタンクの購入ボタンを表示
                            if (tankView.TankNumber + 1 <= tankList.Count)
                            {
                                tankList[tankView.TankNumber].SetState(TankState.Lock);
                                tankList[tankView.TankNumber].SetCanBuy(PriceList[tankView.TankNumber] <= CoinManager.Instance.OwnCoin);
                            }
                        }

                        shopData.price = price;
                        ShopItemPurchaseView.ShowDialog((shopData, 0), PurchaseAction);
                    }).AddTo(tankView);
            }
            else
            {
                tankView.SetData(gameData.StorageTanks[tankView.TankNumber - 1]);
                tankView.SetState(TankState.Unlock);
            }
            tankView.DetailButtonObservable.Subscribe(_ =>
            {
                state.Value = ManagementState.None;
                var tankData = gameData.StorageTanks[tankView.TankNumber - 1];
                var shopStock = gameData.ShopStock.Count(data => data.FlavorId == tankData.FlavorId);
                var tankStock = gameData.StorageTanks.Where(x => x.FlavorId == tankData.FlavorId).Sum(x => x.Stock);
                var totalStock = shopStock + tankStock;
                var productData = productList.FirstOrDefault(data => data.id == tankData.FlavorId);
                List<(ProductRarity rarity, int price, int stock)> raritySummaryData = rarityDataList.Select(x =>
                    (x.Rarity, productData?.GetRarityPrice(x.Rarity) ?? 0, tankData.Stocks.FirstOrDefault(sd => sd.Rarity == x.Rarity)?.Stock ?? 0)
                ).ToList();
                LocalCacheManager.Save(TankDetailView.TankDetailDataTag, (tankView.TankNumber, totalStock, tankData, productData, raritySummaryData));
                TransitionManager.Instance.LoadSceneAdditive(GameScenes.PopcornDescription);
            }).AddTo(tankView);
            state.Subscribe(x =>
            {
                tankView.SetDetailButtonActive(x != ManagementState.Tasting);
            }).AddTo(tankView);
        }

        var selectedTank = new ReactiveProperty<(int num, ProductData productData)>().AddTo(this);
        state.Subscribe(mState =>
        {
            switch (mState)
            {
                case ManagementState.None:
                    ChangeAllTankInteractable(tankList, true);
                    break;
                case ManagementState.Replacing:
                    break;
                case ManagementState.Tasting:
                    // ストックのあるタンクのIndex取得。なければ試食モード中止。
                    var index = gameData.StorageTanks.FindIndex(tank => !tank.IsEmpty);
                    if (index == -1)
                    {
                        state.Value = ManagementState.None;
                        return;
                    }
                    // 選択中のタンクにストックがあればIndex更新
                    var selectedIndex = tankList.FindIndex(tankView => tankView.State.Value == TankState.Selected);
                    if (selectedIndex != -1 && !gameData.StorageTanks[selectedIndex].IsEmpty)
                    {
                        index = selectedIndex;
                    }

                    ChangeAllTankInteractable(tankList, false);
                    ClearSelectedTank(tankList);
                    tankList[index].SetState(TankState.Selected);

                    var tankData = gameData.StorageTanks[index];
                    selectedTank.Value = (index + 1, productList.FirstOrDefault(data => data.id == tankData.FlavorId));
                    void TastingAction()
                    {
                        state.Value = ManagementState.None;
                        Tasting(selectedTank.Value.num);
                        tankList[selectedTank.Value.num - 1].SetData(gameData.StorageTanks[selectedTank.Value.num - 1]);
                        SetActiveTastingButton(gameData.StorageTanks.Any(tank => !tank.IsEmpty));
                    }
                    TastingView.ShowDialog(selectedTank.AsObservable(), () => state.Value = ManagementState.None, TastingAction);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(mState), mState, null);
            }
        }).AddTo(this);
        
        // タンククリック検知
        var tankClickStream = tankList.Select(tankView => tankView.TankClickObservable.Select(_ => tankView)).Merge(); 
        tankClickStream
            .Where(tankView => tankView.State.Value != TankState.Lock)
            .Where(tankView => tankView.State.Value != TankState.Unavailable)
            .Subscribe(tankView =>
            {
                switch (state.Value)
                {
                    case ManagementState.None:
                        ClearSelectedTank(tankList);
                        tankView.SetState(TankState.Selected);
                        state.Value = ManagementState.Replacing;
                        break;
                    case ManagementState.Replacing:
                        break;
                    case ManagementState.Tasting:
                        var tankData = gameData.StorageTanks[tankView.TankNumber - 1];
                        if (tankData.IsEmpty)
                        {
                            break;
                        }
                        ClearSelectedTank(tankList);
                        tankView.SetState(TankState.Selected);
                        selectedTank.Value = (tankView.TankNumber, productList.FirstOrDefault(data => data.id == tankData.FlavorId));
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }).AddTo(this);
        
        // 2連続でクリックされたViewがまとめて取得できるストリーム
        var replacingObservable = tankClickStream
            .TakeWhile(_ => state.Value != ManagementState.Tasting)
            .Where(tankView => tankView.State.Value != TankState.Lock)
            .Pairwise();
        // タンク入れ替え
        state.Where(x => x == ManagementState.None)
            .Select(_ => replacingObservable)
            .Switch()
            .Subscribe(tanks =>
            {
                if (tanks.Previous.TankNumber != tanks.Current.TankNumber)
                {
                    var tmpData = gameData.StorageTanks[tanks.Previous.TankNumber - 1];
                    gameData.StorageTanks[tanks.Previous.TankNumber - 1] = gameData.StorageTanks[tanks.Current.TankNumber - 1];
                    gameData.StorageTanks[tanks.Current.TankNumber - 1] = tmpData;
                    // 再設定
                    tanks.Previous.SetData(gameData.StorageTanks[tanks.Previous.TankNumber - 1]);
                    tanks.Current.SetData(gameData.StorageTanks[tanks.Current.TankNumber - 1]);
                    GameDataManager.SaveGameData();
                }
                ClearSelectedTank(tankList);
                state.Value = ManagementState.None;
            }).AddTo(this);
        
        // 空以外のタンクが1つでもあれば有効
        SetActiveTastingButton(gameData.StorageTanks.Any(tank => !tank.IsEmpty));
        // 試食ボタン
        tastingButton.OnClickAsObservable().Subscribe(_ =>
        {
            state.Value = ManagementState.Tasting;
        }).AddTo(this);
        
        // 商品補充タイプ切替
        view.RefillType.Subscribe(mode =>
        {
            // 補充方法変更
            gameData.RefillMode = mode;
            GameDataManager.SaveGameData();
        }).AddTo(this);
    }

    private void SetActiveTastingButton(bool active)
    {
        tastingDisabledObject.SetActive(!active);
        tastingButton.gameObject.SetActive(active);
        tastingButton.interactable = active;
    }

    private void ClearSelectedTank(List<ProductManagementTankView> tankList)
    {
        foreach (var tankView in tankList)
        {
            if (tankView.State.Value == TankState.Selected)
            {
                tankView.SetState(TankState.Unlock);
            }
        }
    }

    private void ChangeAllTankInteractable(List<ProductManagementTankView> tankList, bool active)
    {
        foreach (var tankView in tankList)
        {
            if (tankView.State.Value == TankState.Lock)
            {
                tankView.SetPurchaseButtonActive(active);
            }
        }
    }

    private void Tasting(int tankNumber)
    {
        // 試食フラグに終了時間を設定
        var tank = GameDataManager.GameData.StorageTanks[tankNumber - 1];
        GameDataManager.GameData.AddTastingCustomerCount(tank.Stock);
        tank.ClearStock();
        GameDataManager.SaveGameData();
        BrotherPinkView.Instance.StartTasting();
    }

    private void OnDestroy()
    {
        Market.Instance.IsPause.Value = false;
    }
}
