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

/// <summary>
/// 次卡模板保存处理器。
/// </summary>
public sealed class SavePunchCardTemplateCommandHandler(
    IPunchCardRepository repository,
    ITenantProvider tenantProvider)
    : IRequestHandler<SavePunchCardTemplateCommand, PunchCardDetailDto>
{
    /// <inheritdoc />
    public async Task<PunchCardDetailDto> Handle(
        SavePunchCardTemplateCommand request,
        CancellationToken cancellationToken)
    {
        var tenantId = tenantProvider.GetCurrentTenantId();

        var normalizedName = PunchCardMapping.NormalizeName(request.Name);
        var normalizedCoverImageUrl = PunchCardMapping.NormalizeOptionalCoverUrl(request.CoverImageUrl);
        var normalizedSalePrice = PunchCardMapping.NormalizeAmount(request.SalePrice, "salePrice", false);
        var normalizedOriginalPrice = PunchCardMapping.NormalizeOptionalAmount(request.OriginalPrice, "originalPrice", true);
        var normalizedTotalTimes = PunchCardMapping.NormalizeRequiredPositiveInt(request.TotalTimes, "totalTimes", 10_000);

        if (normalizedOriginalPrice.HasValue && normalizedOriginalPrice.Value < normalizedSalePrice)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "originalPrice 不能小于 salePrice");
        }

        var validityType = PunchCardMapping.ParseValidityType(request.ValidityType);
        var (normalizedValidityDays, normalizedValidFrom, normalizedValidTo) = PunchCardMapping.NormalizeValidity(
            validityType,
            request.ValidityDays,
            request.ValidFrom,
            request.ValidTo);

        var scopeType = PunchCardMapping.ParseScopeType(request.ScopeType);
        var (normalizedCategoryIds, normalizedTagIds, normalizedProductIds) = PunchCardMapping.NormalizeScopeIds(
            scopeType,
            request.ScopeCategoryIds,
            request.ScopeTagIds,
            request.ScopeProductIds);

        var usageMode = PunchCardMapping.ParseUsageMode(request.UsageMode);
        var normalizedUsageCapAmount = usageMode switch
        {
            PunchCardUsageMode.Free => null,
            PunchCardUsageMode.Cap => PunchCardMapping.NormalizeOptionalAmount(request.UsageCapAmount, "usageCapAmount", false),
            _ => null
        };

        if (usageMode == PunchCardUsageMode.Cap && !normalizedUsageCapAmount.HasValue)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "usageCapAmount 不能为空");
        }

        var normalizedDailyLimit = PunchCardMapping.NormalizeOptionalLimit(request.DailyLimit, "dailyLimit", normalizedTotalTimes);
        var normalizedPerOrderLimit = PunchCardMapping.NormalizeOptionalLimit(request.PerOrderLimit, "perOrderLimit", normalizedTotalTimes);
        var normalizedPerUserPurchaseLimit = PunchCardMapping.NormalizeOptionalLimit(request.PerUserPurchaseLimit, "perUserPurchaseLimit", 1000);

        var expireStrategy = PunchCardMapping.ParseExpireStrategy(request.ExpireStrategy);
        var normalizedDescription = PunchCardMapping.NormalizeOptionalDescription(request.Description);
        var normalizedNotifyChannelsJson = PunchCardMapping.SerializeNotifyChannels(request.NotifyChannels);

        var normalizedCategoryIdsJson = PunchCardMapping.SerializeSnowflakeIds(normalizedCategoryIds);
        var normalizedTagIdsJson = PunchCardMapping.SerializeSnowflakeIds(normalizedTagIds);
        var normalizedProductIdsJson = PunchCardMapping.SerializeSnowflakeIds(normalizedProductIds);

        if (!request.TemplateId.HasValue)
        {
            var newEntity = PunchCardDtoFactory.CreateTemplateEntity(
                request,
                normalizedName,
                normalizedCoverImageUrl,
                normalizedSalePrice,
                normalizedOriginalPrice,
                normalizedTotalTimes,
                validityType,
                normalizedValidityDays,
                normalizedValidFrom,
                normalizedValidTo,
                scopeType,
                normalizedCategoryIdsJson,
                normalizedTagIdsJson,
                normalizedProductIdsJson,
                usageMode,
                normalizedUsageCapAmount,
                normalizedDailyLimit,
                normalizedPerOrderLimit,
                normalizedPerUserPurchaseLimit,
                expireStrategy,
                normalizedDescription,
                normalizedNotifyChannelsJson);

            await repository.AddTemplateAsync(newEntity, cancellationToken);
            await repository.SaveChangesAsync(cancellationToken);

            return PunchCardDtoFactory.ToDetailDto(
                newEntity,
                PunchCardDtoFactory.EmptyAggregate(newEntity.Id));
        }

        var entity = await repository.FindTemplateByIdAsync(
            tenantId,
            request.StoreId,
            request.TemplateId.Value,
            cancellationToken)
            ?? throw new BusinessException(ErrorCodes.NotFound, "次卡不存在");

        entity.Name = normalizedName;
        entity.CoverImageUrl = string.IsNullOrWhiteSpace(normalizedCoverImageUrl)
            ? null
            : normalizedCoverImageUrl;
        entity.SalePrice = normalizedSalePrice;
        entity.OriginalPrice = normalizedOriginalPrice;
        entity.TotalTimes = normalizedTotalTimes;
        entity.ValidityType = validityType;
        entity.ValidityDays = normalizedValidityDays;
        entity.ValidFrom = normalizedValidFrom;
        entity.ValidTo = normalizedValidTo;
        entity.ScopeType = scopeType;
        entity.ScopeCategoryIdsJson = normalizedCategoryIdsJson;
        entity.ScopeTagIdsJson = normalizedTagIdsJson;
        entity.ScopeProductIdsJson = normalizedProductIdsJson;
        entity.UsageMode = usageMode;
        entity.UsageCapAmount = normalizedUsageCapAmount;
        entity.DailyLimit = normalizedDailyLimit;
        entity.PerOrderLimit = normalizedPerOrderLimit;
        entity.PerUserPurchaseLimit = normalizedPerUserPurchaseLimit;
        entity.AllowTransfer = request.AllowTransfer;
        entity.ExpireStrategy = expireStrategy;
        entity.Description = normalizedDescription;
        entity.NotifyChannelsJson = normalizedNotifyChannelsJson;

        await repository.UpdateTemplateAsync(entity, cancellationToken);
        await repository.SaveChangesAsync(cancellationToken);

        var aggregateMap = await repository.GetTemplateAggregateByTemplateIdsAsync(
            tenantId,
            request.StoreId,
            [entity.Id],
            cancellationToken);

        var aggregate = aggregateMap.TryGetValue(entity.Id, out var value)
            ? value
            : PunchCardDtoFactory.EmptyAggregate(entity.Id);

        return PunchCardDtoFactory.ToDetailDto(entity, aggregate);
    }
}
