using MediatR;
using Microsoft.Extensions.Logging;
using PromotionService.Domain.AggregatesModel.CampaignAggregate;
using PromotionService.Domain.Exceptions;

namespace PromotionService.API.Application.Commands;

/// <summary>
/// EN: Handler for UpdateCampaignCommand.
/// VI: Handler cho UpdateCampaignCommand.
/// </summary>
public class UpdateCampaignCommandHandler : IRequestHandler<UpdateCampaignCommand, bool>
{
    private readonly ICampaignRepository _campaignRepository;
    private readonly ILogger<UpdateCampaignCommandHandler> _logger;

    public UpdateCampaignCommandHandler(
        ICampaignRepository campaignRepository,
        ILogger<UpdateCampaignCommandHandler> logger)
    {
        _campaignRepository = campaignRepository;
        _logger = logger;
    }

    public async Task<bool> Handle(UpdateCampaignCommand request, CancellationToken cancellationToken)
    {
        var campaign = await _campaignRepository.GetByIdAsync(request.CampaignId)
            ?? throw new PromotionDomainException($"Campaign {request.CampaignId} not found");

        campaign.Update(request.Name, request.Description, request.StartDate, request.EndDate, request.MaxPerUser);
        _campaignRepository.Update(campaign);
        await _campaignRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        _logger.LogInformation("Campaign {CampaignId} updated", request.CampaignId);
        return true;
    }
}

/// <summary>
/// EN: Handler for CompleteCampaignCommand.
/// VI: Handler cho CompleteCampaignCommand.
/// </summary>
public class CompleteCampaignCommandHandler : IRequestHandler<CompleteCampaignCommand, bool>
{
    private readonly ICampaignRepository _campaignRepository;
    private readonly ILogger<CompleteCampaignCommandHandler> _logger;

    public CompleteCampaignCommandHandler(
        ICampaignRepository campaignRepository,
        ILogger<CompleteCampaignCommandHandler> logger)
    {
        _campaignRepository = campaignRepository;
        _logger = logger;
    }

    public async Task<bool> Handle(CompleteCampaignCommand request, CancellationToken cancellationToken)
    {
        var campaign = await _campaignRepository.GetByIdAsync(request.CampaignId)
            ?? throw new PromotionDomainException($"Campaign {request.CampaignId} not found");

        campaign.Complete();
        _campaignRepository.Update(campaign);
        await _campaignRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        _logger.LogInformation("Campaign {CampaignId} force completed", request.CampaignId);
        return true;
    }
}

/// <summary>
/// EN: Handler for DeleteCampaignCommand (soft delete).
/// VI: Handler cho DeleteCampaignCommand (xóa mềm).
/// </summary>
public class DeleteCampaignCommandHandler : IRequestHandler<DeleteCampaignCommand, bool>
{
    private readonly ICampaignRepository _campaignRepository;
    private readonly ILogger<DeleteCampaignCommandHandler> _logger;

    public DeleteCampaignCommandHandler(
        ICampaignRepository campaignRepository,
        ILogger<DeleteCampaignCommandHandler> logger)
    {
        _campaignRepository = campaignRepository;
        _logger = logger;
    }

    public async Task<bool> Handle(DeleteCampaignCommand request, CancellationToken cancellationToken)
    {
        var campaign = await _campaignRepository.GetByIdAsync(request.CampaignId)
            ?? throw new PromotionDomainException($"Campaign {request.CampaignId} not found");

        campaign.Cancel(); // Soft delete = Cancel
        _campaignRepository.Update(campaign);
        await _campaignRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        _logger.LogInformation("Campaign {CampaignId} soft deleted (cancelled)", request.CampaignId);
        return true;
    }
}

/// <summary>
/// EN: Handler for RevokeVoucherCommand.
/// VI: Handler cho RevokeVoucherCommand.
/// </summary>
public class RevokeVoucherCommandHandler : IRequestHandler<RevokeVoucherCommand, bool>
{
    private readonly ICampaignRepository _campaignRepository;
    private readonly ILogger<RevokeVoucherCommandHandler> _logger;

    public RevokeVoucherCommandHandler(
        ICampaignRepository campaignRepository,
        ILogger<RevokeVoucherCommandHandler> logger)
    {
        _campaignRepository = campaignRepository;
        _logger = logger;
    }

    public async Task<bool> Handle(RevokeVoucherCommand request, CancellationToken cancellationToken)
    {
        var voucher = await _campaignRepository.GetVoucherByIdAsync(request.VoucherId)
            ?? throw new PromotionDomainException($"Voucher {request.VoucherId} not found");

        voucher.Expire(); // Revoke = Mark as expired
        await _campaignRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        _logger.LogInformation("Voucher {VoucherId} revoked. Reason: {Reason}", request.VoucherId, request.Reason);
        return true;
    }
}

/// <summary>
/// EN: Handler for ExtendVoucherExpiryCommand.
/// VI: Handler cho ExtendVoucherExpiryCommand.
/// </summary>
public class ExtendVoucherExpiryCommandHandler : IRequestHandler<ExtendVoucherExpiryCommand, bool>
{
    private readonly ICampaignRepository _campaignRepository;
    private readonly ILogger<ExtendVoucherExpiryCommandHandler> _logger;

    public ExtendVoucherExpiryCommandHandler(
        ICampaignRepository campaignRepository,
        ILogger<ExtendVoucherExpiryCommandHandler> logger)
    {
        _campaignRepository = campaignRepository;
        _logger = logger;
    }

    public async Task<bool> Handle(ExtendVoucherExpiryCommand request, CancellationToken cancellationToken)
    {
        var voucher = await _campaignRepository.GetVoucherByIdAsync(request.VoucherId)
            ?? throw new PromotionDomainException($"Voucher {request.VoucherId} not found");

        voucher.ExtendExpiry(request.AdditionalDays);
        await _campaignRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        _logger.LogInformation("Voucher {VoucherId} expiry extended by {Days} days", request.VoucherId, request.AdditionalDays);
        return true;
    }
}
