using qtc_net_client_2.ClientModel;
using qtc_net_client_2.Controls;
using qtc_net_client_2.Services;
using QtCNETAPI.Dtos.User;
using QtCNETAPI.Events;
using QtCNETAPI.Models;
using QtCNETAPI.Services.ApiService;
using QtCNETAPI.Services.GatewayService;
using System.Threading.Tasks;

namespace qtc_net_client_2.Forms
{
    public partial class ChatRoom : Form
    {
        private readonly IGatewayService _gatewayService;
        private readonly IApiService _apiService;
        private readonly AudioService AudioService = new();

        private readonly List<User> RoomUserList = new();
        private Room? Room;
        public ChatRoom(IGatewayService gatewayService, IApiService apiService, Room? room = null)
        {
            _gatewayService = gatewayService;
            _apiService = apiService;
            Room = room;

            // subscribe to server message event
            _gatewayService.OnRoomMessageReceived += _gatewayService_OnServerMessageReceived;
            _gatewayService.OnRoomUserListReceived += _gatewayService_OnRoomUserListReceived;
            _gatewayService.OnRoomDeleted += _gatewayService_OnRoomDeleted;
            _gatewayService.OnGuestUserJoin += _gatewayService_OnGuestUserJoin;

            InitializeComponent();
        }

        private async void frmChat_Load(object sender, EventArgs e)
        {
            if (Room == null)
            {
                // join lobby
                await _gatewayService.JoinRoomAsync();
                Room = new Room
                {
                    Id = "LOBBY",
                    Name = "Lobby"
                };
            }
            else
            {
                // join selected room
                await _gatewayService.JoinRoomAsync(Room);
            }

            // init ui

            Text = $"QtC.NET Client - Chat Room - {Room.Name}";
            lblRoomName.Text = Room.Name;

            RoomUserList.Clear();
            lvUserList.Items.Clear();
        }

        private async void frmChat_FormClosing(object sender, FormClosingEventArgs e)
        {
            // unsubscribe from server message event
            _gatewayService.OnRoomMessageReceived -= _gatewayService_OnServerMessageReceived;
            _gatewayService.OnRoomUserListReceived -= _gatewayService_OnRoomUserListReceived;

            if (_gatewayService.CurrentRoom != null)
            {
                // leave any room user is in
                await _gatewayService.LeaveRoomAsync();
            }
        }

        private async void btnSend_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrWhiteSpace(rtxtChatbox.Text))
            {
                // construct message
                QtCNETAPI.Models.Message message = new() { Content = rtxtChatbox.Text };

                // send it and clear text box
                await _gatewayService.PostMessageAsync(message);
                rtxtChatbox.Text = string.Empty;
                AudioService.PlaySoundEffect("sndSendClick");
            }
        }

        private void rtxtChatbox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
                btnSend_Click(sender, e);
        }

        private async void lvUserList_DoubleClick(object sender, EventArgs e)
        {
            if (lvUserList.SelectedItems.Count > 0)
            {
                string? selectedUser = (string?)lvUserList.SelectedItems[lvUserList.SelectedItems.Count - 1].Text;

                if (selectedUser != null)
                {
                    // split from [ if it exists
                    if (selectedUser.Contains('[')) selectedUser = selectedUser.Split('[', options: StringSplitOptions.TrimEntries)[0];

                    // in this context, we need to find the main form to get the current user directory
                    Main? mainForm = (Main?)Application.OpenForms[0];
                    if (mainForm != null)
                    {
                        // get user info and open profile dialog
                        var user = mainForm.UserDirectory.FirstOrDefault(e => e.Username == selectedUser);
                        if (user != null)
                        {
                            var res = await _apiService.GetUserInformationAsync(user.Id);
                            var pfpRes = await _apiService.GetUserProfilePic(user.Id);

                            if (res.Data != null && res.Success)
                            {
                                Profile frmProfile = new Profile(res.Data, pfpRes, mainForm.Contacts, _apiService, _gatewayService);
                                frmProfile.Show();
                            }
                        }
                    }
                }
            }
        }

        private async void _gatewayService_OnServerMessageReceived(object? sender, EventArgs e)
        {
            var msgEventArgs = (ServerMessageEventArgs)e;
            var user = RoomUserList.FirstOrDefault(e => e.Username == msgEventArgs.Message.Split(':')[0]);

            AddMessage(msgEventArgs.Message, user?.Id);
            if (!msgEventArgs.Message.Contains(_apiService.CurrentUser.Username)) AudioService.PlaySoundEffect("sndMessage");
        }

        private void _gatewayService_OnRoomUserListReceived(object? sender, EventArgs e)
        {
            var args = (RoomListEventArgs)e;

            if (IsHandleCreated && !IsDisposed)
            {
                Invoke(async delegate ()
                {
                    RoomUserList.Clear();
                    lvUserList.Items.Clear();
                    foreach (var user in args.UserList)
                    {
                        lvUserList.Items.Add(user.Username, user.Status);
                        RoomUserList.Add(user);

                        // TODO - probably do this only when a message is received (requires response model change)
                        var pfpRes = await _apiService.GetUserProfilePic(user.Id);
                        if (pfpRes != null && pfpRes.Success && pfpRes.Data != null)
                        {
                            using var ms = new MemoryStream(pfpRes.Data);
                            using var bmp = new Bitmap(ms);
                            ilProfileImages.Images.Add(user.Id, bmp);
                        }
                    }
                });
            }
        }
        private void _gatewayService_OnGuestUserJoin(object? sender, EventArgs e)
        {
            var args = (GuestUserJoinEventArgs)e;
            AddMessage($"Server: Guest User {args.Username} Has Joined {_gatewayService.CurrentRoom?.Name}");
        }

        private void _gatewayService_OnRoomDeleted(object? sender, EventArgs e)
        {
            if (IsHandleCreated && !IsDisposed)
            {
                Invoke(delegate ()
                {
                    AddMessage($"Server: This Room Was Deleted By An Admin.");
                    lvUserList.Items.Clear();
                    RoomUserList.Clear();

                    lvUserList.Enabled = false;
                    rtxtChatbox.Enabled = false;
                    btnSend.Enabled = false;
                });
            }
        }


        private void AddMessage(string message, string? userId = null)
        {
            if (IsHandleCreated && !IsDisposed)
            {
                Invoke((Delegate)delegate ()
                {
                    var ctrl = new ChatMessageControl()
                    {
                        Username = message.Split(':')[0],
                        Message = message.Split(':')[1].Trim(),
                        Width = fpnlMessages.ClientSize.Width - SystemInformation.VerticalScrollBarWidth
                    };

                    if(ilProfileImages.Images.ContainsKey(userId))
                        ctrl.Avatar = ilProfileImages.Images[userId];
                    else ctrl.Avatar = null;

                    ctrl.Height = ctrl.CalculateHeight(ctrl.Width);

                    fpnlMessages.Controls.Add(ctrl);
                    fpnlMessages.VerticalScroll.Value = fpnlMessages.VerticalScroll.Maximum;
                    fpnlMessages.PerformLayout();
                });
            }
        }
    }
}
