using System.ComponentModel;
using ClaudeDo.Data;
using ClaudeDo.Worker.Queue;
using Microsoft.EntityFrameworkCore;
using ModelContextProtocol.Server;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Worker.External;

public sealed record QueueSlotDto(string Slot, string TaskId, int? Number, DateTime StartedAt);

public sealed record GetQueueStateResult(
    int ConfiguredSlots,
    int EffectiveSlots,
    IReadOnlyList<QueueSlotDto> ActiveSlots,
    IReadOnlyList<string> WaitingTaskIds,
    IReadOnlyList<int> WaitingTaskNumbers);

[McpServerToolType]
public sealed class QueueStateMcpTools
{
    private readonly QueueService _queue;
    private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;

    public QueueStateMcpTools(QueueService queue, IDbContextFactory<ClaudeDoDbContext> dbFactory)
    {
        _queue = queue;
        _dbFactory = dbFactory;
    }

    [McpServerTool, Description(
        "Read-only snapshot of the execution queue -- call this to observe slot occupancy instead " +
        "of inferring it from maxParallelExecutions. effectiveSlots is configuredSlots stepped down " +
        "by the usage throttle (lower when the 5h/7d usage window fills up), so comparing the two " +
        "shows whether throttling is currently active. Each active slot is \"queue\" (a normal " +
        "queue slot) or \"override\" (the single run_task_now/continue_task slot). waitingTaskIds " +
        "lists queued, unblocked, non-manual, due tasks in the order the queue would pick them next.")]
    public async Task<GetQueueStateResult> GetQueueState(CancellationToken cancellationToken = default)
    {
        var (configured, effective) = await _queue.GetSlotCountsAsync(cancellationToken);
        var active = _queue.GetActive();

        await using var ctx = await _dbFactory.CreateDbContextAsync(cancellationToken);
        var now = DateTime.UtcNow;

        var activeTaskIds = active.Select(a => a.taskId).ToList();
        var activeNumbers = await ctx.Tasks
            .Where(t => activeTaskIds.Contains(t.Id))
            .Select(t => new { t.Id, t.Number })
            .ToDictionaryAsync(t => t.Id, t => t.Number, cancellationToken);

        var activeSlots = active
            .Select(a => new QueueSlotDto(a.slot, a.taskId, activeNumbers.TryGetValue(a.taskId, out var n) ? n : null, a.startedAt))
            .ToList();

        var waiting = await ctx.Tasks
            .Where(t => t.Status == TaskStatus.Queued
                && t.BlockedByTaskId == null
                && !t.IsManual
                && (t.ScheduledFor == null || t.ScheduledFor <= now))
            .OrderBy(t => t.SortOrder).ThenBy(t => t.CreatedAt)
            .ToListAsync(cancellationToken);

        return new GetQueueStateResult(
            configured, effective, activeSlots, waiting.Select(t => t.Id).ToList(),
            waiting.Select(t => t.Number).ToList());
    }
}
