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 GetContactsQuery(Guid UserId) : IQuery<List<ContactDto>>;

internal sealed class GetContactsQueryHandler : IQueryHandler<GetContactsQuery, List<ContactDto>>
{
    private readonly IContactsDbContext _context;

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

    public async Task<Result<List<ContactDto>>> Handle(GetContactsQuery request, CancellationToken cancellationToken)
    {
        // 1. Fetch ALL accepted relations for current user
        var relations = await _context.Contacts
            .Where(c => (c.UserId == request.UserId || c.ContactId == request.UserId) && c.Status == ContactStatus.Accepted)
            .ToListAsync(cancellationToken);

        // 2. Fetch all unique IDs for users we need replicas for
        var contactIds = relations.Select(c => c.UserId == request.UserId ? c.ContactId : c.UserId).Distinct().ToList();
        
        // 3. Fetch replicas
        var replicas = await _context.UserReplicas
            .Where(r => contactIds.Contains(r.Id))
            .ToDictionaryAsync(r => r.Id, cancellationToken);

        var result = new List<ContactDto>();

        // 4. Iterate over RELATIONS (to ensure we don't skip people with missing replicas)
        foreach (var rel in relations)
        {
            var otherUserId = rel.UserId == request.UserId ? rel.ContactId : rel.UserId;
            
            if (replicas.TryGetValue(otherUserId, out var replica))
            {
                result.Add(new ContactDto(
                    replica.Id,
                    replica.Username,
                    replica.DisplayName,
                    replica.Avatar,
                    false, // isOnline - current user query doesn't handle this here
                    null,  // lastSeen
                    rel.Id,
                    rel.Status == ContactStatus.Blocked,
                    replica.IsExternal,
                    replica.Domain
                ));
            }
            else
            {
                // Return placeholder but ensure it's in the list
                result.Add(new ContactDto(
                    otherUserId,
                    "Unknown",
                    "Unknown",
                    "",
                    false,
                    null,
                    rel.Id,
                    false,
                    false,
                    null
                ));
            }
        }

        return Result.Success(result);
    }
}
