using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Knot.Contracts.Relations.Application.Abstractions;
using Knot.Modules.Stories.Application.Abstractions;
using Knot.Modules.Stories.Application.DTOs;
using Knot.Modules.Stories.Domain;
using Knot.Modules.Stories.Infrastructure.Database;
using Knot.Shared.Kernel;
using MediatR;
using Microsoft.EntityFrameworkCore;



namespace Knot.Modules.Stories.Application.Stories.Queries.GetStories;

public record GetStoriesQuery(Guid UserId) : IQuery<List<StoryGroupDto>>;

internal sealed class GetStoriesQueryHandler : IQueryHandler<GetStoriesQuery, List<StoryGroupDto>>
{
    private readonly StoriesDbContext _context;
    private readonly IStoryRepository _storyRepository;
    private readonly IUserRepository _userRepository;
    private readonly IFriendshipRepository _friendshipRepository;

    public GetStoriesQueryHandler(
        StoriesDbContext context,
        IStoryRepository storyRepository,
        IUserRepository userRepository,
        IFriendshipRepository friendshipRepository)
    {
        _context = context;
        _storyRepository = storyRepository;
        _userRepository = userRepository;
        _friendshipRepository = friendshipRepository;
    }

    public async Task<Result<List<StoryGroupDto>>> Handle(GetStoriesQuery request, CancellationToken cancellationToken)
    {
        var friendships = await _friendshipRepository.GetAcceptedFriendshipsAsync(request.UserId, cancellationToken);

        var friendIds = friendships.Select(f => f.UserId == request.UserId ? f.FriendId : f.UserId).ToList();
        friendIds.Add(request.UserId);

        var stories = await _storyRepository.GetFeedStoriesAsync(friendIds, cancellationToken);
        var storyIds = stories.Select(s => s.Id).ToList();

        var viewedStoriesIds = await _context.StoryViewers
            .Where(v => v.UserId == request.UserId && storyIds.Contains(v.StoryId))
            .Select(v => v.StoryId)
            .ToListAsync(cancellationToken);

        var groups = stories.GroupBy(s => s.UserId).ToList();
        var result = new List<StoryGroupDto>();

        var userIds = groups.Select(g => g.Key).ToList();
        var userMap = new Dictionary<Guid, dynamic>();
        foreach (var uid in userIds)
        {
            var u = await _userRepository.GetByIdAsync(uid, cancellationToken);
            if (u != null)
            {
                userMap[uid] = u;
            }
        }

        foreach (var group in groups)
        {
            if (!userMap.TryGetValue(group.Key, out var user))
            {
                continue;
            }

            result.Add(new StoryGroupDto(
                new StoryUserDto(user.Id, user.Username, user.DisplayName, user.Avatar),
                group.Select(s => new StoryDto(
                    s.Id,
                    s.Type,
                    s.MediaUrl,
                    s.Content,
                    s.BgColor,
                    s.IsMuted,
                    s.CreatedAt,
                    s.ViewsCount,
                    viewedStoriesIds.Contains(s.Id)
                )).OrderBy(s => s.CreatedAt).ToList(),
                group.Any(s => !viewedStoriesIds.Contains(s.Id))
            ));
        }

        return Result.Success(result.OrderBy(r =>
    {
        if (r.User.Id == request.UserId)
        {
            return 0;
        }
        return 1;
    }).ToList());
    }
}


