using Krypton.Toolkit;
using qtcnet_client.Controls;
using qtcnet_client.Factories;
using qtcnet_client.Forms;
using qtcnet_client.Forms.Games;
using qtcnet_client.Model;
using qtcnet_client.Services;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Events;
using QtCNETAPI.Models;
using QtCNETAPI.Services;
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services.GatewayService;
using System.Drawing.Drawing2D;
using System.Threading.Tasks;

namespace qtcnet_client
{
    public partial class MainForm : Form
    {
        private BrandingControl? BrandingControl;
        private LoginControl? LoginControl;
        private RegisterControl? RegisterControl;
        private CurrentProfileControl? CurrentProfileControl;
        private MainTabControl? MainTabControl;

        private readonly List<ChatRoomForm> OpenChatRoomForms = [];
        private readonly List<DirectMessageForm> OpenDirectMessagesForms = [];
        private readonly List<ProfileForm> OpenProfileForms = [];

        private readonly List<Contact> UserContactsList = [];

        private Size LoggedOutSize = new(615, 702);
        private Size LoggedInSize = new(419, 715);

        private readonly IApiService _apiService;
        private readonly IGatewayService _gatewayService;
        private readonly LoggingService _loggingService;
        private readonly CredentialService _credentialService;
        private readonly ClientConfig _config;
        private readonly ImageFactory _imgFactory;
        private readonly UpdateService _updateService;
        private readonly AudioService _audioService;

        private bool IsAdmin = false;
        public MainForm(IApiService apiService,
            IGatewayService gatewayService,
            LoggingService loggingService,
            CredentialService credentialService,
            ClientConfig config,
            ImageFactory imageFactory,
            UpdateService updateService,
            AudioService audioService)
        {
            _apiService = apiService;
            _gatewayService = gatewayService;
            _loggingService = loggingService;
            _credentialService = credentialService;
            _config = config;
            _imgFactory = imageFactory;
            _updateService = updateService;
            _audioService = audioService;

            // sub to currentuser updates
            _apiService.OnCurrentUserUpdate += _apiService_OnCurrentUserUpdate;

            InitializeComponent();
        }

        private void MainForm_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Rectangle _formRect = ClientRectangle;

            if (_formRect.IsEmpty)
                _formRect = e.ClipRectangle;

            // draw gradient background
            using LinearGradientBrush b = new(_formRect, Color.White, Color.DodgerBlue, LinearGradientMode.Vertical);
            g.FillRectangle(b, _formRect);
        }

        private void MainForm_Resize(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized && _config.MinimizeToTray)
                Hide();

            Invalidate();
        }

        private async void MainForm_Load(object sender, EventArgs e)
        {
            // first check for updates
            await _updateService.CheckForUpdatesAsync();

            SuspendLayout();
            Size = LoggedOutSize;

            // add branding control
            BrandingControl = new()
            {
                Location = new(-2, -17),
                Anchor = AnchorStyles.Top | AnchorStyles.Left
            };

            // add login control
            LoginControl = new()
            {
                Location = new(12, 233)
            };
            LoginControl.OnSuccessfulLogin += LoginControl_OnSuccessfulLogin;
            LoginControl.OnRegisterPressed += LoginControl_OnRegisterPressed;

            Controls.Add(BrandingControl);
            Controls.Add(LoginControl);

            ResumeLayout(true);

            // ensure api is reachable before letting the user login
            LoginControl.ToggleControls(false);
            var _pingRes = await Task.Run(PingAPI);
            if (_pingRes)
            {
                // check for existing login in credential service
                var _token = _credentialService.GetAccessToken();
                if (_token != null)
                {
                    // try it
                    await LoginAsync();
                }
                else
                    LoginControl.ToggleControls(true);
            }
            else
            {
                KryptonMessageBox.Show("The Server Is Unreachable. Ensure Your Config Is Correct. The Client Will Now Close", "Oops");
                Application.Exit();
            }
        }

        private void LoginControl_OnRegisterPressed(object? sender, EventArgs e)
        {
            if (sender is LoginControl _)
            {
                // pause ui
                SuspendLayout();

                // remove and dispose login control
                Controls.Remove(LoginControl);
                LoginControl?.Dispose();
                LoginControl = null;

                // create register control
                RegisterControl = new()
                {
                    Location = new(1, 233)
                };
                RegisterControl.OnReshowLoginPressed += RegisterControl_OnReshowLoginPressed;
                RegisterControl.OnRegister += RegisterControl_OnRegister;

                Controls.Add(RegisterControl);

                ResumeLayout(true);
            }
        }

        private void RegisterControl_OnReshowLoginPressed(object? sender, EventArgs e)
        {
            if (sender is RegisterControl _)
            {
                // pause ui
                SuspendLayout();

                // remove and dispose register control
                Controls.Remove(RegisterControl);
                RegisterControl?.Dispose();
                RegisterControl = null;

                // add login control
                LoginControl = new()
                {
                    Location = new(12, 233)
                };
                LoginControl.OnSuccessfulLogin += LoginControl_OnSuccessfulLogin;
                LoginControl.OnRegisterPressed += LoginControl_OnRegisterPressed;

                Controls.Add(LoginControl);

                ResumeLayout(true);
            }
        }

        private async void RegisterControl_OnRegister(object? sender, EventArgs e)
        {
            if (sender is RegisterControl _senderCtrl)
            {
                // attempt registration
                var res = await _apiService.RegisterAsync(new()
                {
                    Username = _senderCtrl.Username,
                    Email = _senderCtrl.Email,
                    Password = _senderCtrl.Password,
                    DateOfBirth = _senderCtrl.DateOfBirth,
                });


                if (res.Success && res.Data != null)
                {
                    KryptonMessageBox.Show("Registration Success!\n\nNote: Some Servers Require Email Verification, Check Your Email!\n\nYou May Now Login.", "Success!");

                    // pause ui
                    SuspendLayout();

                    // remove and dispose register control
                    Controls.Remove(RegisterControl);
                    RegisterControl?.Dispose();
                    RegisterControl = null;

                    // add login control
                    LoginControl = new()
                    {
                        Location = new(12, 233)
                    };
                    LoginControl.OnSuccessfulLogin += LoginControl_OnSuccessfulLogin;
                    LoginControl.OnRegisterPressed += LoginControl_OnRegisterPressed;

                    Controls.Add(LoginControl);

                    ResumeLayout(true);
                }
                else
                {
                    KryptonMessageBox.Show($"Sorry, Something Went Wrong Registering You. Please Try Again Later.\n\nServer Response = {res.Message}", "Uh Oh.",
                        KryptonMessageBoxButtons.OK,
                        KryptonMessageBoxIcon.Error);
                    _senderCtrl.ToggleControls(true);
                }
            }
        }

        private async void LoginControl_OnSuccessfulLogin(object? sender, EventArgs e)
        {
            if (sender is LoginControl _senderCtrl)
            {
                // login using info
                await LoginAsync(new() { Email = _senderCtrl.Email, Password = _senderCtrl.Password, RememberMe = _senderCtrl.RememberMe });
            }
        }

        private async void MainTabControl_OnRoomControlDoubleClicked(object? sender, EventArgs e)
        {
            if (sender is RoomControl _roomCtrl)
            {
                if (OpenChatRoomForms.Count > 0)
                {
                    KryptonMessageBox.Show("Joining Multiple Rooms Is Currently Unsupported.", ":(");
                    return;
                }

                // open the room form
                ChatRoomForm _chatRoom = new(_gatewayService, _apiService, _imgFactory)
                {
                    RoomId = _roomCtrl.RoomId,
                    RoomName = _roomCtrl.RoomName,
                };
                OpenChatRoomForms.Add(_chatRoom);

                _chatRoom.OnSend += Chat_OnSend;
                _chatRoom.OnClose += Chat_OnClose;

                _chatRoom.Show();

                if (_roomCtrl.RoomId == "LOBBY")
                    await _gatewayService.JoinRoomAsync();
                else
                {
                    // join the user to the room
                    await _gatewayService.JoinRoomAsync(new() // TODO - refactor this server side (its so bad)
                    {
                        Id = _roomCtrl.RoomId,
                        Name = _roomCtrl.Name,
                    });
                }
            }
        }

        private async void Chat_OnSend(object? sender, EventArgs e)
        {
            if (sender is ChatRoomForm _chatRoom)
            {
                // send the message
                await _gatewayService.PostMessageAsync(new()
                {
                    Content = _chatRoom.SentMessage
                });
            }
        }

        private async void Chat_OnClose(object? sender, EventArgs e)
        {
            if (sender is ChatRoomForm chatRoom)
            {
                // leave whatever room the user is in
                await _gatewayService.LeaveRoomAsync();

                OpenChatRoomForms.Remove(chatRoom);
                chatRoom.Dispose();
            }
        }

        private async void MainTabControl_OnContactControlDoubleClicked(object? sender, EventArgs e)
        {
            if (sender is ContactControl _contactCtrl)
            {
                var _user = await _apiService.GetUserInformationAsync(_contactCtrl.UserId);
                if (_user.Success && _user.Data != null)
                {
                    // create a profile form
                    ProfileForm _profile = new(_apiService, _imgFactory)
                    {
                        UserId = _user.Data.Id,
                        Username = _user.Data.Username,
                        Bio = _user.Data.Bio,
                        Status = _user.Data.Status,
                    };

                    // check if user is in contacts list (if profile isn't self)
                    var _contact = UserContactsList.FirstOrDefault(e => e.OwnerId == _user.Data.Id || e.UserId == _user.Data.Id);
                    if (_contact != null)
                    {
                        // set contact status
                        if (_contact.UserId == _apiService.CurrentUser?.Id)
                            _profile.ContactStatus = _contact.UserStatus;
                        else
                            _profile.ContactStatus = _contact.OwnerStatus;
                    }
                    else
                        _profile.ContactStatus = Contact.ContactStatus.NoRelation;

                    // get profile image
                    _profile.ProfileImage = _imgFactory.GetAndCreateProfileImage(_user.Data.Id, _user.Data.Status, _user.Data.ProfileCosmeticId);

                    _profile.OnClose += ProfileForm_OnClose;
                    _profile.OnMessageClicked += ProfileForm_OnMessageClicked;

                    OpenProfileForms.Add(_profile);
                    _profile.Show();
                }
            }
        }

        private async void ProfileForm_OnMessageClicked(object? sender, EventArgs e)
        {
            if(sender is ProfileForm _profileForm)
            {
                var _userInfo = await _apiService.GetUserInformationAsync(_profileForm.UserId);
                if(_userInfo.Success && _userInfo.Data != null)
                {
                    // create dm form
                    DirectMessageForm _dmForm = new(_imgFactory)
                    {
                        UserId = _profileForm.UserId,
                        Username = _profileForm.Username,
                        ProfileImage = _imgFactory.GetAndCreateProfileImage(_profileForm.UserId, -1, _userInfo.Data.ProfileCosmeticId),
                    };
                    _dmForm.OnClose += DirectMessage_OnClose;
                    _dmForm.OnSend += DirectMessage_OnSend;

                    _dmForm.Show();
                    OpenDirectMessagesForms.Add(_dmForm);
                }
            }
        }

        private async void MainTabControl_OnUserItemDoubleClicked(object? sender, EventArgs e)
        {
            if (sender is MainTabControl _mtCtrl)
            {
                if (_mtCtrl.SelectedUser != null)
                {
                    // get the user to open the profile for
                    if (_mtCtrl.SelectedUser.Tag is string _tagString)
                    {
                        var _user = await _apiService.GetUserInformationAsync(_tagString);
                        if (_user.Success && _user.Data != null)
                        {
                            // create a profile form
                            ProfileForm _profile = new(_apiService, _imgFactory)
                            {
                                UserId = _user.Data.Id,
                                Username = _user.Data.Username,
                                Bio = _user.Data.Bio,
                                Status = _user.Data.Status,
                            };

                            // check if user is in contacts list (if profile isn't self)
                            var _contact = UserContactsList.FirstOrDefault(e => e.OwnerId == _user.Data.Id || e.UserId == _user.Data.Id);
                            if (_contact != null)
                            {
                                // set contact status
                                if (_contact.OwnerId == _apiService.CurrentUser?.Id)
                                    _profile.ContactStatus = _contact.UserStatus;
                                else
                                    _profile.ContactStatus = _contact.OwnerStatus;
                            }
                            else
                                _profile.ContactStatus = Contact.ContactStatus.NoRelation;

                            // get profile image
                            _profile.ProfileImage = _imgFactory.GetAndCreateProfileImage(_user.Data.Id, _user.Data.Status, _user.Data.ProfileCosmeticId);

                            _profile.OnClose += ProfileForm_OnClose;
                            _profile.OnMessageClicked += ProfileForm_OnMessageClicked;

                            OpenProfileForms.Add(_profile);
                            _profile.Show();
                        }
                    }
                }
            }
        }

        private void ProfileForm_OnClose(object? sender, EventArgs e)
        {
            if (sender is ProfileForm profile)
            {
                OpenProfileForms.Remove(profile);
                profile.Dispose();
            }
        }

        private async void _apiService_OnCurrentUserUpdate(object? sender, EventArgs e)
        {
            // set everything in the current user profile
            CurrentProfileControl?.Username = _apiService.CurrentUser?.Username ?? "Username";
            CurrentProfileControl?.CurrencyCount = _apiService.CurrentUser?.CurrencyAmount ?? 0;

            CurrentProfileControl?.ProfileImage = _imgFactory.GetAndCreateProfileImage(_apiService.CurrentUser!.Id, _apiService.CurrentUser!.Status, _apiService.CurrentUser!.ActiveProfileCosmetic);

            CurrentProfileControl?.RefreshInfo();
        }

        private async void _gatewayService_OnUserForceLogout(object? sender, EventArgs e)
        {
            KryptonMessageBox.Show("The Server Has Logged You Out. Please Try Signing In Again.", "Uh Oh.",
                KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon.Error);

            LogoutAsync();
        }

        private void _gatewayService_OnServerConfigReceived(object? sender, EventArgs e)
        {
            if (e is ServerConfigEventArgs _args)
            {
                if (InvokeRequired)
                    Invoke(() => Text = $"{Text} - Connected To {_args.ServerConfig.Name}");
                else Text = $"{Text} - Connected To {_args.ServerConfig.Name}";
            }
        }

        private async void _gatewayService_OnRefreshContactsListReceived(object? sender, EventArgs e)
        {
            // get and set contacts
            var _currentUserContacts = await _apiService.GetCurrentUserContacts();
            if (_currentUserContacts.Success && _currentUserContacts.Data != null)
            {
                UserContactsList.Clear();
                UserContactsList.AddRange([.. _currentUserContacts.Data.DistinctBy(c => c.Id)]);
                await SetupContactsUI(_currentUserContacts.Data);
            }
            else
            {
                // if no contacts are found, just clear the lists
                UserContactsList.Clear();
                await SetupContactsUI([]);
            }
        }

        private async void _gatewayService_OnRefreshRoomListReceived(object? sender, EventArgs e)
        {
            // get and set rooms
            var _roomList = await _apiService.GetAllRoomsAsync();
            if (_roomList.Success && _roomList.Data != null)
                await SetupRoomsUI(_roomList.Data);
        }

        private async void _gatewayService_OnRefreshUserListReceived(object? sender, EventArgs e)
        {
            // get and set user directory
            var _currentUserDirectory = await _apiService.GetAllUsersAsync();
            if (_currentUserDirectory.Success && _currentUserDirectory.Data != null)
                await SetupDirectoryUI(_currentUserDirectory.Data);
        }

        private async void _gatewayService_OnServerDisconnect(object? sender, EventArgs e)
        {
            if (e is ServerConnectionClosedEventArgs _args)
            {
                if (_args.Error != null)
                {
                Reconnect: // probably not the best idea to use labels but whatever
                    var _dialogRes = KryptonMessageBox.Show($"Connection To The Server Lost. Would You Like To Try Again?\n\nError - {_args.Error?.Message}", "Uh Oh.",
                        KryptonMessageBoxButtons.YesNo, KryptonMessageBoxIcon.Error);
                    if (_dialogRes == DialogResult.Yes)
                    {
                        // completely restart connection
                        await _gatewayService.StopAsync();
                        await _gatewayService.StartAsync();

                        if (_gatewayService.HubConnection?.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected)
                            _gatewayService_OnServerReconnected(sender, e);
                        else
                            goto Reconnect;
                    }
                }
            }
        }

        private void _gatewayService_OnServerReconnected(object? sender, EventArgs e)
        {
            // reenable interactive controls
            if (InvokeRequired)
            {
                Invoke(() =>
                {
                    CurrentProfileControl?.Enabled = true;
                    MainTabControl?.Enabled = true;
                });
            }
            else
            {
                CurrentProfileControl?.Enabled = true;
                MainTabControl?.Enabled = true;
            }
        }

        private void _gatewayService_OnServerReconnecting(object? sender, EventArgs e)
        {
            // disable interactive controls
            if(InvokeRequired)
            {
                Invoke(() =>
                {
                    CurrentProfileControl?.Enabled = false;
                    MainTabControl?.Enabled = false;
                });
            }
            else
            {
                CurrentProfileControl?.Enabled = false;
                MainTabControl?.Enabled = false;
            }
        }

        private async void _gatewayService_OnDirectMessageReceived(object? sender, EventArgs e)
        {
            if(e is DirectMessageEventArgs _args)
            {
                // check for existing open form
                var _existingForm = OpenDirectMessagesForms.FirstOrDefault(e => e.UserId == _args.User.Id);
                if (_existingForm != null)
                {
                    // just create a new message ctrl and add it
                    MessageControl _newMsg = new()
                    {
                        Message = _args.Message.Content,
                        Username = _args.User.Username,
                    };

                    _existingForm.AddMessage(_newMsg);
                    await _existingForm.LoadProfileImage(_newMsg, _args.User);
                    _audioService.PlaySoundEffect("sndMessage");
                }
                else
                {
                    // create a new form
                    DirectMessageForm _dmForm = new(_imgFactory)
                    {
                        UserId = _args.User.Id,
                        Username = _args.User.Username,
                        ProfileImage = _imgFactory.GetAndCreateProfileImage(_args.User.Id, -1, _args.User.ProfileCosmeticId),
                    };
                    _dmForm.OnSend += DirectMessage_OnSend;
                    _dmForm.OnClose += DirectMessage_OnClose;

                    // add the new message to it
                    MessageControl _msg = new()
                    {
                        Username = _args.User.Username,
                        Message = _args.Message.Content,
                    };

                    _dmForm.AddMessage(_msg);
                    await _dmForm.LoadProfileImage(_msg, _args.User);

                    Task.Run(_dmForm.ShowDialog); // workaround async issues (i suck at coding and dont wanna rework the entire ui again)
                    OpenDirectMessagesForms.Add(_dmForm);
                    _audioService.PlaySoundEffect("sndDirectMsg");
                }
            }
        }

        private void DirectMessage_OnClose(object? sender, EventArgs e)
        {
            if (sender is DirectMessageForm _dmForm)
            {
                OpenDirectMessagesForms.Remove(_dmForm);
                _dmForm.Dispose();
            }
        }

        private async void DirectMessage_OnSend(object? sender, EventArgs e)
        {
            if(sender is DirectMessageForm _dmForm)
            {
                // send message to user
                var _userInfo = await _apiService.GetUserInformationAsync(_dmForm.UserId);
                if(_userInfo.Success && _userInfo.Data != null)
                {
                    await _gatewayService.SendDirectMessageAsync(_userInfo.Data, new QtCNETAPI.Models.Message { Content = _dmForm.SentMessage });

                    // add own message to form message list
                    MessageControl _selfMsg = new()
                    {
                        Username = _apiService.CurrentUser!.Username,
                        Message = _dmForm.SentMessage,
                    };

                    _dmForm.AddMessage(_selfMsg);
                    await _dmForm.LoadProfileImage(_selfMsg, new UserInformationDto { Id = _apiService.CurrentUser!.Id, ProfileCosmeticId = _apiService.CurrentUser.ActiveProfileCosmetic });
                    _audioService.PlaySoundEffect("sndSendClick");
                }
            }
        }

        private async Task<bool> PingAPI()
        {
            // ping it
            var res = await _apiService.PingServerAsync();

            // return result
            return res.Success;
        }

        private async Task LoginAsync(UserLoginDto? dto = null)
        {
            if (dto != null)
            {
                // attempt login using dto
                var res = await _apiService.LoginAsync(dto);

                if (res.Success && res.Data != null && _apiService.CurrentUser != null) // "CurrentUser" should not be null on successful login but checking anyways
                {
                    // store refresh token in credential store
                    _credentialService.SaveAccessToken(_apiService.CurrentUser.Username, res.Message);
                }
                else
                {
                    KryptonMessageBox.Show(res.Message, "Login Error", KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon.Error);
                    LoginControl?.ToggleControls(true);
                }
            }
            else
            {
                // use refresh token
                var _token = _credentialService.GetAccessToken();
                if (_token != null)
                {
                    var _res = await _apiService.RefreshLogin(_token);
                    if (!_res.Success)
                        LoginControl?.ToggleControls(true);
                }
            }

            if (_apiService.CurrentUser != null)
            {
                // pause ui
                SuspendLayout();

                // remove and dispose login and branding controls
                Controls.Remove(LoginControl);
                Controls.Remove(BrandingControl);

                LoginControl?.Dispose();
                LoginControl = null;
                BrandingControl?.Dispose();
                BrandingControl = null;

                // set size to logged in size
                Size = LoggedInSize;

                // subscribe to gateway events

                _gatewayService.OnServerReconnecting += _gatewayService_OnServerReconnecting;
                _gatewayService.OnServerReconnected += _gatewayService_OnServerReconnected;
                _gatewayService.OnServerDisconnect += _gatewayService_OnServerDisconnect;
                _gatewayService.OnDirectMessageReceived += _gatewayService_OnDirectMessageReceived;
                _gatewayService.OnRoomMessageReceived += _gatewayService_OnRoomMessageReceived;
                _gatewayService.OnRoomUserListReceived += _gatewayService_OnRoomUserListReceived;
                _gatewayService.OnRefreshUserListsReceived += _gatewayService_OnRefreshUserListReceived;
                _gatewayService.OnRefreshRoomListReceived += _gatewayService_OnRefreshRoomListReceived;
                _gatewayService.OnRefreshContactsListReceived += _gatewayService_OnRefreshContactsListReceived;
                _gatewayService.OnServerConfigReceived += _gatewayService_OnServerConfigReceived;
                _gatewayService.OnUserForceLogout += _gatewayService_OnUserForceLogout;

                // start connection
                await _gatewayService.StartAsync();
                var _res = _gatewayService.HubConnection != null && _gatewayService.HubConnection.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Connected;

                if (_res)
                {
                    // setup current profile control based on current user
                    CurrentProfileControl = new()
                    {
                        Username = _apiService.CurrentUser.Username,
                        CurrencyCount = _apiService.CurrentUser.CurrencyAmount,
                        Location = new(12, 12),
                    };

                    CurrentProfileControl.OnEditProfileClicked += CurrentProfileControl_OnEditProfileClicked;
                    CurrentProfileControl.OnSignOutClicked += CurrentProfileControl_OnSignOutClicked;
                    CurrentProfileControl.OnStatusChanged += CurrentProfileControl_OnStatusChanged;

                    // get profile image for the current user
                    CurrentProfileControl.ProfileImage = _imgFactory.GetAndCreateProfileImage(_apiService.CurrentUser.Id, _apiService.CurrentUser.Status, _apiService.CurrentUser.ActiveProfileCosmetic);

                    // setup main tab control
                    MainTabControl = new(_imgFactory)
                    {
                        Location = new(12, 91)
                    };

                    MainTabControl.OnRoomControlDoubleClicked += MainTabControl_OnRoomControlDoubleClicked;
                    MainTabControl.OnContactControlDoubleClicked += MainTabControl_OnContactControlDoubleClicked;
                    MainTabControl.OnUserItemDoubleClicked += MainTabControl_OnUserItemDoubleClicked;
                    MainTabControl.OnStoreItemDoubleClicked += MainTabControl_OnStoreItemDoubleClicked;
                    MainTabControl.OnGameItemDoubleClicked += MainTabControl_OnGameItemDoubleClicked;

                    // get and set contacts
                    var _currentUserContacts = await _apiService.GetCurrentUserContacts();
                    if (_currentUserContacts.Success && _currentUserContacts.Data != null)
                    {
                        UserContactsList.Clear();
                        UserContactsList.AddRange([.. _currentUserContacts.Data.DistinctBy(c => c.Id)]);
                        await SetupContactsUI(_currentUserContacts.Data);
                    }

                    // get and set rooms
                    var _roomList = await _apiService.GetAllRoomsAsync();
                    if (_roomList.Success && _roomList.Data != null)
                        await SetupRoomsUI(_roomList.Data);

                    // get and set user directory
                    var _currentUserDirectory = await _apiService.GetAllUsersAsync();
                    if (_currentUserDirectory.Success && _currentUserDirectory.Data != null)
                        await SetupDirectoryUI(_currentUserDirectory.Data);

                    // get and set store
                    var _currentStore = await _apiService.GetStoreItems();
                    if (_currentStore.Success && _currentStore.Data != null)
                        MainTabControl?.AddStoreItems(_currentStore.Data);

                    // add controls to current form
                    Controls.Add(CurrentProfileControl);
                    Controls.Add(MainTabControl);

                    if(_apiService.CurrentUser.Role == "Admin")
                    {
                        // create a link label that opens the admin panel when clicked
                        LinkLabel llblAdminPanel = new()
                        {
                            Location = new(325, 9),
                            Text = "Admin Panel",
                            BackColor = Color.Transparent,
                        };

                        llblAdminPanel.Click += (sender, e) =>
                        {
                            // open the admin panel
                            AdminPanelForm adminPanelForm = new(_apiService);
                            adminPanelForm.ShowDialog();
                            adminPanelForm.Dispose();
                        };

                        Controls.Add(llblAdminPanel);

                        // set isadmin to true for other admin stuff
                        IsAdmin = true;
                    }

                    ResumeLayout(true);

                    // minimize main window if start minimized is on
                    if (_config.StartMinimized)
                        WindowState = FormWindowState.Minimized;

                    // check for daily spin eligibility

                    var now = DateTime.UtcNow;
                    var lastSpin = _apiService.CurrentUser.LastCurrencySpin;
                    if (lastSpin.Date < now.Date)
                    {
                        Random rnd = new();

                        JackpotSpinForm _jackpotSpin = new(_audioService)
                        {
                            CurrencyWon = rnd.Next(0, 200)
                        };
                        var _dialogRes = _jackpotSpin.ShowDialog();
                        if (_dialogRes == DialogResult.OK)
                        {
                            // claim
                            var _apiRes = await _apiService.AddCurrencyToCurrentUser(_jackpotSpin.CurrencyWon, true);
                            if (_apiRes.Success)
                                _jackpotSpin.Dispose();
                            else
                                KryptonMessageBox.Show("We Couldn't Claim Your Prize At This Time. You Should Be Able To Spin Again Next Restart.", "Uh Oh.");
                        }
                    }
                }
                else
                {
                    ResumeLayout(true);
                    KryptonMessageBox.Show("An Error Occured Trying To Connect To The Gateway. Please Try Again Later.", "Uh Oh.", KryptonMessageBoxButtons.OK, KryptonMessageBoxIcon.Error);
                    Application.Exit();
                }
            }
        }

        private async void _gatewayService_OnUserStatusUpdated(object? sender, EventArgs e)
        {
            if(e is UserStatusUpdatedEventArgs _args)
            {
                CurrentProfileControl?.ProfileImage = _imgFactory.GetAndCreateProfileImage(_apiService.CurrentUser!.Id, _args.StatusDto!.Status, _apiService.CurrentUser!.ActiveProfileCosmetic);
            }
        }

        private async void CurrentProfileControl_OnStatusChanged(object? sender, EventArgs e)
        {
            if(sender is CurrentProfileControl _ctrl)
            {
                // set status based on selected
                await _gatewayService.UpdateStatus(_ctrl.SelectedStatus);
            }
        }

        private void MainTabControl_OnGameItemDoubleClicked(object? sender, EventArgs e)
        {
            if(sender is MainTabControl _ctrl)
            {
                string? gameSelected = (string?)_ctrl.SelectedGameItem?.Tag;

                switch (gameSelected)
                {
                    case "StockMarketGame":
                        StockMarketGame stockMarketGame = new(_apiService);
                        stockMarketGame.Show();
                        break;
                    case "GuessTheNumberGame":
                        GuessTheNumber guessTheNumber = new(_apiService, _audioService);
                        guessTheNumber.Show();
                        break;
                    case "TicTacToeGame":
                        TicTacToeGame ticTacToeGame = new(_apiService, _config);
                        ticTacToeGame.Show();
                        break;
                }
            }
        }

        private async void MainTabControl_OnStoreItemDoubleClicked(object? sender, EventArgs e)
        {
            if (sender is MainTabControl _ctrl && _ctrl.SelectedStoreItem?.Tag is int _itemId)
            {
                // get store item
                var _item = await _apiService.GetStoreItem(_itemId);
                if (_item.Success && _item.Data != null)
                {
                    // create buy form
                    StoreItemForm _itemForm = new(_apiService)
                    {
                        ItemId = _itemId,
                        ItemName = _ctrl.SelectedStoreItem.Text,
                        ItemThumbnail = await _imgFactory.GetStoreItemThumb(_itemId),
                        Price = _item.Data.Price,
                    };
                    _itemForm.OnBuyClicked += _itemForm_OnBuyClicked;

                    _itemForm.ShowDialog();
                    _itemForm.Dispose();
                }
            }
        }

        private async void _itemForm_OnBuyClicked(object? sender, EventArgs e)
        {
            if (sender is StoreItemForm _form)
            {
                // buy the item
                var _res = await _apiService.BuyStoreItem(_form.ItemId);
                if (_res.Success)
                {
                    KryptonMessageBox.Show("Item Bought!", "Yay!");
                    _form.Close();
                }
                else
                {
                    KryptonMessageBox.Show("Something Went Wrong Buying This Item. You May Not Have Enough Q's. Please Try Again Later.", "Uh Oh.");
                    _form.Close();
                }
            }
        }

        private async void LogoutAsync()
        {
            var _res = await _apiService.LogoutAsync();

            if (_res)
            {
                _credentialService.DeleteAccessToken();

                SuspendLayout();

                // remove controls
                Controls.Remove(MainTabControl);
                Controls.Remove(CurrentProfileControl);

                // dispose of them
                MainTabControl?.Dispose();
                MainTabControl = null;
                CurrentProfileControl?.Dispose();
                CurrentProfileControl = null;

                // readd login control and branding
                Size = LoggedOutSize;

                // unsub from gateway events

                _gatewayService.OnServerReconnecting -= _gatewayService_OnServerReconnecting;
                _gatewayService.OnServerReconnected -= _gatewayService_OnServerReconnected;
                _gatewayService.OnServerDisconnect -= _gatewayService_OnServerDisconnect;
                //_gatewayService.OnDirectMessageReceived -= _gatewayService_OnDirectMessageReceived;
                _gatewayService.OnRoomMessageReceived -= _gatewayService_OnRoomMessageReceived;
                _gatewayService.OnRoomUserListReceived -= _gatewayService_OnRoomUserListReceived;
                _gatewayService.OnRefreshUserListsReceived -= _gatewayService_OnRefreshUserListReceived;
                _gatewayService.OnRefreshRoomListReceived -= _gatewayService_OnRefreshRoomListReceived;
                _gatewayService.OnRefreshContactsListReceived -= _gatewayService_OnRefreshContactsListReceived;
                _gatewayService.OnServerConfigReceived -= _gatewayService_OnServerConfigReceived;
                _gatewayService.OnUserForceLogout -= _gatewayService_OnUserForceLogout;

                // stop gateway connection
                await _gatewayService.StopAsync();

                // add branding control
                BrandingControl = new()
                {
                    Location = new(-2, -17),
                    Anchor = AnchorStyles.Top | AnchorStyles.Left
                };

                // add login control
                LoginControl = new()
                {
                    Location = new(12, 233)
                };
                LoginControl.OnSuccessfulLogin += LoginControl_OnSuccessfulLogin;
                LoginControl.OnRegisterPressed += LoginControl_OnRegisterPressed;

                Controls.Add(BrandingControl);
                Controls.Add(LoginControl);

                ResumeLayout(true);
            }
        }

        private async void CurrentProfileControl_OnSignOutClicked(object? sender, EventArgs e)
        {
            var _dialogRes = KryptonMessageBox.Show("Doing This Will Sign You Out. Are You Sure?", "are you sure...?",
                KryptonMessageBoxButtons.YesNo, KryptonMessageBoxIcon.Question);

            if (_dialogRes == DialogResult.Yes)
                LogoutAsync();
        }

        private async void CurrentProfileControl_OnEditProfileClicked(object? sender, EventArgs e)
        {
            var _user = await _apiService.GetUserInformationAsync(_apiService.CurrentUser!.Id);
            if (_user.Success && _user.Data != null)
            {
                // create a profile form
                ProfileForm _profile = new(_apiService, _imgFactory)
                {
                    UserId = _user.Data.Id,
                    Username = _user.Data.Username,
                    Bio = _user.Data.Bio,
                    Status = _user.Data.Status,
                    ProfileImage = _imgFactory.GetAndCreateProfileImage(_user.Data.Id, _user.Data.Status, _user.Data.ProfileCosmeticId)
                };

                _profile.OnClose += ProfileForm_OnClose;

                OpenProfileForms.Add(_profile);
                _profile.Show();
            }
        }

        private void _gatewayService_OnRoomUserListReceived(object? sender, EventArgs e)
        {
            if (e is RoomListEventArgs _args)
            {
                // get the open chat room form
                var _chatRoom = OpenChatRoomForms.FirstOrDefault(e => e.RoomId == _args.Room.Id);
                if (_chatRoom != null)
                {
                    // the users to the list
                    _chatRoom.AddUsersToRoomList(_args.UserList);
                }
            }
        }

        private async void _gatewayService_OnRoomMessageReceived(object? sender, EventArgs e)
        {
            if (e is ServerMessageEventArgs _args)
            {
                // get the open chat room form
                var _chatRoom = OpenChatRoomForms.FirstOrDefault(e => e.RoomId == _args.Room.Id);
                if (_chatRoom != null)
                {
                    // build message control
                    MessageControl _msg = new()
                    {
                        Message = _args.Message,
                        Username = _args.User.Username
                    };

                    // add message
                    _chatRoom.AddMessage(_msg);

                    if (_args.User.Id != "0")
                        await _chatRoom.LoadProfileImage(_msg, _args.User);

                    // play sound
                    if (_args.User.Id != _apiService.CurrentUser?.Id)
                        _audioService.PlaySoundEffect("sndMessage");
                    else
                        _audioService.PlaySoundEffect("sndSendClick");
                }
            }
        }

        private async Task SetupContactsUI(List<Contact> data)
        {
            // build ctrl list
            List<ContactControl> _contactCtrls = [];
            foreach (var contact in data)
            {
                var ctrl = await BuildContactControl(contact);
                if (ctrl != null)
                    _contactCtrls.Add(ctrl);
            }

            // add to control
            MainTabControl?.AddContacts(_contactCtrls);
        }

        private async Task SetupRoomsUI(List<Room> data)
        {
            List<RoomControl> _roomCtrls = [];

            // before adding db rooms, add lobby
            _roomCtrls.Add(new()
            {
                RoomId = "LOBBY",
                RoomName = "Lobby",
                IsUserCountVisible = false,
            });

            // build roomcontrol list
            foreach (var room in data)
            {
                RoomControl _ctrl = new()
                {
                    RoomId = room.Id,
                    RoomName = room.Name,
                    UserCount = room.UserCount,
                };
                _roomCtrls.Add(_ctrl);
            }

            // add to control
            MainTabControl?.AddRooms(_roomCtrls);
        }

        private async Task SetupDirectoryUI(List<UserInformationDto> data)
        {
            // build listviewitem list
            List<ListViewItem> _userViews = [];
            foreach (var user in data)
            {
                ListViewItem lvi = new()
                {
                    Tag = user.Id,
                    Text = user.Username,
                    ImageIndex = user.Status,
                };
                _userViews.Add(lvi);
            }

            // add to control
            MainTabControl?.AddUsers(_userViews);
        }

        private async Task<ContactControl?> BuildContactControl(Contact contact)
        {
            if (_apiService.CurrentUser != null)
            {
                ServiceResponse<UserInformationDto> user = null!;
                if (contact.OwnerId == _apiService.CurrentUser.Id)
                    user = await _apiService.GetUserInformationAsync(contact.UserId);
                else if (contact.UserId == _apiService.CurrentUser.Id)
                    user = await _apiService.GetUserInformationAsync(contact.OwnerId);

                if (user.Data != null)
                {
                    var ctrl = new ContactControl
                    {
                        UserId = user.Data.Id,
                        Username = user.Data.Username,
                        TextStatus = "NOT IMPLEMENTED",
                        Status = user.Data.Status,
                    };

                    if (contact.OwnerId == _apiService.CurrentUser.Id)
                    {
                        switch (contact.OwnerStatus)
                        {
                            case Contact.ContactStatus.AwaitingApprovalFromOther:
                                ctrl.Username = $"{user.Data.Username} [Request Sent]";
                                break;
                            case Contact.ContactStatus.Accepted:
                                ctrl.Username = user.Data.Username;
                                break;
                        }

                        // get profile image
                        ctrl.ProfilePic = _imgFactory.GetAndCreateProfileImage(user.Data.Id, user.Data.Status, user.Data.ProfileCosmeticId);
                    }
                    else if (contact.UserId == _apiService.CurrentUser.Id)
                    {
                        switch (contact.UserStatus)
                        {
                            case Contact.ContactStatus.AwaitingApprovalFromSelf:
                                ctrl.Username = $"{user.Data.Username} [Contact Request]";
                                _audioService.PlaySoundEffect("sndContactRequest");
                                break;
                            case Contact.ContactStatus.Accepted:
                                ctrl.Username = user.Data.Username;
                                break;
                        }

                        // get profile image
                        ctrl.ProfilePic = _imgFactory.GetAndCreateProfileImage(user.Data.Id, user.Data.Status, user.Data.ProfileCosmeticId);
                    }

                    // return the control
                    return ctrl;
                }
            }

            return default;
        }

        private void niMain_DoubleClick(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized)
            {
                Show();
                WindowState = FormWindowState.Normal;
            }
        }

        private void tsiShow_Click(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized)
            {
                Show();
                WindowState = FormWindowState.Normal;
            }
        }

        private void tsiExit_Click(object sender, EventArgs e)
        {
            Close();
        }
    }
}
