using System;
using System.Collections.Generic;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public enum GameState
{
    Guide,
    Playing,
    Result
}

public class PopcornGameManager : MonoBehaviour
{
    // View
    [SerializeField] private PopcornGameView gameView;
    [SerializeField] private GameObject perfectResultObject;
    [SerializeField] private GameObject goodResultObject;
    [SerializeField] private GameObject failureResultObject;
    
    [SerializeField] private ThermalControl thermalControl;
    [SerializeField] private ThermoMeter thermoMeter;
    [SerializeField] private CornManager cornManager;
    
#if DEVELOPMENT_BUILD || UNITY_EDITOR
    [SerializeField] private Button resetButton;
#endif

    public IReadOnlyReactiveProperty<GameState> State => state;
    private readonly ReactiveProperty<GameState> state = new ReactiveProperty<GameState>(GameState.Guide);
    private readonly CompositeDisposable compositeDisposable = new CompositeDisposable();
    
    // Start is called before the first frame update
    void Start()
    {
        compositeDisposable.AddTo(this);

        thermalControl.Condition.Subscribe(x =>
        {
            cornManager.ChangeGrowSpeed(x);
            gameView.ChangeCharactorState(x);
        }).AddTo(this);

        state.Subscribe(x => cornManager.GameState.Value = x);

        ResetGame();
        
#if DEVELOPMENT_BUILD || UNITY_EDITOR
        resetButton.OnClickAsObservable().Subscribe(_ =>
        {
            ResetGame();
        }).AddTo(this);
#endif

#if UNITY_EDITOR
        this.UpdateAsObservable()
            .Where(_ => Input.GetKeyDown(KeyCode.R))
            // 連打防止
            .ThrottleFirst(TimeSpan.FromSeconds(0.1f))
            .Subscribe(_ => UnityEngine.SceneManagement.SceneManager.LoadScene("Cooking"))
            .AddTo(this);
        
        this.UpdateAsObservable()
            .Where(_ => Input.GetKeyDown(KeyCode.E))
            // 連打防止
            .ThrottleFirst(TimeSpan.FromSeconds(0.1f))
            .Subscribe(_ => ResetGame())
            .AddTo(this);

        cornManager.Result
            .SkipLatestValueOnSubscribe()
            // .DistinctUntilChanged()
            .Materialize()
            .Subscribe(x => { Debug.Log(x); }).AddTo(this);
#endif
    }

    private void ResetGame()
    {
        compositeDisposable.Clear();
        cornManager.Result.SkipLatestValueOnSubscribe()
            .FirstOrDefault()
            .DelayFrame(1) // Disposeタイミング調整
            .Subscribe(x =>
            {
                state.Value = GameState.Result;
                thermalControl.StopMeter();
                gameView.ChangeCharactorState(ThermalCondition.Cold);
                gameView.ChangeFireState(false);
                gameView.ChangeStreamState(true);
                // リザルト表示遅延
                this.CallWaitForSeconds(1.2f, () =>
                {
                    SetResult(x);
                    if (x != CornResult.Failure)
                    {
                        AddStock();
                    }
                    // 画面タップで次へ
                    this.UpdateAsObservable()
                        .Select(_ => Input.GetMouseButton(0))
                        .DistinctUntilChanged()
                        .Skip(1)
                        .FirstOrDefault(b => b)
                        .DelaySubscription(TimeSpan.FromSeconds(1f))
                        .Subscribe(_ =>
                        {
                            // ResetGame();
                            TransitionManager.Instance.LoadScene(GameScenes.Main);
                        }).AddTo(compositeDisposable);
                });
            }).AddTo(compositeDisposable);
        
        this.UpdateAsObservable()
            .Select(_ => Input.GetMouseButton(0))
            .DistinctUntilChanged()
            .Skip(1)
            .FirstOrDefault(b => b)
            .Subscribe(_ =>
            {
                StartGame();
            }).AddTo(compositeDisposable);
        
        ResetUI();
        thermalControl.ResetMeter();
        cornManager.RespawnCorn();
        state.Value = GameState.Guide;
    }
    
    private void ResetUI()
    {
        gameView.ResetUI();
        perfectResultObject.SetActive(false);
        goodResultObject.SetActive(false);
        failureResultObject.SetActive(false);
        
        thermalControl.StopMeter();
        thermoMeter.gameObject.SetActive(false);
    }

    private void StartGame()
    {
        gameView.ChangeGuideState(false);
        gameView.ChangeFireState(true);
        thermoMeter.gameObject.SetActive(true);
        thermalControl.StartMeter();
        state.Value = GameState.Playing;
    }

    private void SetResult(CornResult result)
    {
        switch (result)
        {
            case CornResult.Perfect:
                perfectResultObject.SetActive(true);
                break;
            case CornResult.Good:
                goodResultObject.SetActive(true);
                break;
            case CornResult.Failure:
                failureResultObject.SetActive(true);
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(result), result, null);
        }
    }

    private void AddStock()
    {
        var recipe = LocalCacheManager.Load<RecipeData>(RecipeDetailView.DetailRecipeTag);
        var gameData = GameDataManager.GameData;
        // 店頭の空きに追加
        var remain = recipe.Volume;
        var shopSpace = Market.ShopStockCount - gameData.ShopStock.Count;
        if (shopSpace > 0)
        {
            var stockCount = Mathf.Min(shopSpace, remain);
            for (int i = 0; i < stockCount; i++)
            {
                gameData.ShopStock.Add(recipe.RecipeId);
            }
            remain -= stockCount;
        }
        if (remain == 0)
        {
            GameDataManager.SaveGameData();
            return;
        }
        // 同じフレーバーのタンクを確認
        var index = gameData.StorageTanks.FindIndex(x => x.FlavorId == recipe.RecipeId);
        if (index != -1)
        {
            // 同じフレーバーのタンクの空きに追加
            var tank = gameData.StorageTanks[index];
            var tankSpace = tank.Capacity - tank.Stock;
            if (tankSpace > 0)
            {
                var stockCount = Mathf.Min(tankSpace, remain);
                tank.Stock += stockCount;
                gameData.StorageTanks[index] = tank;
                remain -= stockCount;
            }
        }
        if (remain == 0)
        {
            GameDataManager.SaveGameData();
            return;
        }
        // 空のタンクに追加
        var emptyTankIndex = gameData.StorageTanks.FindIndex(x => x.Stock == 0);
        if (emptyTankIndex != -1)
        {
            gameData.StorageTanks[emptyTankIndex].FlavorId = recipe.RecipeId;
            gameData.StorageTanks[emptyTankIndex].Stock = remain;
            GameDataManager.SaveGameData();
            return;
        }
    }
}
