using MediatR;
using MembershipService.Domain.AggregatesModel.ExperienceAggregate;
using MembershipService.Domain.AggregatesModel.LevelAggregate;
using MembershipService.Domain.AggregatesModel.MemberAggregate;

namespace MembershipService.API.Application.Commands;

/// <summary>
/// EN: Handler for adding experience points to a member.
/// VI: Handler để thêm điểm kinh nghiệm cho member.
/// </summary>
public class AddExperienceCommandHandler : IRequestHandler<AddExperienceCommand, AddExperienceResult>
{
    private readonly IMemberRepository _memberRepository;
    private readonly ILevelDefinitionRepository _levelDefinitionRepository;
    private readonly IExperienceTransactionRepository _experienceTransactionRepository;
    private readonly ILogger<AddExperienceCommandHandler> _logger;

    public AddExperienceCommandHandler(
        IMemberRepository memberRepository,
        ILevelDefinitionRepository levelDefinitionRepository,
        IExperienceTransactionRepository experienceTransactionRepository,
        ILogger<AddExperienceCommandHandler> logger)
    {
        _memberRepository = memberRepository ?? throw new ArgumentNullException(nameof(memberRepository));
        _levelDefinitionRepository = levelDefinitionRepository ?? throw new ArgumentNullException(nameof(levelDefinitionRepository));
        _experienceTransactionRepository = experienceTransactionRepository ?? throw new ArgumentNullException(nameof(experienceTransactionRepository));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task<AddExperienceResult> Handle(AddExperienceCommand request, CancellationToken cancellationToken)
    {
        // EN: Get member
        // VI: Lấy member
        var member = await _memberRepository.GetByIdAsync(request.MemberId, cancellationToken);
        if (member == null)
        {
            throw new KeyNotFoundException($"Member {request.MemberId} not found");
        }

        // EN: Get active level rules
        // VI: Lấy level rules đang active
        var levelRules = await _levelDefinitionRepository.GetAllActiveAsync();
        if (!levelRules.Any())
        {
            throw new InvalidOperationException("No active level definitions found");
        }

        // EN: Get experience source
        // VI: Lấy experience source
        var source = ExperienceSource.FromValue<ExperienceSource>(request.SourceId);

        // EN: Store previous level
        // VI: Lưu level trước đó
        var previousLevel = member.CurrentLevel;

        // EN: Add experience and get transaction
        // VI: Thêm experience và lấy transaction
        var transaction = member.AddExperience(
            request.Points,
            source,
            levelRules,
            request.ReferenceId,
            request.Metadata);

        // EN: Save transaction
        // VI: Lưu transaction
        _experienceTransactionRepository.Add(transaction);

        // EN: Update member
        // VI: Cập nhật member
        _memberRepository.Update(member);

        // EN: Save all changes
        // VI: Lưu tất cả thay đổi
        await _memberRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        _logger.LogInformation(
            "Added {Points} EXP to member {MemberId} from {Source}. Level: {PrevLevel} -> {NewLevel}",
            request.Points, request.MemberId, source.Name, previousLevel, member.CurrentLevel);

        return new AddExperienceResult
        {
            MemberId = member.Id,
            PointsAdded = request.Points,
            CurrentExp = member.CurrentExp,
            TotalExpEarned = member.TotalExpEarned,
            PreviousLevel = previousLevel,
            CurrentLevel = member.CurrentLevel,
            LeveledUp = member.CurrentLevel > previousLevel,
            TransactionId = transaction.Id
        };
    }
}
