using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Knot.Modules.Relations.Domain;
using Knot.Shared.Kernel;
using Microsoft.EntityFrameworkCore;
using Knot.Modules.Relations.Application.Abstractions;

namespace Knot.Modules.Relations.Application.Contacts;

public record ContactUserDto(Guid Id, string Username, string DisplayName, string Avatar);
public record ContactRequestDto(Guid Id, ContactUserDto User, DateTime CreatedAt, bool IsOutgoing);

public record GetContactRequestsQuery(Guid UserId) : IQuery<List<ContactRequestDto>>;

internal sealed class GetContactRequestsQueryHandler : IQueryHandler<GetContactRequestsQuery, List<ContactRequestDto>>
{
    private readonly IContactsDbContext _context;

    public GetContactRequestsQueryHandler(IContactsDbContext context)
    {
        _context = context;
    }

    public async Task<Result<List<ContactRequestDto>>> Handle(GetContactRequestsQuery request, CancellationToken cancellationToken)
    {
        // 1. Fetch ALL pending requests where current user is either sender or receiver
        var contacts = await _context.Contacts
            .Where(c => (c.ContactId == request.UserId || c.UserId == request.UserId) && c.Status == ContactStatus.Pending)
            .ToListAsync(cancellationToken);

        // 2. Fetch all unique IDs for users we need replicas for
        var userIds = contacts
            .Select(c => c.UserId == request.UserId ? c.ContactId : c.UserId)
            .Distinct()
            .ToList();

        // 3. Fetch replicas
        var replicas = await _context.UserReplicas
            .Where(r => userIds.Contains(r.Id))
            .ToDictionaryAsync(r => r.Id, cancellationToken);

        // 4. Transform into DTOs
        var result = contacts
            .Select(c => 
            {
                var isOutgoing = c.UserId == request.UserId;
                var otherUserId = isOutgoing ? c.ContactId : c.UserId;
                
                // If replica is missing, we try to at least return the record (Visibility fix part 1)
                // We will handle replica creation in SendContactRequest proactively.
                if (!replicas.TryGetValue(otherUserId, out var user))
                {
                    return new ContactRequestDto(
                        c.Id,
                        new ContactUserDto(otherUserId, "Unknown", "Unknown", ""),
                        c.CreatedAt,
                        isOutgoing
                    );
                }

                return new ContactRequestDto(
                    c.Id,
                    new ContactUserDto(user.Id, user.Username, user.DisplayName, user.Avatar),
                    c.CreatedAt,
                    isOutgoing
                );
            })
            .ToList();

        return Result.Success(result);
    }
}
