using Diligent.WebAPI.Data.Entities;
using Diligent.WebAPI.Host.DTOs.Notification;
using Diligent.WebAPI.Host.DTOs.Chat;
using Diligent.WebAPI.Host.Mediator.Messages.Commands;
using Diligent.WebAPI.Host.Mediator.Notifications.Commands;
using MediatR;
using Microsoft.AspNetCore.SignalR;
using Diligent.WebAPI.Host.Middlewares;
using Diligent.WebAPI.Host.Mediator.Rooms.Commands;

namespace Diligent.WebAPI.Host.Hubs
{
    public class ChatHub : Hub<IChatClient>
    {
        private readonly IDictionary<string, UserConnection> _connections;
        private readonly IMediator _mediator;

        public ChatHub(IDictionary<string, UserConnection> connections,IMediator mediator)
        {
            _connections = connections;
            _mediator = mediator;
        }

        [HubMethodName("SendMessageToGroup")]
        [AuthorizationHubFilter(Roles = "Customer,Support")]
        public async Task SendMessageToGroup(ChatMessage message)
        {
            // Find user's room and send message to everyone
            // All other users will receive notification
            if (_connections.TryGetValue(message.ConnId, out UserConnection room))
            {
                await Clients.Group(room.RoomId).ReceiveMessage(new ChatMessage { UserId = room.UserId, User = room.UserId, Message = message.Message, RoomId = room.RoomId });
                await _mediator.Send(new AddMessageCommand(room.RoomId, new Message { Content = message.Message, SenderId = message.UserId, Username = room.Username }));

                // Find other users in room and save notification in database
                foreach (KeyValuePair<string, UserConnection> conn in _connections)
                {
                    if (conn.Value.RoomId == room.RoomId && conn.Key != message.ConnId)
                    {
                        await _mediator.Send(new AddNotificationCommand(new NotificationSaveDTO { RoomId = room.RoomId, ReceiverId = conn.Value.UserId }));
                    }
                }

                await Clients.OthersInGroup(room.RoomId).ReceiveNotifications(room.UserId, room.RoomId);
            }
        }

        [HubMethodName("JoinRoom")]
        [AuthorizationHubFilter(Roles = "Customer,Support")]
        public async Task JoinRoom(UserConnection userConnection)
        {
            // Check is user in requested room
            var result = _connections.Where(x => x.Value.UserId == userConnection.UserId && x.Value.RoomId == userConnection.RoomId).FirstOrDefault();

            // If user is not in room, add him
            // Return message "User has joined room" to all users in room
            if (result.Value == null)
            {
                await Groups.AddToGroupAsync(Context.ConnectionId, userConnection.RoomId);
                _connections[Context.ConnectionId] = userConnection;
                await Clients.Group(userConnection.RoomId).ReceiveMessage(new ChatMessage { UserId = userConnection.UserId, User = userConnection.UserId, RoomId = userConnection.RoomId, Message = $"{userConnection.Username} has joined room", ConnId = Context.ConnectionId,IsAccessMessage = true });
            }
            else
            {
                await _mediator.Send(new DeleteNotificationCommand(new NotificationDeleteDTO { UserId = userConnection.UserId, RoomId = userConnection.RoomId }));
            }
        }

        [HubMethodName("LeaveRoom")]
        [AuthorizationHubFilter(Roles = "Customer,Support")]
        public async Task LeaveRoom(LeaveChatRoomDTO connection)
        {
            if (_connections.TryGetValue(connection.ConnId, out UserConnection room))
            {
                // Find user connection in connections dictionary and delete it
                _connections.Remove(Context.ConnectionId);
                await Clients.OthersInGroup(room.RoomId).ReceiveMessage(new ChatMessage { UserId = room.UserId, User = room.UserId, Message = $"{room.Username} has left room", RoomId = room.RoomId,IsAccessMessage = true });
                await _mediator.Send(new RemoveUserFromGroupCommand(room.RoomId, room.UserId));
            }
        }

        [HubMethodName("SeeWhoIsTyping")]
        public async Task SeeWhoIsTyping(TypingInvokeMessage message)
        {
            // Find user's room and send message to everyone
            if (_connections.TryGetValue(message.ConnId, out UserConnection room))
            {
                // send the typing info to all in group except the caller
                await Clients.GroupExcept(message.RoomId, message.ConnId).ViewTyping(new TypingMessage { RoomId = room.RoomId, Message = message.Message });
            }
        }
    }
}
