using MediatR;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using TakeoutSaaS.Application.App.Subscriptions.Commands;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Shared.Abstractions.Ids;

namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;

/// <summary>
/// 自动续费处理器：生成续费账单（幂等）。
/// </summary>
public sealed class ProcessAutoRenewalCommandHandler(
    ISubscriptionRepository subscriptionRepository,
    ITenantBillingRepository billingRepository,
    IIdGenerator idGenerator,
    ILogger<ProcessAutoRenewalCommandHandler> logger)
    : IRequestHandler<ProcessAutoRenewalCommand, ProcessAutoRenewalResult>
{
    /// <inheritdoc />
    public async Task<ProcessAutoRenewalResult> Handle(ProcessAutoRenewalCommand request, CancellationToken cancellationToken)
    {
        // 1. 计算续费阈值时间
        var now = DateTime.UtcNow;
        var renewalThreshold = now.AddDays(request.RenewalDaysBeforeExpiry);

        // 2. 查询候选订阅（含套餐）
        var candidates = await subscriptionRepository.FindAutoRenewalCandidatesAsync(
            now,
            renewalThreshold,
            cancellationToken);
        var createdBillCount = 0;

        // 3. 遍历候选订阅，生成账单
        foreach (var candidate in candidates)
        {
            // 3.1 幂等校验：同一周期开始时间只允许存在一张未取消账单
            var periodStart = candidate.Subscription.EffectiveTo;
            var exists = await billingRepository.ExistsNotCancelledByPeriodStartAsync(
                candidate.Subscription.TenantId,
                periodStart,
                cancellationToken);

            if (exists)
            {
                logger.LogInformation(
                    "自动续费：租户 {TenantId} 订阅 {SubscriptionId} 已存在周期 {PeriodStart} 的续费账单，跳过",
                    candidate.Subscription.TenantId,
                    candidate.Subscription.Id,
                    periodStart);
                continue;
            }

            // 3.2 计算续费周期（月）
            var durationMonths = CalculateDurationMonths(candidate.Subscription.EffectiveFrom, candidate.Subscription.EffectiveTo);
            if (durationMonths <= 0)
            {
                durationMonths = 1;
            }

            // 3.3 计算账单周期与金额
            var periodEnd = periodStart.AddMonths(durationMonths);
            var amountDue = CalculateRenewalAmount(candidate.Package, durationMonths);

            // 3.4 生成账单（Pending）
            var statementNo = $"BILL-{now:yyyyMMddHHmmss}-{candidate.Subscription.TenantId}-{candidate.Subscription.Id}";
            var lineItemsJson = JsonSerializer.Serialize(new
            {
                PackageName = candidate.Package.Name,
                RenewalMonths = durationMonths,
                SubscriptionId = candidate.Subscription.Id
            });

            await billingRepository.AddAsync(new TenantBillingStatement
            {
                Id = idGenerator.NextId(),
                TenantId = candidate.Subscription.TenantId,
                StatementNo = statementNo,
                PeriodStart = periodStart,
                PeriodEnd = periodEnd,
                AmountDue = amountDue,
                AmountPaid = 0,
                DueDate = periodStart.AddDays(-1),
                LineItemsJson = lineItemsJson,
                CreatedAt = now
            }, cancellationToken);

            createdBillCount++;
            logger.LogInformation(
                "自动续费：为租户 {TenantId} 订阅 {SubscriptionId} 生成账单 {StatementNo}，金额 {AmountDue}，周期 {PeriodStart}~{PeriodEnd}",
                candidate.Subscription.TenantId,
                candidate.Subscription.Id,
                statementNo,
                amountDue,
                periodStart,
                periodEnd);
        }

        // 4. 保存账单变更
        if (createdBillCount > 0)
        {
            await billingRepository.SaveChangesAsync(cancellationToken);
        }

        return new ProcessAutoRenewalResult
        {
            CandidateCount = candidates.Count,
            CreatedBillCount = createdBillCount
        };
    }

    private static int CalculateDurationMonths(DateTime effectiveFrom, DateTime effectiveTo)
    {
        // 1. 以年月差作为周期（月），兼容“按月续费”模型
        var months = (effectiveTo.Year - effectiveFrom.Year) * 12 + effectiveTo.Month - effectiveFrom.Month;

        // 2. 对不足 1 个月的情况兜底为 1
        return months <= 0 ? 1 : months;
    }

    private static decimal CalculateRenewalAmount(TenantPackage package, int durationMonths)
    {
        // 1. 优先使用年付价（按整年计费），剩余月份按月付价补齐
        var monthlyPrice = package.MonthlyPrice ?? 0m;
        var yearlyPrice = package.YearlyPrice;
        if (yearlyPrice is null || durationMonths < 12)
        {
            return monthlyPrice * durationMonths;
        }

        // 2. 按年 + 月组合计算金额
        var years = durationMonths / 12;
        var remainingMonths = durationMonths % 12;
        return yearlyPrice.Value * years + monthlyPrice * remainingMonths;
    }
}
