using System;
using System.Linq;
using System.Threading.Tasks;
using LoginServer.Network;
using PhoenixLib.Logging;
using PhoenixLib.MultiLanguage;
using WingsAPI.Communication;
using WingsAPI.Communication.DbServer.AccountService;
using WingsAPI.Communication.ServerApi;
using WingsAPI.Communication.ServerApi.Protocol;
using WingsAPI.Communication.Sessions;
using WingsAPI.Communication.Sessions.Model;
using WingsAPI.Communication.Sessions.Request;
using WingsAPI.Communication.Sessions.Response;
using WingsAPI.Data.Account;
using WingsEmu.DTOs.Account;
using WingsEmu.Health;
using WingsEmu.Packets.ClientPackets;
using WingsEmu.Packets.Enums;

namespace LoginServer.Handlers
{
    public class TypedCredentialsLogin0577PacketHandler : GenericLoginPacketHandlerBase<Nos0577Packet>
    {
        private readonly IAccountService _accountService;
        private readonly IMaintenanceManager _maintenanceManager;
        private readonly IServerApiService _serverApiService;
        private readonly ISessionService _sessionService;

        public TypedCredentialsLogin0577PacketHandler(ISessionService sessionService, IServerApiService serverApiService, IMaintenanceManager maintenanceManager, IAccountService accountService)
        {
            _sessionService = sessionService;
            _serverApiService = serverApiService;
            _maintenanceManager = maintenanceManager;
            _accountService = accountService;
        }

        protected override async Task HandlePacketAsync(LoginClientSession session, Nos0577Packet packet)
        {
            if (packet == null)
            {
                return;
            }

            AccountLoadResponse accountLoadResponse = null;
            try
            {
                accountLoadResponse = await _accountService.LoadAccountByName(new AccountLoadByNameRequest
                {
                    Name = packet.Name
                });
            }
            catch (Exception e)
            {
                Log.Error("[NEW_TYPED_AUTH_0577] Unexpected error: ", e);
            }

            if (accountLoadResponse?.ResponseType != RpcResponseType.SUCCESS)
            {
                Log.Warn($"[NEW_TYPED_AUTH_0577] Failed to load account for accountName: '{packet.Name}'");
                session.SendPacket(session.GenerateFailcPacket(LoginFailType.AccountOrPasswordWrong));
                session.Disconnect();
                return;
            }

            AccountDTO loadedAccount = accountLoadResponse.AccountDto;
            bool devBypass = string.Equals(Environment.GetEnvironmentVariable("DEV_LOGIN_BYPASS"), "true", StringComparison.OrdinalIgnoreCase);
            bool hasValidAuthCode = Guid.TryParse(packet.AuthCode, out _);
            bool passwordOk = string.Equals(loadedAccount.Password, packet.Password, StringComparison.CurrentCultureIgnoreCase);

            if (!devBypass && !passwordOk && !hasValidAuthCode)
            {
                session.SendPacket(session.GenerateFailcPacket(LoginFailType.AccountOrPasswordWrong));
                Log.Debug($"[NEW_TYPED_AUTH_0577] WRONG_CREDENTIALS : {loadedAccount.Name}");
                session.Disconnect();
                return;
            }

            if (devBypass)
            {
                Log.Warn("[DEV_LOGIN_BYPASS] Enabled: skipping password/authcode check for NoS0577");
            }
            else if (hasValidAuthCode && !passwordOk)
            {
                Log.Info($"[NEW_TYPED_AUTH_0577] AUTHCODE accepted for account '{loadedAccount.Name}'");
            }

            SessionResponse modelResponse = await _sessionService.CreateSession(new CreateSessionRequest
            {
                AccountId = loadedAccount.Id,
                AccountName = loadedAccount.Name,
                AuthorityType = loadedAccount.Authority,
                IpAddress = session.IpAddress
            });

            if (modelResponse.ResponseType != RpcResponseType.SUCCESS)
            {
                Log.Debug($"[NEW_TYPED_AUTH_0577] FAILED TO CREATE SESSION {loadedAccount.Id}");
                session.SendPacket(session.GenerateFailcPacket(LoginFailType.AlreadyConnected));
                session.Disconnect();
                return;
            }

            AuthorityType type = loadedAccount.Authority;

            AccountBanGetResponse banResponse = null;
            try
            {
                banResponse = await _accountService.GetAccountBan(new AccountBanGetRequest
                {
                    AccountId = loadedAccount.Id
                });
            }
            catch (Exception e)
            {
                Log.Error("[NEW_TYPED_AUTH_0577] Unexpected error: ", e);
            }

            if (banResponse?.ResponseType != RpcResponseType.SUCCESS)
            {
                Log.Warn($"[NEW_TYPED_AUTH_0577] Failed to get account ban for accountId: '{loadedAccount.Id}'");
                session.SendPacket(session.GenerateFailcPacket(LoginFailType.UnhandledError));
                session.Disconnect();
                return;
            }

            AccountBanDto characterPenalty = banResponse.AccountBanDto;
            if (characterPenalty != null)
            {
                session.SendPacket(session.GenerateFailcPacket(LoginFailType.Banned));
                Log.Debug($"[NEW_TYPED_AUTH_0577] ACCOUNT_BANNED : {loadedAccount.Name}");
                session.Disconnect();
                return;
            }

            switch (type)
            {
                case AuthorityType.Banned:
                    session.SendPacket(session.GenerateFailcPacket(LoginFailType.Banned));
                    Log.Debug("[NEW_TYPED_AUTH_0577] ACCOUNT_BANNED");
                    session.Disconnect();
                    break;

                case AuthorityType.Unconfirmed:
                case AuthorityType.Closed:
                    session.SendPacket(session.GenerateFailcPacket(LoginFailType.CantConnect));
                    Log.Debug("[NEW_TYPED_AUTH_0577] ACCOUNT_NOT_VERIFIED");
                    session.Disconnect();
                    break;

                default:
                    // Temporary: do not block logins by maintenance flag while local setup is being validated.
                    if (false && _maintenanceManager.IsMaintenanceActive && loadedAccount.Authority < AuthorityType.GameMaster)
                    {
                        session.SendPacket(session.GenerateFailcPacket(LoginFailType.Maintenance));
                        return;
                    }

                    SessionResponse connectResponse = await _sessionService.ConnectToLoginServer(new ConnectToLoginServerRequest
                    {
                        AccountId = loadedAccount.Id,
                        ClientVersion = "BYPASS",
                        HardwareId = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
                    });

                    if (connectResponse.ResponseType != RpcResponseType.SUCCESS)
                    {
                        Log.Warn("[NEW_TYPED_AUTH_0577] General Error SessionId: " + session.Id);
                        session.SendPacket(session.GenerateFailcPacket(LoginFailType.CantConnect));
                        session.Disconnect();
                        return;
                    }

                    Session connectedSession = connectResponse.Session;

                    Log.Debug($"[NEW_TYPED_AUTH_0577] Connected : {packet.Name}:{connectedSession.EncryptionKey}:{connectedSession.HardwareId}");

                    RetrieveRegisteredWorldServersResponse worldServersResponse = await _serverApiService.RetrieveRegisteredWorldServers(new RetrieveRegisteredWorldServersRequest
                    {
                        RequesterAuthority = loadedAccount.Authority
                    });

                    if (worldServersResponse?.WorldServers is null || !worldServersResponse.WorldServers.Any())
                    {
                        session.SendPacket(session.GenerateFailcPacket(LoginFailType.Maintenance));
                        session.Disconnect();
                        return;
                    }

                    session.SendChannelPacketList(connectedSession.EncryptionKey, loadedAccount.Name, RegionLanguageType.EN, worldServersResponse.WorldServers, true);
                    session.Disconnect();
                    break;
            }
        }
    }
}
