using MediatR;
using TakeoutSaaS.Application.App.Coupons.FullReduction.Commands;
using TakeoutSaaS.Application.App.Coupons.FullReduction.Dto;
using TakeoutSaaS.Domain.Coupons.Entities;
using TakeoutSaaS.Domain.Coupons.Enums;
using TakeoutSaaS.Domain.Coupons.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Coupons.FullReduction.Handlers;

/// <summary>
/// 保存满减活动命令处理器。
/// </summary>
public sealed class SaveFullReductionCampaignCommandHandler(
    IPromotionCampaignRepository promotionCampaignRepository,
    ITenantProvider tenantProvider)
    : IRequestHandler<SaveFullReductionCampaignCommand, FullReductionDetailDto>
{
    /// <inheritdoc />
    public async Task<FullReductionDetailDto> Handle(SaveFullReductionCampaignCommand request, CancellationToken cancellationToken)
    {
        var normalizedName = request.Name.Trim();
        if (string.IsNullOrWhiteSpace(normalizedName))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "活动名称不能为空");
        }

        if (normalizedName.Length > 64)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "活动名称长度不能超过 64");
        }

        var normalizedStartAt = FullReductionMapping.NormalizeStartOfDay(request.StartAt);
        var normalizedEndAt = FullReductionMapping.NormalizeEndOfDay(request.EndAt);
        if (normalizedStartAt > normalizedEndAt)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "活动开始时间不能晚于结束时间");
        }

        var tenantId = tenantProvider.GetCurrentTenantId();

        var existingMetrics = default(FullReductionMetricsDto);
        PromotionCampaign campaign;
        if (!request.CampaignId.HasValue)
        {
            var rules = FullReductionMapping.NormalizeRulesForSave(
                request.ActivityType,
                request.ReduceTiers,
                request.GiftRule,
                request.SecondHalfRule,
                request.Channels,
                request.StoreScopeMode,
                request.StoreScopeStoreIds,
                request.ScopeStoreId,
                request.StackWithCoupon,
                request.Description,
                request.Metrics,
                null);

            campaign = FullReductionDtoFactory.CreateNewCampaign(
                normalizedName,
                normalizedStartAt,
                normalizedEndAt,
                FullReductionMapping.SerializeRules(rules),
                rules.Description);

            await promotionCampaignRepository.AddAsync(campaign, cancellationToken);
            await promotionCampaignRepository.SaveChangesAsync(cancellationToken);

            return FullReductionDtoFactory.ToDetailDto(campaign, rules, DateTime.UtcNow);
        }

        campaign = await promotionCampaignRepository.FindByIdAsync(
            request.CampaignId.Value,
            tenantId,
            PromotionType.FullReduction,
            cancellationToken)
            ?? throw new BusinessException(ErrorCodes.NotFound, "活动不存在");

        var existingRules = FullReductionMapping.DeserializeRules(campaign.RulesJson, campaign.Id);
        if (!existingRules.StoreIds.Contains(request.OperationStoreId))
        {
            throw new BusinessException(ErrorCodes.NotFound, "活动不存在");
        }

        existingMetrics = existingRules.Metrics;

        var normalizedRules = FullReductionMapping.NormalizeRulesForSave(
            request.ActivityType,
            request.ReduceTiers,
            request.GiftRule,
            request.SecondHalfRule,
            request.Channels,
            request.StoreScopeMode,
            request.StoreScopeStoreIds,
            request.ScopeStoreId,
            request.StackWithCoupon,
            request.Description,
            request.Metrics,
            existingMetrics);

        campaign.Name = normalizedName;
        campaign.StartAt = normalizedStartAt;
        campaign.EndAt = normalizedEndAt;
        campaign.RulesJson = FullReductionMapping.SerializeRules(normalizedRules);
        campaign.AudienceDescription = normalizedRules.Description;

        if (campaign.Status != PromotionStatus.Completed)
        {
            campaign.Status = PromotionStatus.Active;
        }

        await promotionCampaignRepository.UpdateAsync(campaign, cancellationToken);
        await promotionCampaignRepository.SaveChangesAsync(cancellationToken);

        return FullReductionDtoFactory.ToDetailDto(campaign, normalizedRules, DateTime.UtcNow);
    }
}


