using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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,
        2000,
        3000,
        4000,
        5000
    };
    
    private void Start()
    {
        state.AddTo(this);
        state.Value = ManagementState.None;

        Market.Instance.IsPause.Value = true;
        var gameData = GameDataManager.GameData;
        CoinManager.Instance.ChangeCoin(gameData.Coin);
        HeartMeter.Instance.Initialize(gameData.ViewedShopLevel, gameData.Heart);
        GameDataObserver.Instance.ShopLevelUpdate
            .Where(x => x)
            .Subscribe(x =>
            {
                LocalCacheManager.Save(ShopLevelUp.DataTag, gameData.ViewedShopLevel + 1);
                TransitionManager.Instance.LoadSceneAdditive(GameScenes.StoreLevel);
            }).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);
        foreach (var tankView in tankList)
        {
            tankView.SetArrow(tankView.TankNumber != 4 && tankView.TankNumber != 7 && tankView.TankNumber != gameData.StorageTanks.Count);
            if (tankView.TankNumber > gameData.StorageTanks.Count)
            {
                // 価格の一番小さいタンクのみ購入できるようにする
                if (tankView.TankNumber == gameData.StorageTanks.Count + 1)
                {
                    tankView.SetState(TankState.Lock);
                }
                else
                {
                    tankView.SetState(TankState.Unavailable);
                }
                // タンク購入
                var shopData = new ShopData
                {
                    nameId = 0,
                    name = "タンク",
                    price = PriceList[tankView.TankNumber - 1],
                    text = "ポップコーンを保存する容器",
                    category = 3,
                    consumeType = 2,
                    prefabName = "tank"
                };
                tankView.PurchaseButtonObservable
                    .Where(_ => shopData.price <= CoinManager.Instance.OwnCoin)
                    .TakeWhile(_ => tankView.State.Value == TankState.Lock || tankView.State.Value == TankState.Unavailable)
                    .Subscribe(_ =>
                    {
                        Action<int> purchaseAction = amount =>
                        {
                            // タンク追加
                            gameData.StorageTanks.Add(new StorageTank() {Capacity = Const.TankCapacity});
                            tankView.SetData(gameData.StorageTanks[tankView.TankNumber - 1]);
                            tankView.SetState(TankState.Unlock);
                            CoinManager.Instance.SubCoin(shopData.price);
                            gameData.Coin = CoinManager.Instance.OwnCoin;
                            GameDataManager.SaveGameData();
                            // 次に大きいタンクの購入ボタンを表示
                            if (tankView.TankNumber + 1 <= tankList.Count)
                            {
                                tankList[tankView.TankNumber].SetState(TankState.Lock);
                            }
                        };
                        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);
                    ClearSelectedTank(tankList);
                    break;
                case ManagementState.Replacing:
                    break;
                case ManagementState.Tasting:
                    // 空以外のタンクを選択
                    var index = gameData.StorageTanks.FindIndex(tank => tank.Stock > 0);
                    if (index == -1)
                    {
                        state.Value = ManagementState.None;
                        return;
                    }
                    ChangeAllTankInteractable(tankList, false);
                    ClearSelectedTank(tankList);
                    tankList[index].SetState(TankState.Selected);
                    var tankData = gameData.StorageTanks[index];
                    selectedTank.Value = (tankList[index].TankNumber, productList.FirstOrDefault(data => data.id == tankData.FlavorId));
                    Action tastingAction = () =>
                    {
                        Tasting(selectedTank.Value.num);
                        tankList[selectedTank.Value.num - 1].SetData(gameData.StorageTanks[selectedTank.Value.num - 1]);
                    };
                    LocalCacheManager.Save(TastingView.TastingDataTag, (selectedTank.AsObservable(), new Action(() => state.Value = ManagementState.None), tastingAction));
                    TransitionManager.Instance.LoadSceneAdditive(GameScenes.Tasting);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(mState), mState, null);
            }
        }).AddTo(this);
        
        // タンククリック検知
        Observable.Merge(tankList.Select(tankView => tankView.TankClickObservable.Select(_ => tankView)))
            .Where(tankView => tankView.State.Value != TankState.Lock)
            .Where(tankView => tankView.State.Value != TankState.Unavailable)
            .Where(tankView => gameData.StorageTanks[tankView.TankNumber - 1].Stock > 0)
            .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:
                        ClearSelectedTank(tankList);
                        tankView.SetState(TankState.Selected);
                        var tankData = gameData.StorageTanks[tankView.TankNumber - 1];
                        selectedTank.Value = (tankView.TankNumber, productList.FirstOrDefault(data => data.id == tankData.FlavorId));
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }).AddTo(this);
        
        // 2連続でクリックされたViewがまとめて取得できるストリーム
        var replacingObservable = Observable.Merge(tankList.Select(x => x.TankClickObservable.Select(_ => x)))
            .Where(x => x.State.Value != TankState.Lock)
            .Where(x => x.State.Value != TankState.Unavailable)
            .Where(tankView => gameData.StorageTanks[tankView.TankNumber - 1].Stock > 0)
            .TakeWhile(_ => state.Value != ManagementState.Tasting)
            .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();
                }
                state.Value = ManagementState.None;
            }).AddTo(this);
        
        var allTankStock = gameData.StorageTanks.Sum(x => x.Stock);
        tastingDisabledObject.SetActive(allTankStock == 0);
        tastingButton.interactable = allTankStock != 0;
        tastingButton.OnClickAsObservable().Subscribe(_ =>
        {
            state.Value = ManagementState.Tasting;
        }).AddTo(this);
        
        view.RefillType.Subscribe(mode =>
        {
            if (state.Value != ManagementState.Tasting)
            {
                state.Value = ManagementState.None;
            }
            // 補充方法変更
            gameData.RefillMode = mode;
            GameDataManager.SaveGameData();
        }).AddTo(this);
    }

    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)
    {
        state.Value = ManagementState.None;
        // 試食フラグに終了時間を設定
        var tank = GameDataManager.GameData.StorageTanks[tankNumber - 1];
        GameDataManager.GameData.AddTastingCustomerCount(tank.Stock);
        tank.ClearStock();
        GameDataManager.SaveGameData();
    }

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