using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using AxCopilot.Models;

namespace AxCopilot.Services.Agent;

public class SubAgentTool : IAgentTool
{
	private static readonly Dictionary<string, SubAgentTask> _activeTasks = new Dictionary<string, SubAgentTask>();

	private static readonly object _lock = new object();

	public string Name => "spawn_agent";

	public string Description => "Create a read-only sub-agent for bounded parallel research or codebase analysis.\nUse this when a side task can run independently while the main agent continues.\nThe sub-agent can inspect files, search code, review diffs, and summarize findings.\nCollect results later with wait_agents.";

	public ToolParameterSchema Parameters => new ToolParameterSchema
	{
		Properties = new Dictionary<string, ToolProperty>
		{
			["task"] = new ToolProperty
			{
				Type = "string",
				Description = "The self-contained task for the sub-agent."
			},
			["id"] = new ToolProperty
			{
				Type = "string",
				Description = "A unique sub-agent identifier used by wait_agents."
			}
		},
		Required = new List<string> { "task", "id" }
	};

	public static IReadOnlyDictionary<string, SubAgentTask> ActiveTasks
	{
		get
		{
			lock (_lock)
			{
				return new Dictionary<string, SubAgentTask>(_activeTasks);
			}
		}
	}

	public static event Action<SubAgentStatusEvent>? StatusChanged;

	public Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default(CancellationToken))
	{
		JsonElement value;
		string task = (args.TryGetProperty("task", out value) ? (value.GetString() ?? "") : "");
		JsonElement value2;
		string id = (args.TryGetProperty("id", out value2) ? (value2.GetString() ?? "") : "");
		if (string.IsNullOrWhiteSpace(task) || string.IsNullOrWhiteSpace(id))
		{
			return Task.FromResult(ToolResult.Fail("task and id are required."));
		}
		CleanupStale();
		int num = ((!(Application.Current is App app)) ? ((int?)null) : app.SettingsService?.Settings.Llm.MaxSubAgents) ?? 3;
		lock (_lock)
		{
			if (_activeTasks.ContainsKey(id))
			{
				return Task.FromResult(ToolResult.Fail("Sub-agent id already exists: " + id));
			}
			int num2 = _activeTasks.Values.Count((SubAgentTask x) => !x.CompletedAt.HasValue);
			if (num2 >= num)
			{
				return Task.FromResult(ToolResult.Fail($"Maximum concurrent sub-agents reached ({num})."));
			}
		}
		SubAgentTask subTask = new SubAgentTask
		{
			Id = id,
			Task = task,
			StartedAt = DateTime.Now
		};
		subTask.RunTask = Task.Run(async delegate
		{
			try
			{
				string result = await RunSubAgentAsync(id, task, context).ConfigureAwait(continueOnCapturedContext: false);
				subTask.Result = result;
				subTask.Success = true;
				NotifyStatus(new SubAgentStatusEvent
				{
					Id = id,
					Task = task,
					Status = SubAgentRunStatus.Completed,
					Summary = "Sub-agent '" + id + "' completed.",
					Result = result,
					Timestamp = DateTime.Now
				});
			}
			catch (Exception ex)
			{
				Exception ex2 = ex;
				subTask.Result = "Error: " + ex2.Message;
				subTask.Success = false;
				NotifyStatus(new SubAgentStatusEvent
				{
					Id = id,
					Task = task,
					Status = SubAgentRunStatus.Failed,
					Summary = "Sub-agent '" + id + "' failed: " + ex2.Message,
					Result = subTask.Result,
					Timestamp = DateTime.Now
				});
			}
			finally
			{
				subTask.CompletedAt = DateTime.Now;
			}
		}, CancellationToken.None);
		lock (_lock)
		{
			_activeTasks[id] = subTask;
		}
		NotifyStatus(new SubAgentStatusEvent
		{
			Id = id,
			Task = task,
			Status = SubAgentRunStatus.Started,
			Summary = "Sub-agent '" + id + "' started.",
			Timestamp = DateTime.Now
		});
		return Task.FromResult(ToolResult.Ok($"Sub-agent '{id}' started.\nTask: {task}\nUse wait_agents later to collect the result."));
	}

	private static async Task<string> RunSubAgentAsync(string id, string task, AgentContext parentContext)
	{
		SettingsService settings = CreateSubAgentSettings(parentContext);
		using LlmService llm = new LlmService(settings);
		using ToolRegistry tools = await CreateSubAgentRegistryAsync(settings).ConfigureAwait(continueOnCapturedContext: false);
		AgentLoopService loop = new AgentLoopService(llm, tools, settings)
		{
			ActiveTab = parentContext.ActiveTab
		};
		List<ChatMessage> messages = new List<ChatMessage>
		{
			new ChatMessage
			{
				Role = "system",
				Content = BuildSubAgentSystemPrompt(parentContext)
			},
			new ChatMessage
			{
				Role = "user",
				Content = task
			}
		};
		string finalText = await loop.RunAsync(messages, CancellationToken.None).ConfigureAwait(continueOnCapturedContext: false);
		string eventSummary = SummarizeEvents(loop.Events);
		StringBuilder sb = new StringBuilder();
		StringBuilder stringBuilder = sb;
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(12, 1, stringBuilder);
		handler.AppendLiteral("[Sub-agent ");
		handler.AppendFormatted(id);
		handler.AppendLiteral("]");
		stringBuilder2.AppendLine(ref handler);
		stringBuilder = sb;
		StringBuilder stringBuilder3 = stringBuilder;
		handler = new StringBuilder.AppendInterpolatedStringHandler(6, 1, stringBuilder);
		handler.AppendLiteral("Task: ");
		handler.AppendFormatted(task);
		stringBuilder3.AppendLine(ref handler);
		if (!string.IsNullOrWhiteSpace(parentContext.WorkFolder))
		{
			stringBuilder = sb;
			StringBuilder stringBuilder4 = stringBuilder;
			handler = new StringBuilder.AppendInterpolatedStringHandler(13, 1, stringBuilder);
			handler.AppendLiteral("Work folder: ");
			handler.AppendFormatted(parentContext.WorkFolder);
			stringBuilder4.AppendLine(ref handler);
		}
		if (!string.IsNullOrWhiteSpace(eventSummary))
		{
			sb.AppendLine();
			sb.AppendLine("Observed work:");
			sb.AppendLine(eventSummary);
		}
		sb.AppendLine();
		sb.AppendLine("Result:");
		sb.AppendLine(string.IsNullOrWhiteSpace(finalText) ? "(empty)" : finalText.Trim());
		return sb.ToString().TrimEnd();
	}

	private static SettingsService CreateSubAgentSettings(AgentContext parentContext)
	{
		SettingsService settingsService = new SettingsService();
		settingsService.Load();
		LlmSettings llm = settingsService.Settings.Llm;
		llm.WorkFolder = parentContext.WorkFolder;
		llm.FilePermission = "Deny";
		llm.PlanMode = "off";
		llm.AgentHooks = new List<AgentHookEntry>();
		llm.ToolPermissions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
		llm.DisabledTools = new List<string>
		{
			"spawn_agent", "wait_agents", "file_write", "file_edit", "process", "build_run", "snippet_runner", "memory", "notify", "open_external",
			"user_ask", "checkpoint", "diff_preview", "playbook", "http_tool", "clipboard", "sql_tool"
		};
		return settingsService;
	}

	private static async Task<ToolRegistry> CreateSubAgentRegistryAsync(SettingsService settings)
	{
		ToolRegistry registry = new ToolRegistry();
		registry.Register(new FileReadTool());
		registry.Register(new GlobTool());
		registry.Register(new GrepTool());
		registry.Register(new FolderMapTool());
		registry.Register(new DocumentReaderTool());
		registry.Register(new DevEnvDetectTool());
		registry.Register(new GitTool());
		registry.Register(new LspTool());
		registry.Register(new CodeSearchTool());
		registry.Register(new CodeReviewTool());
		registry.Register(new ProjectRuleTool());
		registry.Register(new SkillManagerTool());
		registry.Register(new JsonTool());
		registry.Register(new RegexTool());
		registry.Register(new DiffTool());
		registry.Register(new Base64Tool());
		registry.Register(new HashTool());
		registry.Register(new DateTimeTool());
		registry.Register(new MathTool());
		registry.Register(new XmlTool());
		registry.Register(new MultiReadTool());
		registry.Register(new FileInfoTool());
		registry.Register(new DocumentReviewTool());
		await registry.RegisterMcpToolsAsync(settings.Settings.Llm.McpServers).ConfigureAwait(continueOnCapturedContext: false);
		return registry;
	}

	private static string BuildSubAgentSystemPrompt(AgentContext parentContext)
	{
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.AppendLine("You are a focused sub-agent for AX Copilot.");
		stringBuilder.AppendLine("You are running a bounded, read-only investigation.");
		stringBuilder.AppendLine("Use tools to inspect the project and gather evidence, then return a concise result.");
		stringBuilder.AppendLine("Do not ask the user questions.");
		stringBuilder.AppendLine("Do not attempt file edits, command execution, notifications, or external side effects.");
		stringBuilder.AppendLine("Prefer direct evidence from files and tool results over speculation.");
		stringBuilder.AppendLine("If something is uncertain, say so briefly.");
		if (!string.IsNullOrWhiteSpace(parentContext.WorkFolder))
		{
			StringBuilder stringBuilder2 = stringBuilder;
			StringBuilder stringBuilder3 = stringBuilder2;
			StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(21, 1, stringBuilder2);
			handler.AppendLiteral("Current work folder: ");
			handler.AppendFormatted(parentContext.WorkFolder);
			stringBuilder3.AppendLine(ref handler);
		}
		if (!string.IsNullOrWhiteSpace(parentContext.ActiveTab))
		{
			StringBuilder stringBuilder2 = stringBuilder;
			StringBuilder stringBuilder4 = stringBuilder2;
			StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(13, 1, stringBuilder2);
			handler.AppendLiteral("Current tab: ");
			handler.AppendFormatted(parentContext.ActiveTab);
			stringBuilder4.AppendLine(ref handler);
		}
		stringBuilder.AppendLine("Final answer format:");
		stringBuilder.AppendLine("1. Short conclusion");
		stringBuilder.AppendLine("2. Key evidence");
		stringBuilder.AppendLine("3. Risks or unknowns");
		return stringBuilder.ToString().TrimEnd();
	}

	private static string SummarizeEvents(IEnumerable<AgentEvent> events)
	{
		List<string> list = events.Where(delegate(AgentEvent e)
		{
			AgentEventType type = e.Type;
			return (type == AgentEventType.StepStart || (uint)(type - 4) <= 1u) ? true : false;
		}).TakeLast(12).Select(delegate(AgentEvent e)
		{
			AgentEventType type = e.Type;
			if (1 == 0)
			{
			}
			string text = type switch
			{
				AgentEventType.ToolCall => "tool:" + e.ToolName, 
				AgentEventType.ToolResult => "result:" + e.ToolName, 
				AgentEventType.StepStart => "step", 
				_ => e.Type.ToString().ToLowerInvariant(), 
			};
			if (1 == 0)
			{
			}
			string text2 = text;
			string text3 = (string.IsNullOrWhiteSpace(e.Summary) ? "" : (" - " + e.Summary.Trim()));
			return "- " + text2 + text3;
		})
			.ToList();
		return (list.Count == 0) ? "" : string.Join(Environment.NewLine, list);
	}

	public static async Task<string> WaitAsync(IEnumerable<string>? ids = null, bool completedOnly = false, CancellationToken ct = default(CancellationToken))
	{
		List<SubAgentTask> tasks;
		lock (_lock)
		{
			tasks = _activeTasks.Values.ToList();
		}
		List<string> requestedIds = (from x in ids?.Where((string x) => !string.IsNullOrWhiteSpace(x))
			select x.Trim()).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
		if (requestedIds != null && requestedIds.Count > 0)
		{
			tasks = tasks.Where((SubAgentTask t) => requestedIds.Contains<string>(t.Id, StringComparer.OrdinalIgnoreCase)).ToList();
		}
		if (tasks.Count == 0)
		{
			if (requestedIds != null && requestedIds.Count > 0)
			{
				return "No matching sub-agents found for: " + string.Join(", ", requestedIds);
			}
			return "No active sub-agents.";
		}
		if (!completedOnly)
		{
			await Task.WhenAll(from t in tasks
				where t.RunTask != null
				select t.RunTask).WaitAsync(ct);
		}
		else
		{
			tasks = tasks.Where((SubAgentTask t) => t.CompletedAt.HasValue).ToList();
		}
		if (tasks.Count == 0)
		{
			return "No requested sub-agents have completed yet.";
		}
		StringBuilder sb = new StringBuilder();
		StringBuilder stringBuilder = sb;
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(31, 1, stringBuilder);
		handler.AppendLiteral("Collected ");
		handler.AppendFormatted(tasks.Count);
		handler.AppendLiteral(" sub-agent result(s):");
		stringBuilder2.AppendLine(ref handler);
		foreach (SubAgentTask task in tasks.OrderBy((SubAgentTask t) => t.StartedAt))
		{
			string status = (task.Success ? "OK" : "FAIL");
			string duration = (task.CompletedAt.HasValue ? $"{(task.CompletedAt.Value - task.StartedAt).TotalSeconds:F1}s" : "running");
			sb.AppendLine();
			stringBuilder = sb;
			StringBuilder stringBuilder3 = stringBuilder;
			handler = new StringBuilder.AppendInterpolatedStringHandler(14, 3, stringBuilder);
			handler.AppendLiteral("--- [");
			handler.AppendFormatted(status);
			handler.AppendLiteral("] ");
			handler.AppendFormatted(task.Id);
			handler.AppendLiteral(" (");
			handler.AppendFormatted(duration);
			handler.AppendLiteral(") ---");
			stringBuilder3.AppendLine(ref handler);
			sb.AppendLine(task.Result ?? "(no result)");
		}
		lock (_lock)
		{
			foreach (SubAgentTask task2 in tasks)
			{
				_activeTasks.Remove(task2.Id);
			}
		}
		return sb.ToString().TrimEnd();
	}

	public static void CleanupStale()
	{
		lock (_lock)
		{
			List<string> list = (from kv in _activeTasks
				where kv.Value.CompletedAt.HasValue && (DateTime.Now - kv.Value.CompletedAt.Value).TotalMinutes > 10.0
				select kv.Key).ToList();
			foreach (string item in list)
			{
				_activeTasks.Remove(item);
			}
		}
	}

	private static void NotifyStatus(SubAgentStatusEvent evt)
	{
		try
		{
			SubAgentTool.StatusChanged?.Invoke(evt);
		}
		catch
		{
		}
	}
}
