using MediatR;
using MiningService.Domain.AggregatesModel.CircleAggregate;
using MiningService.Domain.AggregatesModel.MinerAggregate;
using MiningService.Domain.Exceptions;

namespace MiningService.API.Application.Commands;

/// <summary>
/// EN: Handler for InviteToCircleCommand.
/// VI: Handler cho InviteToCircleCommand.
/// </summary>
public class InviteToCircleCommandHandler : IRequestHandler<InviteToCircleCommand, bool>
{
    private readonly ICircleRepository _circleRepository;
    private readonly IMinerRepository _minerRepository;

    public InviteToCircleCommandHandler(
        ICircleRepository circleRepository,
        IMinerRepository minerRepository)
    {
        _circleRepository = circleRepository;
        _minerRepository = minerRepository;
    }

    public async Task<bool> Handle(InviteToCircleCommand request, CancellationToken cancellationToken)
    {
        var inviter = await _minerRepository.GetByUserIdAsync(request.UserId, cancellationToken)
            ?? throw new MinerNotFoundException($"Miner with UserId {request.UserId} not found");

        var circle = await _circleRepository.GetByOwnerIdAsync(inviter.Id, cancellationToken)
            ?? throw new CircleDomainException("You don't own a circle");

        var targetMiner = await _minerRepository.GetByIdAsync(request.TargetMinerId, cancellationToken)
            ?? throw new MinerNotFoundException($"Target miner not found");

        if (targetMiner.CircleId.HasValue)
            throw new CircleDomainException("Target miner is already in a circle");

        // Add member to circle
        circle.AddMember(targetMiner.Id);
        _circleRepository.Update(circle);

        // Update target miner's circle reference
        targetMiner.JoinCircle(circle.Id);
        _minerRepository.Update(targetMiner);

        // Recalculate rates for all members if circle becomes valid
        if (circle.IsValid)
        {
            foreach (var member in circle.Members.Where(m => m.IsActive))
            {
                var memberMiner = await _minerRepository.GetByIdAsync(member.MinerId, cancellationToken);
                memberMiner?.RecalculateRate(circleBonus: circle.BonusMultiplier);
                if (memberMiner != null) _minerRepository.Update(memberMiner);
            }
        }

        await _circleRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
        return true;
    }
}
