using MediatR;
using TakeoutSaaS.Application.App.Subscriptions.Dto;
using TakeoutSaaS.Application.App.Subscriptions.Queries;
using TakeoutSaaS.Application.App.Tenants;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Domain.Tenants.Enums;

namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;

/// <summary>
/// 订阅详情查询处理器。
/// </summary>
public sealed class GetSubscriptionDetailQueryHandler(
    ISubscriptionRepository subscriptionRepository)
    : IRequestHandler<GetSubscriptionDetailQuery, SubscriptionDetailDto?>
{
    /// <inheritdoc />
    public async Task<SubscriptionDetailDto?> Handle(GetSubscriptionDetailQuery request, CancellationToken cancellationToken)
    {
        // 1. 查询订阅基础信息
        var detail = await subscriptionRepository.GetDetailAsync(
            request.SubscriptionId,
            cancellationToken);

        if (detail == null)
        {
            return null;
        }

        // 2. 查询配额使用情况
        var quotaUsages = await subscriptionRepository.GetQuotaUsagesAsync(
            detail.Subscription.TenantId,
            cancellationToken);

        var quotaUsageDtos = BuildQuotaUsageDtos(detail.Package, quotaUsages);

        // 3. 查询订阅变更历史（关联套餐信息）
        var histories = await subscriptionRepository.GetHistoryAsync(request.SubscriptionId, cancellationToken);

        var historyDtos = histories.Select(h => new SubscriptionHistoryDto
        {
            Id = h.History.Id,
            TenantSubscriptionId = h.History.TenantSubscriptionId,
            FromPackageId = h.History.FromPackageId,
            FromPackageName = h.FromPackageName,
            ToPackageId = h.History.ToPackageId,
            ToPackageName = h.ToPackageName,
            ChangeType = h.History.ChangeType,
            EffectiveFrom = h.History.EffectiveFrom,
            EffectiveTo = h.History.EffectiveTo,
            Amount = h.History.Amount,
            Currency = h.History.Currency,
            Notes = h.History.Notes,
            CreatedAt = h.History.CreatedAt
        }).ToList();

        // 4. 构建返回结果
        return new SubscriptionDetailDto
        {
            Id = detail.Subscription.Id,
            TenantId = detail.Subscription.TenantId,
            TenantName = detail.TenantName,
            TenantCode = detail.TenantCode,
            TenantPackageId = detail.Subscription.TenantPackageId,
            Package = detail.Package?.ToDto(),
            ScheduledPackageId = detail.Subscription.ScheduledPackageId,
            ScheduledPackage = detail.ScheduledPackage?.ToDto(),
            Status = detail.Subscription.Status,
            EffectiveFrom = detail.Subscription.EffectiveFrom,
            EffectiveTo = detail.Subscription.EffectiveTo,
            NextBillingDate = detail.Subscription.NextBillingDate,
            AutoRenew = detail.Subscription.AutoRenew,
            Notes = detail.Subscription.Notes,
            QuotaUsages = quotaUsageDtos,
            ChangeHistory = historyDtos,
            CreatedAt = detail.Subscription.CreatedAt,
            UpdatedAt = detail.Subscription.UpdatedAt
        };
    }

    private static List<QuotaUsageDto> BuildQuotaUsageDtos(
        TakeoutSaaS.Domain.Tenants.Entities.TenantPackage? package,
        IReadOnlyList<TakeoutSaaS.Domain.Tenants.Entities.TenantQuotaUsage> quotaUsages)
    {
        var usageByType = quotaUsages
            .GroupBy(u => u.QuotaType)
            .ToDictionary(g => g.Key, g => g.First());

        var baselineTypes = new List<(TenantQuotaType Type, decimal LimitValue)>();
        if (package != null)
        {
            baselineTypes.Add((TenantQuotaType.StoreCount, package.MaxStoreCount.HasValue ? package.MaxStoreCount.Value : 0));
            baselineTypes.Add((TenantQuotaType.AccountCount, package.MaxAccountCount.HasValue ? package.MaxAccountCount.Value : 0));
            baselineTypes.Add((TenantQuotaType.Storage, package.MaxStorageGb.HasValue ? package.MaxStorageGb.Value : 0));
            baselineTypes.Add((TenantQuotaType.SmsCredits, package.MaxSmsCredits.HasValue ? package.MaxSmsCredits.Value : 0));
            baselineTypes.Add((TenantQuotaType.DeliveryOrders, package.MaxDeliveryOrders.HasValue ? package.MaxDeliveryOrders.Value : 0));
        }

        var results = new List<QuotaUsageDto>();

        foreach (var (type, limitValue) in baselineTypes)
        {
            usageByType.TryGetValue(type, out var usage);
            results.Add(new QuotaUsageDto
            {
                Id = usage?.Id ?? 0,
                QuotaType = type,
                LimitValue = limitValue,
                UsedValue = usage?.UsedValue ?? 0,
                ResetCycle = usage?.ResetCycle,
                LastResetAt = usage?.LastResetAt
            });
        }

        // Add any extra quota usages not covered by package fields (e.g. promotion slots).
        foreach (var usage in usageByType.Values)
        {
            if (baselineTypes.Any(x => x.Type == usage.QuotaType))
            {
                continue;
            }

            results.Add(new QuotaUsageDto
            {
                Id = usage.Id,
                QuotaType = usage.QuotaType,
                LimitValue = usage.LimitValue,
                UsedValue = usage.UsedValue,
                ResetCycle = usage.ResetCycle,
                LastResetAt = usage.LastResetAt
            });
        }

        return results
            .OrderBy(x => (int)x.QuotaType)
            .ToList();
    }
}
