using ModelContextProtocol;

namespace ClaudeDo.Worker.Lifecycle;

/// <summary>
/// Reports MCP/hub progress for a long-running operation so a waiting caller doesn't hit an
/// idle-silence abort (Claude Code's MCP client aborts an MCP call after ~300s of silence).
/// </summary>
internal static class ProgressReporter
{
    /// <summary>
    /// Awaits <paramref name="work"/> while reporting progress every <paramref name="interval"/>.
    /// <paramref name="onTick"/> rides the same cadence for non-MCP callers (e.g. the Hub, which
    /// turns it into an OperationProgress broadcast). No-op passthrough when both are null.
    /// </summary>
    public static async Task<T> RunAsync<T>(
        Task<T> work, TimeSpan interval, IProgress<ProgressNotificationValue>? progress, string message,
        Action<TimeSpan>? onTick = null)
    {
        if (progress is null && onTick is null) return await work;

        var sw = System.Diagnostics.Stopwatch.StartNew();
        while (true)
        {
            var finished = await Task.WhenAny(work, Task.Delay(interval));
            if (finished == work) return await work;
            progress?.Report(new ProgressNotificationValue { Progress = 0, Message = $"{message} ({sw.Elapsed:mm\\:ss})" });
            onTick?.Invoke(sw.Elapsed);
        }
    }

    /// <summary>
    /// Reports progress for one processed item out of a known total, e.g. "cleaning worktrees
    /// (3/12)". For callers iterating a batch (cleanup_task_worktree, get_task_diff,
    /// preview_merge_set) instead of awaiting one long-running task.
    /// </summary>
    public static void ReportItem(
        IProgress<ProgressNotificationValue>? progress, string action, int current, int total)
    {
        progress?.Report(new ProgressNotificationValue
        {
            Progress = current,
            Total = total,
            Message = $"{action} ({current}/{total})",
        });
    }
}
