// Copyright 2018 RED Software, LLC. All Rights Reserved.

using IgniteEngine;
using IgniteEngine.Networking;
using IgniteEngine.Protocols;
using LoginServer.Services;

namespace LoginServer.Handlers
{
    internal static class UserHandlers
    {
        internal static byte[] XTrapKey = {0x33, 0x33, 0x42, 0x35, 0x34, 0x33, 0x42, 0x30, 0x43, 0x41, 0x36, 0x45, 0x37, 0x43, 0x34, 0x31, 0x45, 0x35, 0x44, 0x31, 0x44, 0x30, 0x36, 0x35, 0x31, 0x33, 0x30, 0x37, 0x0};

        internal static void NC_USER_CLIENT_VERSION_CHECK_REQ(NetworkMessage message, NetworkConnection connection)
        {
            var versionKey = message.ReadString(64);

            if (versionKey != "7affa29673ebd9ea5e2eed64413aba9c")
            {
                Debug.Log($"Wrong version key used - {versionKey}");

                new PROTO_NC_USER_CLIENT_WRONGVERSION_CHECK_ACK().Send(connection);
                return;
            }

            new PROTO_NC_USER_CLIENT_RIGHTVERSION_CHECK_ACK((byte) XTrapKey.Length, XTrapKey).Send(connection);
        }

        internal static void NC_USER_US_LOGIN_REQ(NetworkMessage message, NetworkConnection connection)
        {
            var userName = message.ReadString(260);
            var password = message.ReadString(36);

            AccountService.Login(userName, password, out var userNo, out var blocked, out var canLogin);

            if (userNo == -1)
            {
                Debug.LogWarning($"[{connection}] Invalid credentials");

                new PROTO_NC_USER_LOGINFAIL_ACK(0x45).Send(connection);
				new PROTO_NC_LOG_USER_LOGINFAIL(userName, password, connection.GetRemoteIP, 0x45).Send(Program.GameLogServer);
                return;
            }

            if (blocked)
            {
                Debug.LogWarning($"[{connection}] Logging in while banned");

                new PROTO_NC_USER_LOGINFAIL_ACK(0x47).Send(connection);
	            new PROTO_NC_LOG_USER_LOGINFAIL(userName, password, connection.GetRemoteIP, 0x47).Send(Program.GameLogServer);
				return;
            }

            if (!canLogin)
            {
                Debug.LogWarning($"[{connection}] Logging in during maintenance");

                new PROTO_NC_USER_LOGINFAIL_ACK(0x48).Send(connection);
	            new PROTO_NC_LOG_USER_LOGINFAIL(userName, password, connection.GetRemoteIP, 0x48).Send(Program.GameLogServer);
				return;
            }

            connection.Account = new Account(userNo, userName);
            new PROTO_NC_USER_LOGIN_ACK(Program.Worlds).Send(connection);
		}

        internal static void NC_USER_XTRAP_REQ(NetworkMessage message, NetworkConnection connection)
        {
            var xtrapClientKeyLength = message.ReadByte();
            var xtrapClientKey = message.ReadBytes(xtrapClientKeyLength);

            new PROTO_NC_USER_XTRAP_ACK(xtrapClientKey.Compare(XTrapKey)).Send(connection);
        }

        internal static void NC_USER_WORLD_STATUS_REQ(NetworkMessage message, NetworkConnection connection)
        {
            new PROTO_NC_USER_WORLD_STATUS_ACK(Program.Worlds).Send(connection);
        }

        internal static void NC_USER_WORLDSELECT_REQ(NetworkMessage message, NetworkConnection connection)
        {
            var worldNo = message.ReadByte();
            var world = Program.Worlds.First(w => w.Number == worldNo);

            if (world == null)
            {
                new PROTO_NC_USER_LOGINFAIL_ACK(0x42).Send(connection);
	            new PROTO_NC_LOG_USER_LOGINFAIL(connection.Account.Username, null, connection.GetRemoteIP, 0x42).Send(Program.GameLogServer);
				return;
            }

            var transfer = new NetworkTransfer(connection.Account, connection.GUID)
            {
                Connection = connection,
                World = world
            };

            Program.Transfers.Add(transfer);
            new PROTO_NC_USER_WILLLOGIN_REQ(connection.Account, connection.GUID).Send(world.Connection);
			new PROTO_NC_LOG_USER_LOGIN(connection.Account.UserNo, worldNo, connection.GetRemoteIP).Send(Program.GameLogServer);
		}

        internal static void NC_USER_NORMALLOGOUT_CMD(NetworkMessage message, NetworkConnection connection)
        {
            Object.Destroy(connection);
        }

        internal static void NC_USER_LOGIN_WITH_OTP_REQ(NetworkMessage message, NetworkConnection connection)
        {
            var guid = message.ReadString(32);

            if (!Program.CachedAccounts.TryGetValue(guid, out var account))
            {
                new PROTO_NC_USER_LOGINFAIL_ACK(0x49).Send(connection);
                return;
            }

            Program.CachedAccounts.Remove(guid);

            connection.Account = account;
            new PROTO_NC_USER_LOGIN_ACK(Program.Worlds).Send(connection);
        }

        internal static void NC_USER_WILLLOGIN_ACK(NetworkMessage message, NetworkConnection connection)
        {
            var guid = message.ReadString(32);
            var ok = message.ReadBoolean();

            var transfer = Program.Transfers.First(t => t.GUID == guid);
            Program.Transfers.RemoveSafe(transfer);

            if (!ok || !transfer)
            {
                Debug.Log($"Process transfer failed for - {transfer}");
                new PROTO_NC_USER_LOGINFAIL_ACK(0x49).Send(transfer?.Connection);

                return;
            }

            Program.CachedAccounts.AddSafe(guid, transfer.Account);
            new PROTO_NC_USER_WORLDSELECT_ACK(transfer.World, guid).Send(transfer.Connection);

            Object.Destroy(transfer);
        }
    }
}
