using System;
using System.Collections.Generic;
using System.Linq;

namespace AIRuntime.Editor
{
    public sealed partial class AIRuntimeKanbanSession
    {
        [Serializable]
        private sealed class AgentFallbackChain
        {
            public string PreferredAgentId;
            public List<string> FallbackAgentIds = new List<string>();
        }

        private string ResolveImplementationAgentId(KanbanTask task)
        {
            string preferred = task.AssignedAgent?.Trim() ?? string.Empty;
            // Keep diagnostics attached to the actual running worker even when
            // the preferred worker's cooldown expires during the fallback run.
            var running = _activeAgentRuns.FirstOrDefault(pair => pair.Value.TaskId == task.Id);
            if (!string.IsNullOrEmpty(running.Key)) return running.Key;
            if (GetWorkflowPhase(task) != WorkflowPhaseImplementation || IsPlanningCoordinator(task) ||
                !IsKanbanAgentActive(preferred) || !IsAgentProviderCoolingDown(preferred, out _)) return preferred;

            var preferredOption = _agentOptions?.FirstOrDefault(option => option.Id == preferred);
            // Explicitly limited to developer implementation. Neither a saved
            // chain nor a similarly named instance can cross a workflow role.
            if (!string.Equals(preferredOption?.Role, "Developer", StringComparison.OrdinalIgnoreCase)) return preferred;
            var chain = _board.AgentFallbackChains?.FirstOrDefault(item => item != null && item.PreferredAgentId == preferred);
            foreach (string id in (chain?.FallbackAgentIds ?? new List<string>())
                         .Select(value => value?.Trim()).Where(value => !string.IsNullOrEmpty(value)).Distinct(StringComparer.Ordinal))
            {
                if (id == preferred || !IsKanbanAgentActive(id) || IsAgentProviderCoolingDown(id, out _)) continue;
                var option = _agentOptions?.FirstOrDefault(candidate => candidate.Id == id);
                if (!string.Equals(option?.Role, preferredOption.Role, StringComparison.OrdinalIgnoreCase)) continue;
                if (_activeAgentRuns.ContainsKey(id) || _board.Tasks.Any(other => other != null &&
                    other.Status == TaskStatus.InProgress &&
                    (string.IsNullOrWhiteSpace(other.Dispatch?.AgentId) ? other.AssignedAgent?.Trim() : other.Dispatch.AgentId) == id)) continue;
                return id;
            }
            // No recursion: cycles and duplicate entries cannot cause retry loops.
            // Each agent retains its own durable cooldown; the normal scheduler
            // polls the earliest cooldown expiry and checks all gates again.
            return preferred;
        }
    }
}
