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.Enums;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Shared.Abstractions.Ids;

namespace TakeoutSaaS.Application.App.Subscriptions.Handlers;

/// <summary>
/// 续费提醒处理器：在到期前 7/3/1 天等时间点发送站内提醒（幂等）。
/// </summary>
public sealed class ProcessRenewalRemindersCommandHandler(
    ISubscriptionRepository subscriptionRepository,
    ITenantNotificationRepository notificationRepository,
    IIdGenerator idGenerator,
    ILogger<ProcessRenewalRemindersCommandHandler> logger)
    : IRequestHandler<ProcessRenewalRemindersCommand, ProcessRenewalRemindersResult>
{
    private const string ReminderTitle = "订阅续费提醒";

    /// <inheritdoc />
    public async Task<ProcessRenewalRemindersResult> Handle(ProcessRenewalRemindersCommand request, CancellationToken cancellationToken)
    {
        // 1. 读取提醒配置
        var now = DateTime.UtcNow;
        var candidateCount = 0;
        var createdReminderCount = 0;
        var dedupeWindowStart = now.AddHours(-24);

        // 2. 按提醒时间点扫描到期订阅
        foreach (var daysBeforeExpiry in request.ReminderDaysBeforeExpiry.Distinct().OrderByDescending(x => x))
        {
            // 2.1 计算目标日期区间（按天匹配）
            var targetDate = now.AddDays(daysBeforeExpiry);
            var startOfDay = targetDate.Date;
            var endOfDay = startOfDay.AddDays(1);

            // 2.2 查询候选订阅（活跃 + 未开自动续费 + 到期在当日）
            var candidates = await subscriptionRepository.FindRenewalReminderCandidatesAsync(
                startOfDay,
                endOfDay,
                cancellationToken);
            candidateCount += candidates.Count;

            foreach (var item in candidates)
            {
                // 2.3 幂等：同一订阅 + 同一天数提醒，在 24 小时内只发送一次
                var metadataJson = BuildReminderMetadata(item.Subscription.Id, daysBeforeExpiry, item.Subscription.EffectiveTo);
                var alreadySent = await notificationRepository.ExistsByMetadataAsync(
                    item.Subscription.TenantId,
                    ReminderTitle,
                    metadataJson,
                    dedupeWindowStart,
                    cancellationToken);

                if (alreadySent)
                {
                    continue;
                }

                // 2.4 构造提醒内容并入库
                var notification = new TenantNotification
                {
                    Id = idGenerator.NextId(),
                    TenantId = item.Subscription.TenantId,
                    Title = ReminderTitle,
                    Message = $"您的订阅套餐「{item.Package.Name}」将在 {daysBeforeExpiry} 天内到期（到期时间：{item.Subscription.EffectiveTo:yyyy-MM-dd HH:mm}），请及时续费以免影响使用。",
                    Severity = daysBeforeExpiry <= 1
                        ? TenantNotificationSeverity.Critical
                        : TenantNotificationSeverity.Warning,
                    Channel = TenantNotificationChannel.InApp,
                    SentAt = now,
                    ReadAt = null,
                    MetadataJson = metadataJson,
                    CreatedAt = now
                };

                await notificationRepository.AddAsync(notification, cancellationToken);
                createdReminderCount++;

                logger.LogInformation(
                    "续费提醒：TenantId={TenantId}, TenantName={TenantName}, SubscriptionId={SubscriptionId}, DaysBeforeExpiry={DaysBeforeExpiry}",
                    item.Tenant.Id,
                    item.Tenant.Name,
                    item.Subscription.Id,
                    daysBeforeExpiry);
            }
        }

        // 3. 保存变更
        if (createdReminderCount > 0)
        {
            await notificationRepository.SaveChangesAsync(cancellationToken);
        }

        return new ProcessRenewalRemindersResult
        {
            CandidateCount = candidateCount,
            CreatedReminderCount = createdReminderCount
        };
    }

    private static string BuildReminderMetadata(long subscriptionId, int daysBeforeExpiry, DateTime effectiveTo)
    {
        // 1. 使用稳定字段顺序的 JSON 作为幂等键
        return JsonSerializer.Serialize(new
        {
            Type = "RenewalReminder",
            SubscriptionId = subscriptionId,
            DaysBeforeExpiry = daysBeforeExpiry,
            EffectiveTo = effectiveTo.ToString("O")
        });
    }
}
