using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Infrastructure.App.Persistence;
using TakeoutSaaS.Shared.Abstractions.Ids;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Infrastructure.BackgroundServices;

/// <summary>
/// 续费提醒后台服务。
/// 定期检查即将到期的订阅，发送续费提醒通知。
/// </summary>
public sealed class RenewalReminderService : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<RenewalReminderService> _logger;
    private readonly RenewalReminderOptions _options;

    public RenewalReminderService(
        IServiceProvider serviceProvider,
        ILogger<RenewalReminderService> logger,
        IOptions<RenewalReminderOptions> options)
    {
        _serviceProvider = serviceProvider;
        _logger = logger;
        _options = options.Value;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("续费提醒服务已启动");

        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                // 计算下次执行时间（每天执行）
                var now = DateTime.UtcNow;
                var nextRun = now.Date.AddDays(1).AddHours(_options.ExecuteHour);
                var delay = nextRun - now;

                _logger.LogInformation("续费提醒服务将在 {NextRun} 执行，等待 {Delay}", nextRun, delay);

                await Task.Delay(delay, stoppingToken);

                if (stoppingToken.IsCancellationRequested)
                    break;

                await SendRenewalRemindersAsync(stoppingToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "续费提醒服务执行异常");
                // 出错后等待一段时间再重试
                await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
            }
        }

        _logger.LogInformation("续费提醒服务已停止");
    }

    private async Task SendRenewalRemindersAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("开始发送续费提醒");

        using var scope = _serviceProvider.CreateScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<TakeoutAppDbContext>();
        var idGenerator = scope.ServiceProvider.GetRequiredService<IIdGenerator>();
        var tenantContextAccessor = scope.ServiceProvider.GetRequiredService<ITenantContextAccessor>();

        var now = DateTime.UtcNow;

        try
        {
            var tenants = await dbContext.Tenants
                .AsNoTracking()
                .Where(x => x.DeletedAt == null && x.Id > 0)
                .Select(x => new { x.Id, x.Code, x.Name })
                .ToListAsync(cancellationToken);
            if (tenants.Count == 0)
            {
                _logger.LogInformation("续费提醒发送完成：未找到可处理租户");
                return;
            }

            var remindersSentTotal = 0;
            foreach (var tenant in tenants)
            {
                using (tenantContextAccessor.EnterTenantScope(tenant.Id, "background:renewal-reminder", tenant.Code))
                {
                    var remindersSent = 0;

                    // 遍历配置的提醒时间点（例如：到期前7天、3天、1天）
                    foreach (var daysBeforeExpiry in _options.ReminderDaysBeforeExpiry)
                    {
                        var targetDate = now.AddDays(daysBeforeExpiry);
                        var startOfDay = targetDate.Date;
                        var endOfDay = startOfDay.AddDays(1);

                        // 查询即将到期的活跃订阅（且未开启自动续费）
                        var expiringSubscriptions = await dbContext.TenantSubscriptions
                            .Where(s => s.Status == SubscriptionStatus.Active
                                     && !s.AutoRenew
                                     && s.EffectiveTo >= startOfDay
                                     && s.EffectiveTo < endOfDay)
                            .Join(
                                dbContext.TenantPackages,
                                sub => sub.TenantPackageId,
                                package => package.Id,
                                (sub, package) => new { Subscription = sub, Package = package }
                            )
                            .ToListAsync(cancellationToken);

                        foreach (var item in expiringSubscriptions)
                        {
                            // 检查是否已发送过相同天数的提醒（避免重复发送）
                            var alreadySent = await dbContext.TenantNotifications
                                .AnyAsync(n => n.TenantId == item.Subscription.TenantId
                                            && n.Message.Contains($"{daysBeforeExpiry}天内到期")
                                            && n.SentAt >= now.AddHours(-24), // 24小时内已发送过
                                    cancellationToken);

                            if (alreadySent)
                            {
                                continue;
                            }

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

                            dbContext.TenantNotifications.Add(notification);
                            remindersSent++;

                            _logger.LogInformation(
                                "发送续费提醒: 租户 {TenantName} ({TenantId}), 套餐 {PackageName}, 剩余 {Days} 天",
                                tenant.Name, item.Subscription.TenantId, item.Package.Name, daysBeforeExpiry);
                        }
                    }

                    if (remindersSent > 0)
                    {
                        await dbContext.SaveChangesAsync(cancellationToken);
                    }

                    remindersSentTotal += remindersSent;
                }
            }

            _logger.LogInformation("续费提醒发送完成，共发送 {Count} 条提醒", remindersSentTotal);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "发送续费提醒失败");
            throw;
        }
    }
}

/// <summary>
/// 续费提醒配置选项。
/// </summary>
public sealed class RenewalReminderOptions
{
    /// <summary>
    /// 执行时间（小时，UTC时间），默认上午10点。
    /// </summary>
    public int ExecuteHour { get; set; } = 10;

    /// <summary>
    /// 提醒时间点（到期前N天），默认7天、3天、1天。
    /// </summary>
    public int[] ReminderDaysBeforeExpiry { get; set; } = { 7, 3, 1 };
}
