using System;
using System.Collections.Generic;
using System.Linq;
using UniRx;
using UnityEngine;

public class GameDataObserver : SingletonMonoBehaviour<GameDataObserver>
{
    [SerializeField] private FloatReactiveProperty checkInterval = new FloatReactiveProperty(.2f);
    private readonly Subject<int> heartObserver = new Subject<int>();
    private readonly Subject<int> sceneCounter = new Subject<int>();
    private readonly Subject<int> shopLevelObserver = new Subject<int>();
    private readonly Subject<int> achievedMissionObserver = new Subject<int>();
    private readonly Subject<int> addCoinObserver = new Subject<int>();
    private readonly Subject<int> subCoinObserver = new Subject<int>();
    private readonly Subject<int> addCornObserver = new Subject<int>();
    private readonly Subject<int> addCustomerObserver = new Subject<int>();
    private readonly Subject<int> addSalesObserver = new Subject<int>();
    private readonly Subject<int> addRareSalesObserver = new Subject<int>();
    private readonly Subject<int> hasRecipeObserver = new Subject<int>();
    private readonly Subject<int> deliveredBulkOrderObserver = new Subject<int>();
    private readonly Subject<Unit> updateMissionProgress = new Subject<Unit>();
    private readonly BoolReactiveProperty updateMissionAchieved = new BoolReactiveProperty();
    public IReadOnlyReactiveProperty<bool> UpdateMissionAchieved => updateMissionAchieved;

    public IObservable<int> HeartObserver => heartObserver;
    public IObservable<int> SceneCounter => sceneCounter.DistinctUntilChanged();

    private void Start()
    {
        checkInterval.AddTo(this);
        updateMissionAchieved.AddTo(this);
        var levelList = SpreadsheetDataManager.Instance.GetBaseDataList<ShopLevelData>(Const.ShopLevelDataSheet);
        levelList = levelList.Where(data => data.shopLevel != Const.SpecialShopLevel).ToList();
        checkInterval
            .Select(x => Observable.Interval(TimeSpan.FromSeconds(x))).Switch()
            .Subscribe(_ =>
            {
                var gameData = GameDataManager.GameData;
                heartObserver.OnNext(gameData.Heart);
                sceneCounter.OnNext(UnityEngine.SceneManagement.SceneManager.sceneCount);
                shopLevelObserver.OnNext(gameData.ViewedShopLevel);
                achievedMissionObserver.OnNext(gameData.AchievedMission.Count);
                // addCoinObserver.OnNext(gameData.TotalAddCoin);
                subCoinObserver.OnNext(gameData.TotalSubCoin);
                addCornObserver.OnNext(gameData.TotalAddCornSeed);
                addCustomerObserver.OnNext(gameData.TotalCustomerCount);
                addSalesObserver.OnNext(gameData.TotalSales);
                addRareSalesObserver.OnNext(gameData.RareTotalSales);
                hasRecipeObserver.OnNext(gameData.MyRecipes.Length);
                deliveredBulkOrderObserver.OnNext(gameData.BulkOrderDeliveredCount);
            }).AddTo(this);
        
        // ミッション達成チェッカー
        var missionList = SpreadsheetDataManager.Instance.GetBaseDataList<MissionData>(Const.MissionDataSheet);
        var normalMissionList = missionList.Where(data => data.Category == MissionCategory.Normal).ToList();
        var latestMissionList = GetLatestMissionList();
        // 各条件の最前の未達成のミッションのみにフィルタ
        List<MissionData> GetLatestMissionList() => normalMissionList
            .Where(data => data.shopLevel <= GameDataManager.GameData.ViewedShopLevel)
            .Where(data => !GameDataManager.GameData.AchievedMission.Contains(data.id))
            .GroupBy(data => data.Condition, (_, list) => list.FirstOrDefault()).ToList();
        // 店舗レベルアップorミッション達成でリスト更新して達成チェック
        Observable.Merge(
            shopLevelObserver.DistinctUntilChanged(),
            achievedMissionObserver.DistinctUntilChanged(),
            updateMissionProgress.Select(_ => 0))
            .Subscribe(_ =>
            {
                latestMissionList = GetLatestMissionList();
                // ここで達成を再確認
                updateMissionAchieved.Value = latestMissionList
                    .Any(data => data.count <= MissionManager.GetProgressValue(data));

                // dailyMission
                var ids = GameDataManager.GameData.dailyMissionIdArray.Except(GameDataManager.GameData.dailyMissionAchievedIdArray);
                if (missionList
                    .Where(data => ids.Contains(data.id))
                    .Any(missionData => missionData.count <= MissionManager.GetDailyProgressValue(missionData)))
                {
                    updateMissionAchieved.Value = true;
                }
                
                // CheckDailyCompleteMission
                if (!GameDataManager.GameData.dailyMissionCompleted)
                {
                    if (GameDataManager.GameData.dailyMissionIdArray.Length <= GameDataManager.GameData.dailyMissionAchievedIdArray.Length)
                    {
                        updateMissionAchieved.Value = true;
                    }
                }
            });
        
        // 各種ポップコーン以外の達成を確認
        Observable.Merge(
                subCoinObserver.DistinctUntilChanged().Select(_ => MissionCondition.CoinConsumeCount),
                addCornObserver.DistinctUntilChanged().Select(_ => MissionCondition.CornCount),
                addCustomerObserver.DistinctUntilChanged().Select(_ => MissionCondition.CustomerCount),
                hasRecipeObserver.DistinctUntilChanged().Select(_ => MissionCondition.RecipeCount),
                deliveredBulkOrderObserver.DistinctUntilChanged().Select(_ => MissionCondition.BulkOrderDeliveredCount))
            .Subscribe(condition =>
            {
                if (latestMissionList
                    .Where(data => data.Condition == condition)
                    .Any(data => data.count <= MissionManager.GetProgressValue(data)))
                {
                    updateMissionAchieved.Value = true;
                }
                
                // dailyMission
                var ids = GameDataManager.GameData.dailyMissionIdArray.Except(GameDataManager.GameData.dailyMissionAchievedIdArray);
                if (missionList
                    .Where(data => ids.Contains(data.id))
                    .Any(missionData => missionData.count <= MissionManager.GetDailyProgressValue(missionData)))
                {
                    updateMissionAchieved.Value = true;
                }
            });

        // ポップコーン販売達成確認
        // CheckAvailableMissionは行わない
        Observable.Merge(
                addSalesObserver.DistinctUntilChanged(),
                addRareSalesObserver.DistinctUntilChanged())
            .Subscribe(_ =>
            {
                if (latestMissionList
                    .Any(data => data.count <= MissionManager.GetProgressValue(data)))
                {
                    updateMissionAchieved.Value = true;
                }
                
                // dailyMission
                var ids = GameDataManager.GameData.dailyMissionIdArray.Except(GameDataManager.GameData.dailyMissionAchievedIdArray);
                if (missionList
                    .Where(data => ids.Contains(data.id))
                    .Any(missionData => missionData.count <= MissionManager.GetDailyProgressValue(missionData)))
                {
                    updateMissionAchieved.Value = true;
                }
            });

        // デイリーミッション日付チェック
        sceneCounter
            .Where(x => x <= 1)
            .Subscribe(x =>
            {
                var gameData = GameDataManager.GameData;
                if (gameData.ViewedShopLevel == 0)
                {
                    return;
                }

                var lastSetDateUtc = DateTime.FromBinary(gameData.lastDailyMissionSetTime);
                // 現時刻が最後にデイリーミッションを設定した日より前の場合無視(時間操作対策)
                if (lastSetDateUtc > DateTime.UtcNow)
                {
#if UNITY_EDITOR
                    Debug.Log($"不正な時刻 {lastSetDateUtc:s}");
#endif
                    return;
                }

                // ミッションリセットチェック(0時)
                if (gameData.GetDailyMissionLimitTime().TotalSeconds > 0)
                {
                    return;
                }
#if UNITY_EDITOR
                Debug.Log($"reset daily mission");
#endif
                // ミッション実績リセット
                gameData.dailyMissionAchievedIdArray = Array.Empty<int>();
                gameData.dailyMissionCompleted = false;
                gameData.ResetDailyTotalCount();
                // 新ミッション登録
                var targetList = missionList
                    .Where(data => data.Category == MissionCategory.Daily && data.shopLevel <= gameData.ViewedShopLevel)
                    .Where(data => MissionManager.CheckAvailableMission(data, GameDataManager.GameData.MyRecipes))
                    .ToList();
                var staticMissionConditions = new[]
                {
                    MissionCondition.UseFertilizer,
                    MissionCondition.UseAdWalker,
                    MissionCondition.UseAutoCook,
                };
                var staticMissionList = targetList
                    .Where(data => staticMissionConditions.Contains(data.Condition))
                    .ToList();
                gameData.dailyMissionIdArray = targetList
                    .Where(data => !staticMissionConditions.Contains(data.Condition))
                    .ToList()
                    .RandomChoose(2)
                    .Append(staticMissionList.RandomChoose()) // 固定ミッション広告視聴
                    .Select(data => data.id)
                    .ToArray();
                gameData.lastDailyMissionSetTime = DateTime.UtcNow.ToBinary();
                gameData.dailyLoginCount++;
                GameDataManager.SaveGameData();
            });
    }
    
    public void CheckMissionProgress(){
        updateMissionProgress.OnNext(Unit.Default);
    }
}
