using TakeoutSaaS.Application.App.Coupons.PunchCard.Commands;
using TakeoutSaaS.Application.App.Coupons.PunchCard.Dto;
using TakeoutSaaS.Domain.Coupons.Entities;
using TakeoutSaaS.Domain.Coupons.Enums;
using TakeoutSaaS.Domain.Coupons.Repositories;

namespace TakeoutSaaS.Application.App.Coupons.PunchCard;

/// <summary>
/// 次卡 DTO 构造器。
/// </summary>
internal static class PunchCardDtoFactory
{
    public static PunchCardTemplateAggregateSnapshot EmptyAggregate(long templateId)
    {
        return new PunchCardTemplateAggregateSnapshot
        {
            TemplateId = templateId,
            SoldCount = 0,
            ActiveCount = 0,
            RevenueAmount = 0m
        };
    }

    public static PunchCardListItemDto ToListItemDto(
        PunchCardTemplate template,
        PunchCardTemplateAggregateSnapshot aggregate)
    {
        return new PunchCardListItemDto
        {
            Id = template.Id,
            Name = template.Name,
            CoverImageUrl = template.CoverImageUrl,
            SalePrice = template.SalePrice,
            OriginalPrice = template.OriginalPrice,
            TotalTimes = template.TotalTimes,
            ValiditySummary = PunchCardMapping.BuildValiditySummary(template),
            ScopeType = PunchCardMapping.ToScopeTypeText(template.ScopeType),
            UsageMode = PunchCardMapping.ToUsageModeText(template.UsageMode),
            UsageCapAmount = template.UsageCapAmount,
            DailyLimit = template.DailyLimit,
            Status = PunchCardMapping.ToTemplateStatusText(template.Status),
            IsDimmed = template.Status == PunchCardStatus.Disabled,
            SoldCount = aggregate.SoldCount,
            ActiveCount = aggregate.ActiveCount,
            RevenueAmount = decimal.Round(aggregate.RevenueAmount, 2, MidpointRounding.AwayFromZero),
            UpdatedAt = template.UpdatedAt ?? template.CreatedAt
        };
    }

    public static PunchCardDetailDto ToDetailDto(
        PunchCardTemplate template,
        PunchCardTemplateAggregateSnapshot aggregate)
    {
        return new PunchCardDetailDto
        {
            Id = template.Id,
            StoreId = template.StoreId,
            Name = template.Name,
            CoverImageUrl = template.CoverImageUrl,
            SalePrice = template.SalePrice,
            OriginalPrice = template.OriginalPrice,
            TotalTimes = template.TotalTimes,
            ValidityType = PunchCardMapping.ToValidityTypeText(template.ValidityType),
            ValidityDays = template.ValidityDays,
            ValidFrom = template.ValidFrom,
            ValidTo = template.ValidTo,
            Scope = new PunchCardScopeDto
            {
                ScopeType = PunchCardMapping.ToScopeTypeText(template.ScopeType),
                CategoryIds = PunchCardMapping.DeserializeSnowflakeIds(template.ScopeCategoryIdsJson),
                TagIds = PunchCardMapping.DeserializeSnowflakeIds(template.ScopeTagIdsJson),
                ProductIds = PunchCardMapping.DeserializeSnowflakeIds(template.ScopeProductIdsJson)
            },
            UsageMode = PunchCardMapping.ToUsageModeText(template.UsageMode),
            UsageCapAmount = template.UsageCapAmount,
            DailyLimit = template.DailyLimit,
            PerOrderLimit = template.PerOrderLimit,
            PerUserPurchaseLimit = template.PerUserPurchaseLimit,
            AllowTransfer = template.AllowTransfer,
            ExpireStrategy = PunchCardMapping.ToExpireStrategyText(template.ExpireStrategy),
            Description = template.Description,
            NotifyChannels = PunchCardMapping.DeserializeNotifyChannels(template.NotifyChannelsJson),
            Status = PunchCardMapping.ToTemplateStatusText(template.Status),
            SoldCount = aggregate.SoldCount,
            ActiveCount = aggregate.ActiveCount,
            RevenueAmount = decimal.Round(aggregate.RevenueAmount, 2, MidpointRounding.AwayFromZero),
            UpdatedAt = template.UpdatedAt ?? template.CreatedAt
        };
    }

    public static PunchCardStatsDto ToStatsDto(PunchCardTemplateStatsSnapshot source)
    {
        return new PunchCardStatsDto
        {
            OnSaleCount = source.OnSaleCount,
            TotalSoldCount = source.TotalSoldCount,
            TotalRevenueAmount = decimal.Round(source.TotalRevenueAmount, 2, MidpointRounding.AwayFromZero),
            ActiveInUseCount = source.ActiveInUseCount
        };
    }

    public static PunchCardUsageStatsDto ToUsageStatsDto(PunchCardUsageStatsSnapshot source)
    {
        return new PunchCardUsageStatsDto
        {
            TodayUsedCount = source.TodayUsedCount,
            MonthUsedCount = source.MonthUsedCount,
            ExpiringSoonCount = source.ExpiringSoonCount
        };
    }

    public static PunchCardUsageRecordDto ToUsageRecordDto(
        PunchCardUsageRecord record,
        PunchCardInstance? instance,
        PunchCardTemplate? template,
        DateTime nowUtc)
    {
        var resolvedTotalTimes = instance?.TotalTimes ?? template?.TotalTimes ?? 0;
        var status = record.StatusAfterUse;

        if (instance is not null)
        {
            status = PunchCardMapping.ResolveUsageRecordStatus(instance, record.RemainingTimesAfterUse, nowUtc);
        }

        return new PunchCardUsageRecordDto
        {
            Id = record.Id,
            RecordNo = record.RecordNo,
            PunchCardTemplateId = record.PunchCardTemplateId,
            PunchCardName = template?.Name ?? string.Empty,
            PunchCardInstanceId = record.PunchCardInstanceId,
            MemberName = instance?.MemberName ?? string.Empty,
            MemberPhoneMasked = instance?.MemberPhoneMasked ?? string.Empty,
            ProductName = record.ProductName,
            UsedAt = record.UsedAt,
            UsedTimes = record.UsedTimes,
            RemainingTimesAfterUse = record.RemainingTimesAfterUse,
            TotalTimes = resolvedTotalTimes,
            DisplayStatus = PunchCardMapping.ToUsageDisplayStatusText(status),
            ExtraPayAmount = record.ExtraPayAmount
        };
    }

    public static PunchCardTemplate CreateTemplateEntity(
        SavePunchCardTemplateCommand request,
        string normalizedName,
        string normalizedCoverImageUrl,
        decimal normalizedSalePrice,
        decimal? normalizedOriginalPrice,
        int normalizedTotalTimes,
        PunchCardValidityType validityType,
        int? normalizedValidityDays,
        DateTime? normalizedValidFrom,
        DateTime? normalizedValidTo,
        PunchCardScopeType scopeType,
        string normalizedCategoryIdsJson,
        string normalizedTagIdsJson,
        string normalizedProductIdsJson,
        PunchCardUsageMode usageMode,
        decimal? normalizedUsageCapAmount,
        int? normalizedDailyLimit,
        int? normalizedPerOrderLimit,
        int? normalizedPerUserPurchaseLimit,
        PunchCardExpireStrategy expireStrategy,
        string? normalizedDescription,
        string normalizedNotifyChannelsJson)
    {
        return new PunchCardTemplate
        {
            StoreId = request.StoreId,
            Name = normalizedName,
            CoverImageUrl = string.IsNullOrWhiteSpace(normalizedCoverImageUrl)
                ? null
                : normalizedCoverImageUrl,
            SalePrice = normalizedSalePrice,
            OriginalPrice = normalizedOriginalPrice,
            TotalTimes = normalizedTotalTimes,
            ValidityType = validityType,
            ValidityDays = normalizedValidityDays,
            ValidFrom = normalizedValidFrom,
            ValidTo = normalizedValidTo,
            ScopeType = scopeType,
            ScopeCategoryIdsJson = normalizedCategoryIdsJson,
            ScopeTagIdsJson = normalizedTagIdsJson,
            ScopeProductIdsJson = normalizedProductIdsJson,
            UsageMode = usageMode,
            UsageCapAmount = normalizedUsageCapAmount,
            DailyLimit = normalizedDailyLimit,
            PerOrderLimit = normalizedPerOrderLimit,
            PerUserPurchaseLimit = normalizedPerUserPurchaseLimit,
            AllowTransfer = request.AllowTransfer,
            ExpireStrategy = expireStrategy,
            Description = normalizedDescription,
            NotifyChannelsJson = normalizedNotifyChannelsJson,
            Status = PunchCardStatus.Enabled
        };
    }

    public static string GenerateInstanceNo(DateTime nowUtc)
    {
        return $"PKI{nowUtc:yyyyMMddHHmmssfff}{Random.Shared.Next(1000, 9999)}";
    }

    public static string GenerateRecordNo(DateTime nowUtc)
    {
        return $"PK{nowUtc:yyyyMMddHHmmssfff}{Random.Shared.Next(1000, 9999)}";
    }
}
