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

namespace MiningService.API.Application.Commands;

/// <summary>
/// EN: Handler for ApplyReferralCodeCommand.
/// VI: Handler cho ApplyReferralCodeCommand.
/// </summary>
public class ApplyReferralCodeCommandHandler : IRequestHandler<ApplyReferralCodeCommand, ApplyReferralResult>
{
    private readonly IReferralRepository _referralRepository;
    private readonly IMinerRepository _minerRepository;

    public ApplyReferralCodeCommandHandler(
        IReferralRepository referralRepository,
        IMinerRepository minerRepository)
    {
        _referralRepository = referralRepository;
        _minerRepository = minerRepository;
    }

    public async Task<ApplyReferralResult> Handle(ApplyReferralCodeCommand request, CancellationToken cancellationToken)
    {
        // Get the user who is applying the code
        var referredMiner = await _minerRepository.GetByUserIdAsync(request.UserId, cancellationToken)
            ?? throw new MinerNotFoundException($"Miner with UserId {request.UserId} not found");

        // Check if already has a referrer
        if (referredMiner.ReferredById.HasValue)
            throw new ReferralDomainException("User already has a referrer");

        // Find referrer by code
        var referrer = await _minerRepository.GetByReferralCodeAsync(request.ReferralCode, cancellationToken)
            ?? throw new ReferralDomainException("Invalid referral code");

        // Cannot self-refer
        if (referrer.Id == referredMiner.Id)
            throw new ReferralDomainException("Cannot use your own referral code");

        // Create referral (inactive until KYC)
        var referral = Referral.Create(referrer.Id, referredMiner.Id, request.ReferralCode);
        _referralRepository.Add(referral);

        await _referralRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        return new ApplyReferralResult(referral.Id, referrer.Id, referral.IsActive);
    }
}
