using Microsoft.EntityFrameworkCore;


/// <summary>
/// Provides filtered, sorted work queue views for each workflow stage.
/// All queues sort by UpdatedAt ASC to enforce FIFO processing — the oldest
/// pending item is always at the top of the queue.
/// </summary>
public class WorkQueueService : IWorkQueueService
{
    private readonly AppDbContext _db;
    private readonly ILogger<WorkQueueService> _logger;

    public WorkQueueService(AppDbContext db, ILogger<WorkQueueService> logger)
    {
        _db = db;
        _logger = logger;
    }

    private static readonly HashSet<string> _allowedSortFields = new(StringComparer.OrdinalIgnoreCase)
    {
        "createdAt", "updatedAt", "status", "batchType", "track"
    };

    public async Task<WorkQueueResponse> GetVerificationQueueAsync(
        int page, int pageSize, string sortBy = "updatedAt", string sortDirection = "asc")
    {
        var query = _db.DigitizationBatches
            .Where(b => b.Status == BatchStatus.PendingVerification);

        return await BuildQueueResponseAsync("verification", query, page, pageSize, sortBy, sortDirection);
    }

    public async Task<WorkQueueResponse> GetEntryQueueAsync(
        int page, int pageSize, string sortBy = "updatedAt", string sortDirection = "asc")
    {
        var entryStatuses = new[]
        {
            BatchStatus.InEntry,
            BatchStatus.Rejected
        };

        var query = _db.DigitizationBatches
            .Where(b => entryStatuses.Contains(b.Status));

        return await BuildQueueResponseAsync("entry", query, page, pageSize, sortBy, sortDirection);
    }

    public async Task<WorkQueueResponse> GetClinicalApprovalQueueAsync(
        int page, int pageSize, string sortBy = "updatedAt", string sortDirection = "asc")
    {
        var query = _db.DigitizationBatches
            .Where(b => b.Status == BatchStatus.AwaitingClinicalApproval);

        return await BuildQueueResponseAsync("clinical-approval", query, page, pageSize, sortBy, sortDirection);
    }

    private static IOrderedQueryable<DigitizationBatch> ApplySort(
        IQueryable<DigitizationBatch> query, string sortBy, string sortDirection)
    {
        var desc = sortDirection.Equals("desc", StringComparison.OrdinalIgnoreCase);

        return sortBy.ToLowerInvariant() switch
        {
            "createdat" => desc ? query.OrderByDescending(b => b.CreatedAt) : query.OrderBy(b => b.CreatedAt),
            "updatedat" => desc ? query.OrderByDescending(b => b.UpdatedAt) : query.OrderBy(b => b.UpdatedAt),
            "status" => desc ? query.OrderByDescending(b => b.Status) : query.OrderBy(b => b.Status),
            "batchtype" => desc ? query.OrderByDescending(b => b.BatchType) : query.OrderBy(b => b.BatchType),
            "track" => desc ? query.OrderByDescending(b => b.Track) : query.OrderBy(b => b.Track),
            _ => query.OrderBy(b => b.UpdatedAt)
        };
    }

    private async Task<WorkQueueResponse> BuildQueueResponseAsync(
        string queueName,
        IQueryable<DigitizationBatch> query,
        int page,
        int pageSize,
        string sortBy,
        string sortDirection)
    {
        if (!_allowedSortFields.Contains(sortBy))
            throw new ValidationException(
                $"Invalid sortBy field '{sortBy}'. Allowed: {string.Join(", ", _allowedSortFields)}.",
                "INVALID_SORT_FIELD");

        var totalCount = await query.CountAsync();
        var totalPages = (int)Math.Ceiling((double)totalCount / pageSize);

        var sorted = ApplySort(query, sortBy, sortDirection);
        var items = await sorted
            .Include(b => b.EnteredByUser)
            .Include(b => b.Events)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .Select(b => new WorkQueueItemResponse(
                b.Id,
                b.Status.ToDbString(),
                b.BatchType.ToDbString(),
                b.Track.ToDbString(),
                b.PatientId,
                b.EnteredByUserId,
                b.EnteredByUser != null ? b.EnteredByUser.FullName : null,
                b.RejectionReason,
                b.CreatedAt,
                b.UpdatedAt,
                b.Events.Count
            ))
            .ToListAsync();

        return new WorkQueueResponse(queueName, items, page, pageSize, totalCount, totalPages);
    }

    public async Task<WorkQueueOverviewResponse> GetOverviewAsync()
    {
        var now = DateTimeOffset.UtcNow;

        var statusGroups = await _db.DigitizationBatches
            .AsNoTracking()
            .GroupBy(b => b.Status)
            .Select(g => new { Status = g.Key, Count = g.Count() })
            .ToListAsync();

        var statusCounts = new Dictionary<string, int>();
        foreach (var status in Enum.GetValues<BatchStatus>())
        {
            var count = statusGroups.FirstOrDefault(g => g.Status == status)?.Count ?? 0;
            statusCounts[status.ToDbString()] = count;
        }

        var pendingBatches = await _db.DigitizationBatches
            .AsNoTracking()
            .Where(b => b.Status == BatchStatus.PendingVerification)
            .Select(b => b.UpdatedAt)
            .ToListAsync();

        double avgTimeInQueueMinutes = 0;
        double oldestPendingMinutes = 0;

        if (pendingBatches.Count > 0)
        {
            var ages = pendingBatches
                .Select(updatedAt => (now - updatedAt).TotalMinutes)
                .ToList();

            avgTimeInQueueMinutes = ages.Average();
            oldestPendingMinutes = ages.Max();
        }

        var cutoff = now.AddHours(-24);

        var recentEvents = await _db.DigitizationEvents
            .AsNoTracking()
            .Where(e => e.OccurredAt >= cutoff)
            .Where(e => e.EventType == DigitizationEventType.Rejected
                     || e.EventType == DigitizationEventType.VerificationFailed
                     || e.EventType == DigitizationEventType.Verified
                     || e.EventType == DigitizationEventType.VerifiedPendingClinical)
            .GroupBy(e => e.EventType)
            .Select(g => new { EventType = g.Key, Count = g.Count() })
            .ToListAsync();

        var rejections = recentEvents
            .Where(e => e.EventType == DigitizationEventType.Rejected
                     || e.EventType == DigitizationEventType.VerificationFailed)
            .Sum(e => e.Count);

        var verifications = recentEvents
            .Where(e => e.EventType == DigitizationEventType.Verified
                     || e.EventType == DigitizationEventType.VerifiedPendingClinical)
            .Sum(e => e.Count);

        var totalDecisions = rejections + verifications;
        var rejectRate = totalDecisions > 0
            ? (double)rejections / totalDecisions
            : 0.0;

        _logger.LogDebug(
            "Work queue overview: {PendingCount} pending, avg queue {AvgMinutes:F1}m, reject rate {RejectRate:P1}",
            pendingBatches.Count, avgTimeInQueueMinutes, rejectRate);

        return new WorkQueueOverviewResponse
        {
            StatusCounts = statusCounts,
            AverageTimeInQueueMinutes = Math.Round(avgTimeInQueueMinutes, 1),
            RejectRate = Math.Round(rejectRate, 4),
            OldestPendingVerificationMinutes = Math.Round(oldestPendingMinutes, 1)
        };
    }
}