using System.Collections.Concurrent;
using ClaudeDo.Worker.Runner;

namespace ClaudeDo.Worker.External;

// Bounds the list-handler wait<->merge handoff chain server-side, so a model that ignores the
// cooperative "this is the FINAL round" line in handoff.md (InteractiveLaunchSpecService's
// finalNote) cannot loop wait->merge->wait->merge forever, each round spawning a fresh ConPTY
// session. Keyed by handler task id -- a "Let Claude handle it" run always creates a fresh
// handler task (InteractiveLaunchSpecService.CreateMergeHelperTaskAsync), so the counter is
// per-run by construction: no reset logic needed, and a later run on the same list starts at 0
// on its own. In-memory only, same tradeoff as TaskRunTokenRegistry -- a worker restart mid-run
// resets the count, which is acceptable since the chain restarts fresh anyway.
public sealed class HandoffRoundTracker
{
    // Two full wait->merge cycles: wait, merge, wait, merge.
    public const int MaxHandoffs = 4;

    private sealed class TaskState
    {
        public int Count;
        public bool Sealed;
    }

    private readonly ConcurrentDictionary<string, TaskState> _states = new();

    public sealed record Result(bool Rejected, bool Coerced, string EffectivePhase, string? Error);

    public Result Register(string taskId, string nextPhase)
    {
        var state = _states.GetOrAdd(taskId, _ => new TaskState());
        lock (state)
        {
            if (state.Sealed)
                return new Result(true, false, nextPhase,
                    $"Task {taskId} already completed its merge_final round -- no further handoff is allowed.");

            state.Count++;
            var isFinal = nextPhase is MergeHelperPhase.WaitFinal or MergeHelperPhase.MergeFinal;

            var effective = nextPhase;
            if (state.Count > MaxHandoffs && !isFinal)
            {
                effective = nextPhase == MergeHelperPhase.Wait ? MergeHelperPhase.WaitFinal : MergeHelperPhase.MergeFinal;
                isFinal = true;
            }

            if (isFinal && effective == MergeHelperPhase.MergeFinal)
                state.Sealed = true;

            return new Result(false, !string.Equals(effective, nextPhase, StringComparison.Ordinal), effective, null);
        }
    }
}
