using TakeoutSaaS.Application.App.Members.PointsMall.Commands;
using TakeoutSaaS.Application.App.Members.PointsMall.Dto;
using TakeoutSaaS.Domain.Membership.Entities;
using TakeoutSaaS.Domain.Membership.Enums;
using TakeoutSaaS.Domain.Membership.Repositories;

namespace TakeoutSaaS.Application.App.Members.PointsMall;

/// <summary>
/// 积分商城 DTO 构造器。
/// </summary>
internal static class MemberPointMallDtoFactory
{
    public static MemberPointMallProductAggregateSnapshot EmptyProductAggregate(long pointMallProductId)
    {
        return new MemberPointMallProductAggregateSnapshot
        {
            PointMallProductId = pointMallProductId,
            RedeemedCount = 0
        };
    }

    public static MemberPointMallRuleDto ToRuleDto(MemberPointMallRule source)
    {
        return new MemberPointMallRuleDto
        {
            StoreId = source.StoreId,
            IsConsumeRewardEnabled = source.IsConsumeRewardEnabled,
            ConsumeAmountPerStep = source.ConsumeAmountPerStep,
            ConsumeRewardPointsPerStep = source.ConsumeRewardPointsPerStep,
            IsReviewRewardEnabled = source.IsReviewRewardEnabled,
            ReviewRewardPoints = source.ReviewRewardPoints,
            IsRegisterRewardEnabled = source.IsRegisterRewardEnabled,
            RegisterRewardPoints = source.RegisterRewardPoints,
            IsSigninRewardEnabled = source.IsSigninRewardEnabled,
            SigninRewardPoints = source.SigninRewardPoints,
            ExpiryMode = MemberPointMallMapping.ToExpiryModeText(source.ExpiryMode)
        };
    }

    public static MemberPointMallRuleStatsDto ToRuleStatsDto(MemberPointMallRuleStatsSnapshot source)
    {
        return new MemberPointMallRuleStatsDto
        {
            TotalIssuedPoints = source.TotalIssuedPoints,
            RedeemedPoints = source.RedeemedPoints,
            PointMembers = source.PointMembers,
            RedeemRate = decimal.Round(source.RedeemRate, 1, MidpointRounding.AwayFromZero)
        };
    }

    public static MemberPointMallProductDto ToProductDto(
        MemberPointMallProduct source,
        MemberPointMallProductAggregateSnapshot aggregate)
    {
        var notifyChannels = MemberPointMallMapping.DeserializeNotifyChannels(source.NotifyChannelsJson)
            .Select(MemberPointMallMapping.ToNotifyChannelText)
            .ToList();

        return new MemberPointMallProductDto
        {
            PointMallProductId = source.Id,
            StoreId = source.StoreId,
            Name = source.Name,
            ImageUrl = source.ImageUrl,
            RedeemType = MemberPointMallMapping.ToRedeemTypeText(source.RedeemType),
            ProductId = source.ProductId,
            CouponTemplateId = source.CouponTemplateId,
            PhysicalName = source.PhysicalName,
            PickupMethod = source.PickupMethod.HasValue
                ? MemberPointMallMapping.ToPickupMethodText(source.PickupMethod.Value)
                : null,
            Description = source.Description,
            ExchangeType = MemberPointMallMapping.ToExchangeTypeText(source.ExchangeType),
            RequiredPoints = source.RequiredPoints,
            CashAmount = decimal.Round(source.CashAmount, 2, MidpointRounding.AwayFromZero),
            StockTotal = source.StockTotal,
            StockAvailable = source.StockAvailable,
            RedeemedCount = aggregate.RedeemedCount,
            PerMemberLimit = source.PerMemberLimit,
            NotifyChannels = notifyChannels,
            Status = MemberPointMallMapping.ToProductStatusText(source.Status),
            UpdatedAt = source.UpdatedAt ?? source.CreatedAt
        };
    }

    public static MemberPointMallRecordDto ToRecordDto(MemberPointMallRecord source)
    {
        return new MemberPointMallRecordDto
        {
            RecordId = source.Id,
            RecordNo = source.RecordNo,
            PointMallProductId = source.PointMallProductId,
            ProductName = source.ProductName,
            RedeemType = MemberPointMallMapping.ToRedeemTypeText(source.RedeemType),
            ExchangeType = MemberPointMallMapping.ToExchangeTypeText(source.ExchangeType),
            MemberId = source.MemberId,
            MemberName = source.MemberName,
            MemberMobileMasked = source.MemberMobileMasked,
            UsedPoints = source.UsedPoints,
            CashAmount = decimal.Round(source.CashAmount, 2, MidpointRounding.AwayFromZero),
            Status = MemberPointMallMapping.ToRecordStatusText(source.Status),
            RedeemedAt = source.RedeemedAt,
            IssuedAt = source.IssuedAt,
            VerifiedAt = source.VerifiedAt
        };
    }

    public static MemberPointMallRecordDetailDto ToRecordDetailDto(MemberPointMallRecord source)
    {
        return new MemberPointMallRecordDetailDto
        {
            RecordId = source.Id,
            RecordNo = source.RecordNo,
            PointMallProductId = source.PointMallProductId,
            ProductName = source.ProductName,
            RedeemType = MemberPointMallMapping.ToRedeemTypeText(source.RedeemType),
            ExchangeType = MemberPointMallMapping.ToExchangeTypeText(source.ExchangeType),
            MemberId = source.MemberId,
            MemberName = source.MemberName,
            MemberMobileMasked = source.MemberMobileMasked,
            UsedPoints = source.UsedPoints,
            CashAmount = decimal.Round(source.CashAmount, 2, MidpointRounding.AwayFromZero),
            Status = MemberPointMallMapping.ToRecordStatusText(source.Status),
            RedeemedAt = source.RedeemedAt,
            IssuedAt = source.IssuedAt,
            VerifiedAt = source.VerifiedAt,
            VerifyMethod = source.VerifyMethod.HasValue
                ? MemberPointMallMapping.ToVerifyMethodText(source.VerifyMethod.Value)
                : null,
            VerifyRemark = source.VerifyRemark,
            VerifiedBy = source.VerifiedBy
        };
    }

    public static MemberPointMallRecordStatsDto ToRecordStatsDto(MemberPointMallRecordStatsSnapshot source)
    {
        return new MemberPointMallRecordStatsDto
        {
            TodayRedeemCount = source.TodayRedeemCount,
            PendingPhysicalCount = source.PendingPhysicalCount,
            CurrentMonthUsedPoints = source.CurrentMonthUsedPoints
        };
    }

    public static MemberPointMallRule CreateRuleEntity(
        SavePointMallRuleCommand request,
        MemberPointMallExpiryMode expiryMode)
    {
        return new MemberPointMallRule
        {
            StoreId = request.StoreId,
            IsConsumeRewardEnabled = request.IsConsumeRewardEnabled,
            ConsumeAmountPerStep = request.ConsumeAmountPerStep,
            ConsumeRewardPointsPerStep = request.ConsumeRewardPointsPerStep,
            IsReviewRewardEnabled = request.IsReviewRewardEnabled,
            ReviewRewardPoints = request.ReviewRewardPoints,
            IsRegisterRewardEnabled = request.IsRegisterRewardEnabled,
            RegisterRewardPoints = request.RegisterRewardPoints,
            IsSigninRewardEnabled = request.IsSigninRewardEnabled,
            SigninRewardPoints = request.SigninRewardPoints,
            ExpiryMode = expiryMode
        };
    }

    public static MemberPointMallProduct CreateProductEntity(
        SavePointMallProductCommand request,
        MemberPointMallRedeemType redeemType,
        MemberPointMallExchangeType exchangeType,
        MemberPointMallProductStatus status,
        string name,
        string? imageUrl,
        string? physicalName,
        MemberPointMallPickupMethod? pickupMethod,
        string? description,
        int requiredPoints,
        decimal cashAmount,
        int stockTotal,
        int? perMemberLimit,
        IReadOnlyCollection<MemberPointMallNotifyChannel> notifyChannels)
    {
        return new MemberPointMallProduct
        {
            StoreId = request.StoreId,
            Name = name,
            ImageUrl = imageUrl,
            RedeemType = redeemType,
            ProductId = request.ProductId,
            CouponTemplateId = request.CouponTemplateId,
            PhysicalName = physicalName,
            PickupMethod = pickupMethod,
            Description = description,
            ExchangeType = exchangeType,
            RequiredPoints = requiredPoints,
            CashAmount = cashAmount,
            StockTotal = stockTotal,
            StockAvailable = stockTotal,
            PerMemberLimit = perMemberLimit,
            NotifyChannelsJson = MemberPointMallMapping.SerializeNotifyChannels(notifyChannels),
            Status = status
        };
    }
}
