using Fantasy;
using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
using NBF;

#pragma warning disable CS8603 // 可能返回 null 引用。

namespace NB.Game;

public sealed class PlayerManageComponentAwakeSystem : AwakeSystem<PlayerManageComponent>
{
    protected override void Awake(PlayerManageComponent self)
    {
    }
}

public sealed class PlayerManageComponentDestroySystem : DestroySystem<PlayerManageComponent>
{
    protected override void Destroy(PlayerManageComponent self)
    {
        foreach (var (_, gameAccount) in self.Players)
        {
            gameAccount.Dispose();
        }

        self.Players.Clear();
    }
}

public static class PlayerManageComponentSystem
{
    #region 上线下线

    /// <summary>
    /// 玩家上线
    /// </summary>
    /// <param name="self"></param>
    /// <param name="accountId"></param>
    /// <param name="gateRouteId"></param>
    public static async FTask<Player?> Online(this PlayerManageComponent self, long accountId, long gateRouteId)
    {
        Log.Debug("检查账号是否在缓存中");
        if (!self.TryGet(accountId, out var account))
        {
            // 首先要先到数据库中查询是否有这个账号
            account = await PlayerHelper.LoadDataBase(self.Scene, accountId);
            bool needInit = false;
            // 如果有的话，就直接加入在缓存中就可以了
            if (account == null)
            {
                Log.Debug("检查到账号没有在数据库中，需要创建一个新的账号并且保存到数据库中");
                account = PlayerFactory.Create(self.Scene, accountId);
                needInit = true;
            }
            else
            {
                Log.Debug("检查到账号在数据库中");
            }

            await account.TryComponent<PlayerItemContainerComponent>();
            await account.TryComponent<PlayerWalletComponent>();
            await account.TryComponent<FishContainer>();

            if (needInit)
            {
                await account.InitPlayer();
            }

            Log.Debug("把当前账号添加到缓存中");
            // 把创建完成的Account放入到缓存中
            self.Add(account);
        }
        else
        {
            Log.Debug("检测到当前账号已经在缓存中了");
            //  如果有延迟下线的计划任务，那就先取消一下。
            account.CancelTimeout();
            // 如果在Gate的缓存中已经存在了该账号那只能以下几种可能:
            // 1、同一客户端发送了重复登录的请求数据。
            // 2、客户端经历的断线然后又重新连接到这个服务器上了（断线重连）。
            // 3、多个客户端同时登录了这个账号（顶号）。

            if (gateRouteId == account.SessionRunTimeId)
            {
                // 如果执行到这里，说明是客户端发送了多次登录的请求，这样的情况下，直接返回就可以了，不需要做任何操作。
                return null;
            }
        }

        // account.Statistics.LoginTime = TimeHelper.Now;
        account.SetTestData();

        await account.Save();

        return account;
    }

    public static async FTask Offline(this PlayerManageComponent self, long accountId, long sessionId)
    {
        if (self.TryGet(accountId, out var account))
        {
            if (account.SessionRunTimeId == sessionId)
            {
                await PlayerHelper.Disconnect(self.Scene, account.Id);
            }
        }
    }

    #endregion

    #region 获取&移除

    public static void Add(this PlayerManageComponent self, Player account)
    {
        self.Players.Add(account.Id, account);
    }

    public static Player Get(this PlayerManageComponent self, long accountId)
    {
        return self.Players.GetValueOrDefault(accountId);
    }

    public static bool TryGet(this PlayerManageComponent self, long accountId, out Player account)
    {
        return self.Players.TryGetValue(accountId, out account);
    }

    public static void Remove(this PlayerManageComponent self, long accountId, bool isDispose = true)
    {
        if (!self.Players.Remove(accountId, out var account))
        {
            return;
        }

        if (!isDispose)
        {
            return;
        }

        account.Dispose();
    }

    #endregion

    #region 初始化新号数据

    public static async Task InitPlayer(this Player player)
    {
        //TODO: 处理基础信息
        player.Level = 99;
        player.NickName = "王麻子";
        player.Country = "cn";
        player.Exp = 999;
        player.Head = "xxx.png";

        var list = InitConfig.GetList();


        Dictionary<ItemBasicType, Dictionary<uint, int>>
            addDic = new Dictionary<ItemBasicType, Dictionary<uint, int>>();
        foreach (var initConfig in list)
        {
            var itemType = ItemHelper.GetType(initConfig.ItemId);
            if (!addDic.TryGetValue(itemType, out var dic))
            {
                dic = new Dictionary<uint, int>();
                addDic.Add(itemType, dic);
            }

            if (!dic.ContainsKey(initConfig.ItemId))
            {
                dic.Add(initConfig.ItemId, initConfig.Amount);
            }
            else
            {
                dic[initConfig.ItemId] += initConfig.Amount;
            }
        }

        foreach (var (itemType, dictionary) in addDic)
        {
            if (itemType == ItemBasicType.Item)
            {
                var itemContainer = player.GetComponent<PlayerItemContainerComponent>();
                await itemContainer.Add(dictionary);
            }
            else if (itemType == ItemBasicType.Currency)
            {
                var playerWallet = player.GetComponent<PlayerWalletComponent>();
                foreach (var (key, value) in dictionary)
                {
                    if (value > 0)
                    {
                        await playerWallet.Add(key, value);
                    }
                    else if (value < 0)
                    {
                        await playerWallet.Sub(key, value);
                    }
                }
            }
        }
    }

    #endregion


    #region 组件

    /// <summary>
    /// 尝试获取或增加相关组件
    /// </summary>
    /// <param name="player"></param>
    /// <typeparam name="T"></typeparam>
    public static async FTask TryComponent<T>(this Player player) where T : Entity, new()
    {
        if (player.GetComponent<T>() == null)
        {
            var component = await player.Scene.World.DataBase.Query<T>(player.Id, true);
            if (component == null)
            {
                //如果没有组件
                player.AddComponent<T>();
            }
            else
            {
                player.AddComponent(component);
            }
        }
    }

    #endregion
}