using Govor.API.Common.SignalR.Helpers;
using Govor.Application.Interfaces;
using Govor.Contracts.Responses.SignalR;
using Govor.Core.Repositories.Groups;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;

namespace Govor.API.Hubs;

[Authorize]
public class ProfileHub : Hub
{
    private readonly IGroupsRepository _groupsRepository;
    private readonly IFriendsService _friendsService;
    private readonly IProfileService _profileService;
    private readonly IHubUserAccessor _userAccessor;
    private readonly ILogger<ProfileHub> _logger;

    public ProfileHub(
        IGroupsRepository groupsRepository,
        IFriendsService friendsService,
        IProfileService profileService,
        IHubUserAccessor userAccessor,
        ILogger<ProfileHub> logger)
    {
        _groupsRepository = groupsRepository;
        _friendsService = friendsService;
        _profileService = profileService;
        _userAccessor = userAccessor;
        _logger = logger;
    }

    public override async Task OnConnectedAsync()
    {
        var userId = _userAccessor.GetUserId(Context);

        // Добавляем в персональную группу
        await Groups.AddToGroupAsync(Context.ConnectionId, $"user-{userId}");

        // Friends
        var friendships = await _friendsService.GetFriendsAsync(userId);
        foreach (var friends in friendships)
            await Groups.AddToGroupAsync(Context.ConnectionId, $"friends-{friends.Id}");

        // Groups
        var groups = await _groupsRepository.GetByUserIdAsync(userId);
        foreach (var group in groups)
            await Groups.AddToGroupAsync(Context.ConnectionId, $"group-{group.Id}");

        _logger.LogInformation("User {UserId} connected with ConnectionId {ConnectionId}", userId, Context.ConnectionId);

        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception exception)
    {
        var userId = _userAccessor.GetUserId(Context);

        await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"user-{userId}");
        // Friends
        var friendships = await _friendsService.GetFriendsAsync(userId);
        foreach (var friendship in friendships)
            await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"friends-{friendship.Id}");

        // Groups
        var groups = await _groupsRepository.GetByUserIdAsync(userId);
        foreach (var group in groups)
            await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"group-{group.Id}");

        await base.OnDisconnectedAsync(exception);
    }


    public async Task<HubResult<bool>> SetDescription(string description)
    {
        var userId = _userAccessor.GetUserId(Context);

        try
        {
            await _profileService.SetDescription(description, userId);

            var payload = new { userId, description };

            await NotifyFriendsAndGroupsAsync(userId, "DescriptionUpdated", payload);

            return HubResult<bool>.Ok(true);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An error occurred while updating the user's {userId} descripton!", userId);
            return HubResult<bool>.Error("An unaccounted error on the server!");
        }
    }

    public async Task<HubResult<bool>> SetAvatar(Guid iconId)
    {
        var userId = _userAccessor.GetUserId(Context);

        try
        {
            if (iconId == Guid.Empty)
                return HubResult<bool>.BadRequest("IconId can't be empty!");

            await _profileService.SetNewIcon(userId, iconId);

            var payload = new { userId, iconId };

            await NotifyFriendsAndGroupsAsync(userId, "AvatarUpdated", payload);

            return HubResult<bool>.Ok(true);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An error occurred while updating the user's {userId} avatar {iconId}", userId, iconId);
            return HubResult<bool>.Error("An unaccounted error on the server!");
        }
    }

    private async Task NotifyFriendsAndGroupsAsync(Guid userId, string eventName, object payload)
    {
        var friendIds = await _friendsService.GetFriendsAsync(userId);
        var groups = await _groupsRepository.GetByUserIdAsync(userId);

        var groupIds = groups.Select(g => g.Id).ToList();

        await Clients.Group($"user-{userId}") // current user
            .SendAsync(eventName, payload);

        foreach (var friendId in friendIds)
            await Clients.Group($"friends-{friendId}")
                .SendAsync(eventName, payload);

        foreach (var groupId in groupIds)
            await Clients.Group($"group-{groupId}")
                .SendAsync(eventName, payload);

        _logger.LogInformation("Sent {EventName} for {UserId} to {Friends} friends and {Groups} groups",
            eventName, userId, friendIds.Count, groupIds.Count);
    }
}
