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

namespace TakeoutSaaS.Infrastructure.BackgroundServices;

/// <summary>
/// 订阅到期检查后台服务。
/// 每天凌晨执行，检查即将到期和已到期的订阅，自动更新状态。
/// </summary>
public sealed class SubscriptionExpiryCheckService : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<SubscriptionExpiryCheckService> _logger;
    private readonly SubscriptionExpiryCheckOptions _options;

    public SubscriptionExpiryCheckService(
        IServiceProvider serviceProvider,
        ILogger<SubscriptionExpiryCheckService> logger,
        IOptions<SubscriptionExpiryCheckOptions> 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 CheckExpiringSubscriptionsAsync(stoppingToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "订阅到期检查服务执行异常");
                // 出错后等待一段时间再重试
                await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
            }
        }

        _logger.LogInformation("订阅到期检查服务已停止");
    }

    private async Task CheckExpiringSubscriptionsAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("开始执行订阅到期检查");

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

        var now = DateTime.UtcNow;
        var gracePeriodDays = _options.GracePeriodDays;

        try
        {
            var tenants = await dbContext.Tenants
                .AsNoTracking()
                .Where(x => x.DeletedAt == null && x.Id > 0)
                .Select(x => new { x.Id, x.Code })
                .ToListAsync(cancellationToken);
            if (tenants.Count == 0)
            {
                _logger.LogInformation("订阅到期检查完成：未找到可处理租户");
                return;
            }

            var changedTotal = 0;
            var expiredTotal = 0;
            var suspendedTotal = 0;
            foreach (var tenant in tenants)
            {
                using (tenantContextAccessor.EnterTenantScope(tenant.Id, "background:subscription-expiry", tenant.Code))
                {
                    // 1. 检查活跃订阅中已到期的，转为宽限期
                    var expiredActive = await dbContext.TenantSubscriptions
                        .Where(s => s.Status == SubscriptionStatus.Active && s.EffectiveTo < now)
                        .ToListAsync(cancellationToken);

                    foreach (var subscription in expiredActive)
                    {
                        subscription.Status = SubscriptionStatus.GracePeriod;
                        _logger.LogInformation(
                            "订阅 {SubscriptionId} (租户 {TenantId}) 已到期，进入宽限期",
                            subscription.Id, subscription.TenantId);
                    }

                    // 2. 检查宽限期订阅中超过宽限期的，转为暂停
                    var gracePeriodExpired = await dbContext.TenantSubscriptions
                        .Where(s => s.Status == SubscriptionStatus.GracePeriod
                                 && s.EffectiveTo.AddDays(gracePeriodDays) < now)
                        .ToListAsync(cancellationToken);

                    foreach (var subscription in gracePeriodExpired)
                    {
                        subscription.Status = SubscriptionStatus.Suspended;
                        _logger.LogInformation(
                            "订阅 {SubscriptionId} (租户 {TenantId}) 宽限期已结束，已暂停",
                            subscription.Id, subscription.TenantId);
                    }

                    // 3. 保存更改（逐租户保存，避免跨租户写入）
                    var changedCount = await dbContext.SaveChangesAsync(cancellationToken);

                    changedTotal += changedCount;
                    expiredTotal += expiredActive.Count;
                    suspendedTotal += gracePeriodExpired.Count;
                }
            }

            _logger.LogInformation(
                "订阅到期检查完成，共更新 {Count} 条记录 (到期转宽限期: {ExpiredCount}, 宽限期转暂停: {SuspendedCount})",
                changedTotal, expiredTotal, suspendedTotal);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "订阅到期检查失败");
            throw;
        }
    }
}

/// <summary>
/// 订阅到期检查配置选项。
/// </summary>
public sealed class SubscriptionExpiryCheckOptions
{
    /// <summary>
    /// 执行时间（小时，UTC时间），默认凌晨2点。
    /// </summary>
    public int ExecuteHour { get; set; } = 2;

    /// <summary>
    /// 宽限期天数，默认7天。
    /// </summary>
    public int GracePeriodDays { get; set; } = 7;
}
