using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Enums;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace CMSMicroservice.Infrastructure.BackgroundServices;

/// <summary>
/// سرویس پس‌زمینه برای منقضی کردن سفارشات تخفیفی که بیش از ۳۰ دقیقه در وضعیت Pending مانده‌اند.
/// موجودی رزرو شده آزاد و وضعیت پرداخت به Reject تغییر می‌کند.
/// </summary>
public class ExpirePendingOrdersService : BackgroundService
{
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly ILogger<ExpirePendingOrdersService> _logger;

    /// <summary>
    /// مدت زمان انقضا (۱۵ دقیقه)
    /// </summary>
    private static readonly TimeSpan ExpirationTime = TimeSpan.FromMinutes(15);

    /// <summary>
    /// هر ۵ دقیقه چک می‌شود
    /// </summary>
    private static readonly TimeSpan CheckInterval = TimeSpan.FromMinutes(5);

    public ExpirePendingOrdersService(
        IServiceScopeFactory scopeFactory,
        ILogger<ExpirePendingOrdersService> logger)
    {
        _scopeFactory = scopeFactory;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("ExpirePendingOrdersService started — checking every {Interval} min, expiring after {Expiry} min",
            CheckInterval.TotalMinutes, ExpirationTime.TotalMinutes);

        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                await ExpireOldPendingOrders(stoppingToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error in ExpirePendingOrdersService");
            }

            await Task.Delay(CheckInterval, stoppingToken);
        }
    }

    private async Task ExpireOldPendingOrders(CancellationToken cancellationToken)
    {
        using var scope = _scopeFactory.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<IApplicationDbContext>();
        var inventoryService = scope.ServiceProvider.GetRequiredService<IInventoryService>();

        var cutoff = DateTime.Now - ExpirationTime;

        // پیدا کردن سفارشات Pending قدیمی
        var expiredOrders = await context.DiscountOrders
            .Include(o => o.OrderDetails)
            .Where(o => o.PaymentStatus == PaymentStatus.Pending && o.Created < cutoff)
            .ToListAsync(cancellationToken);

        if (!expiredOrders.Any()) return;

        _logger.LogInformation("Found {Count} expired pending orders to clean up", expiredOrders.Count);

        foreach (var order in expiredOrders)
        {
            try
            {
                // آزادسازی رزرو موجودی
                foreach (var detail in order.OrderDetails)
                {
                    var inventoryItem = await inventoryService.GetInventoryAsync(
                        detail.ProductId,
                        ProductType.DiscountProduct,
                        null,
                        cancellationToken);

                    if (inventoryItem == null)
                    {
                        _logger.LogWarning(
                            "Skipping inventory release for expired order #{OrderId}, ProductId={ProductId} because InventoryItem was not found",
                            order.Id,
                            detail.ProductId);
                        continue;
                    }

                    if (inventoryItem.ReservedQuantity <= 0)
                    {
                        _logger.LogInformation(
                            "Skipping inventory release for expired order #{OrderId}, ProductId={ProductId} because no reserved quantity remains",
                            order.Id,
                            detail.ProductId);
                        continue;
                    }

                    var releaseQuantity = Math.Min(detail.Count, inventoryItem.ReservedQuantity);

                    if (releaseQuantity < detail.Count)
                    {
                        _logger.LogWarning(
                            "Partial reservation release for expired order #{OrderId}, ProductId={ProductId}: requested={Requested}, reserved={Reserved}, releasing={Released}",
                            order.Id,
                            detail.ProductId,
                            detail.Count,
                            inventoryItem.ReservedQuantity,
                            releaseQuantity);
                    }

                    await inventoryService.ReleaseReservationAsync(
                        detail.ProductId,
                        ProductType.DiscountProduct,
                        releaseQuantity,
                        order.Id,
                        cancellationToken);
                }

                // تغییر وضعیت به Reject
                order.PaymentStatus = PaymentStatus.Reject;
                order.DeliveryStatus = DeliveryStatus.Cancelled;

                // آپدیت تراکنش مربوطه
                if (order.TransactionId.HasValue)
                {
                    var transaction = await context.Transactions
                        .FirstOrDefaultAsync(t => t.Id == order.TransactionId.Value, cancellationToken);
                    if (transaction != null)
                    {
                        transaction.PaymentStatus = PaymentStatus.Reject;
                    }
                }

                _logger.LogInformation(
                    "Expired order #{OrderId} (created {Created:u}) — inventory released, status set to Reject",
                    order.Id, order.Created);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to expire order #{OrderId}", order.Id);
            }
        }

        await context.SaveChangesAsync(cancellationToken);
    }
}
