using MediatR;
using TakeoutSaaS.Application.App.Coupons.NewCustomer.Commands;
using TakeoutSaaS.Application.App.Coupons.NewCustomer.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.NewCustomer.Handlers;

/// <summary>
/// 保存新客有礼配置处理器。
/// </summary>
public sealed class SaveNewCustomerSettingsCommandHandler(
    INewCustomerGiftRepository repository,
    ITenantProvider tenantProvider)
    : IRequestHandler<SaveNewCustomerSettingsCommand, NewCustomerSettingsDto>
{
    /// <inheritdoc />
    public async Task<NewCustomerSettingsDto> Handle(
        SaveNewCustomerSettingsCommand request,
        CancellationToken cancellationToken)
    {
        if (request.StoreId <= 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "storeId 参数不合法");
        }

        var tenantId = tenantProvider.GetCurrentTenantId();
        var giftType = NewCustomerMapping.ParseGiftType(request.GiftType);
        var shareChannels = NewCustomerMapping.NormalizeShareChannels(request.ShareChannels);

        var welcomeRules = NewCustomerMapping.NormalizeCouponRulesForSave(
            request.StoreId,
            NewCustomerCouponScene.Welcome,
            request.WelcomeCoupons);

        var inviterRules = NewCustomerMapping.NormalizeCouponRulesForSave(
            request.StoreId,
            NewCustomerCouponScene.InviterReward,
            request.InviterCoupons);

        var inviteeRules = NewCustomerMapping.NormalizeCouponRulesForSave(
            request.StoreId,
            NewCustomerCouponScene.InviteeReward,
            request.InviteeCoupons);

        if (giftType == NewCustomerGiftType.Coupon && welcomeRules.Count == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "优惠券包至少需要一张券");
        }

        if (giftType == NewCustomerGiftType.Direct)
        {
            if (!request.DirectReduceAmount.HasValue || request.DirectReduceAmount.Value <= 0m)
            {
                throw new BusinessException(ErrorCodes.BadRequest, "directReduceAmount 必须大于 0");
            }

            if (!request.DirectMinimumSpend.HasValue || request.DirectMinimumSpend.Value < 0m)
            {
                throw new BusinessException(ErrorCodes.BadRequest, "directMinimumSpend 不能小于 0");
            }
        }

        if (request.InviteEnabled && (inviterRules.Count == 0 || inviteeRules.Count == 0))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "开启邀请后必须配置邀请人和被邀请人奖励券");
        }

        var setting = await repository.FindSettingByStoreIdAsync(tenantId, request.StoreId, cancellationToken);
        var isNewSetting = setting is null;
        if (setting is null)
        {
            setting = new NewCustomerGiftSetting
            {
                StoreId = request.StoreId
            };

            await repository.AddSettingAsync(setting, cancellationToken);
        }

        setting.GiftEnabled = request.GiftEnabled;
        setting.GiftType = giftType;
        setting.DirectReduceAmount = giftType == NewCustomerGiftType.Direct
            ? decimal.Round(request.DirectReduceAmount!.Value, 2, MidpointRounding.AwayFromZero)
            : null;
        setting.DirectMinimumSpend = giftType == NewCustomerGiftType.Direct
            ? decimal.Round(request.DirectMinimumSpend!.Value, 2, MidpointRounding.AwayFromZero)
            : null;
        setting.InviteEnabled = request.InviteEnabled;
        setting.ShareChannelsJson = NewCustomerMapping.SerializeShareChannels(shareChannels);

        if (!isNewSetting)
        {
            await repository.UpdateSettingAsync(setting, cancellationToken);
        }

        var allRules = new List<NewCustomerCouponRule>(welcomeRules.Count + inviterRules.Count + inviteeRules.Count);
        allRules.AddRange(welcomeRules);
        allRules.AddRange(inviterRules);
        allRules.AddRange(inviteeRules);

        await repository.ReplaceCouponRulesAsync(
            tenantId,
            request.StoreId,
            allRules,
            cancellationToken);

        await repository.SaveChangesAsync(cancellationToken);

        return new NewCustomerSettingsDto
        {
            StoreId = request.StoreId,
            GiftEnabled = setting.GiftEnabled,
            GiftType = NewCustomerMapping.ToGiftTypeText(setting.GiftType),
            DirectReduceAmount = setting.DirectReduceAmount,
            DirectMinimumSpend = setting.DirectMinimumSpend,
            InviteEnabled = setting.InviteEnabled,
            ShareChannels = shareChannels,
            WelcomeCoupons = welcomeRules.Select(NewCustomerMapping.ToCouponRuleDto).ToList(),
            InviterCoupons = inviterRules.Select(NewCustomerMapping.ToCouponRuleDto).ToList(),
            InviteeCoupons = inviteeRules.Select(NewCustomerMapping.ToCouponRuleDto).ToList(),
            UpdatedAt = DateTime.UtcNow
        };
    }
}
