using System;
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 SendContactRequestCommand(Guid UserId, Guid ContactId) : ICommand;

internal sealed class SendContactRequestCommandHandler : ICommandHandler<SendContactRequestCommand>
{
    private readonly IContactsDbContext _context;
    private readonly Knot.Contracts.Auth.Infrastructure.Persistence.IAuthDbContext _authContext;

    public SendContactRequestCommandHandler(IContactsDbContext context, Knot.Contracts.Auth.Infrastructure.Persistence.IAuthDbContext authContext)
    {
        _context = context;
        _authContext = authContext;
    }

    public async Task<Result> Handle(SendContactRequestCommand request, CancellationToken cancellationToken)
    {
        if (request.UserId == request.ContactId)
        {
            return Result.Failure(new Error("Contacts.Self", "You cannot add yourself to contacts."));
        }

        // 1. Proactively ensure replicas exist for both ends
        await EnsureUserReplicaExists(request.UserId, cancellationToken);
        await EnsureUserReplicaExists(request.ContactId, cancellationToken);

        var existing = await _context.Contacts
            .FirstOrDefaultAsync(f => (f.UserId == request.UserId && f.ContactId == request.ContactId) ||
                                      (f.UserId == request.ContactId && f.ContactId == request.UserId), cancellationToken);

        if (existing != null)
        {
            return Result.Failure(new Error("Contacts.Exists", "Contact relation already exists."));
        }

        var contact = Contact.CreateRequest(request.UserId, request.ContactId);
        _context.Contacts.Add(contact);
        await _context.SaveChangesAsync(cancellationToken);

        return Result.Success();
    }

    private async Task EnsureUserReplicaExists(Guid userId, CancellationToken ct)
    {
        var existing = await _context.UserReplicas.AnyAsync(r => r.Id == userId, ct);
        if (existing) return;

        var user = await _authContext.Users.FirstOrDefaultAsync(u => u.Id == userId, ct);
        if (user == null) return; // User might be external or not found

        var replica = UserReplica.Create(
            user.Id,
            user.Username,
            user.DisplayName,
            user.Avatar ?? string.Empty,
            user.IsExternal,
            user.Domain
        );

        _context.UserReplicas.Add(replica);
        await _context.SaveChangesAsync(ct);
    }
}
