using MediatR;
using TakeoutSaaS.Application.App.Members.StoredCard.Commands;
using TakeoutSaaS.Application.App.Members.StoredCard.Dto;
using TakeoutSaaS.Domain.Membership.Entities;
using TakeoutSaaS.Domain.Membership.Enums;
using TakeoutSaaS.Domain.Membership.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Members.StoredCard.Handlers;

/// <summary>
/// 写入储值卡充值记录处理器。
/// </summary>
public sealed class WriteStoredCardRechargeRecordCommandHandler(
    IStoredCardRepository storedCardRepository,
    IMemberRepository memberRepository,
    ITenantProvider tenantProvider)
    : IRequestHandler<WriteStoredCardRechargeRecordCommand, MemberStoredCardRechargeRecordDto>
{
    /// <inheritdoc />
    public async Task<MemberStoredCardRechargeRecordDto> Handle(
        WriteStoredCardRechargeRecordCommand request,
        CancellationToken cancellationToken)
    {
        var tenantId = tenantProvider.GetCurrentTenantId();
        var rechargeAmount = MemberStoredCardMapping.NormalizeRechargeAmount(request.RechargeAmount);
        var giftAmount = MemberStoredCardMapping.NormalizeGiftAmount(request.GiftAmount);
        var paymentMethod = MemberStoredCardMapping.ParsePaymentMethod(request.PaymentMethod);
        var remark = MemberStoredCardMapping.NormalizeOptionalRemark(request.Remark);
        var rechargedAt = request.RechargedAt.HasValue
            ? MemberStoredCardMapping.NormalizeUtc(request.RechargedAt.Value)
            : DateTime.UtcNow;

        MemberStoredCardPlan? plan = null;
        if (request.PlanId.HasValue && request.PlanId.Value > 0)
        {
            plan = await storedCardRepository.FindPlanByIdAsync(
                tenantId,
                request.StoreId,
                request.PlanId.Value,
                cancellationToken)
                ?? throw new BusinessException(ErrorCodes.NotFound, "储值卡方案不存在");

            if (plan.Status != MemberStoredCardPlanStatus.Enabled)
            {
                throw new BusinessException(ErrorCodes.BadRequest, "储值卡方案未启用");
            }

            if (plan.RechargeAmount != rechargeAmount || plan.GiftAmount != giftAmount)
            {
                throw new BusinessException(ErrorCodes.BadRequest, "充值金额与储值卡方案不一致");
            }
        }

        var member = await memberRepository.FindProfileByIdAsync(
            tenantId,
            request.MemberId,
            cancellationToken)
            ?? throw new BusinessException(ErrorCodes.NotFound, "会员不存在");

        var arrivedAmount = decimal.Round(rechargeAmount + giftAmount, 2, MidpointRounding.AwayFromZero);
        var record = new MemberStoredCardRechargeRecord
        {
            StoreId = request.StoreId,
            MemberId = member.Id,
            PlanId = plan?.Id,
            RecordNo = MemberStoredCardMapping.BuildRechargeRecordNo(rechargedAt),
            MemberName = MemberStoredCardMapping.ResolveMemberName(member),
            MemberMobileMasked = MemberStoredCardMapping.ResolveMemberMobileMasked(member),
            RechargeAmount = rechargeAmount,
            GiftAmount = giftAmount,
            ArrivedAmount = arrivedAmount,
            PaymentMethod = paymentMethod,
            Remark = remark,
            RechargedAt = rechargedAt
        };

        member.StoredRechargeBalance = decimal.Round(member.StoredRechargeBalance + rechargeAmount, 2, MidpointRounding.AwayFromZero);
        member.StoredGiftBalance = decimal.Round(member.StoredGiftBalance + giftAmount, 2, MidpointRounding.AwayFromZero);
        member.StoredBalance = decimal.Round(member.StoredRechargeBalance + member.StoredGiftBalance, 2, MidpointRounding.AwayFromZero);

        await memberRepository.UpdateProfileAsync(member, cancellationToken);
        await storedCardRepository.AddRechargeRecordAsync(record, cancellationToken);
        await storedCardRepository.SaveChangesAsync(cancellationToken);

        return MemberStoredCardDtoFactory.ToRechargeRecordDto(record);
    }
}
