using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace AxCopilot.Services.Agent;

public class SnippetRunnerTool : IAgentTool
{
	private static readonly string[] DangerousPatterns = new string[18]
	{
		"Process.Start", "ProcessStartInfo", "Registry.", "RegistryKey", "Environment.Exit", "File.Delete", "Directory.Delete", "Format-Volume", "Remove-Item", "os.remove",
		"os.rmdir", "shutil.rmtree", "subprocess.call", "subprocess.Popen", "child_process", "require('fs').unlink", "exec(", "eval("
	};

	public string Name => "snippet_runner";

	public string Description => "Execute a code snippet in C#, Python, or JavaScript. Writes the code to a temp file, runs it, and returns stdout/stderr. Useful for quick calculations, data transformations, format conversions, and testing algorithms. Max execution time: 30 seconds. Max output: 8000 chars.";

	public ToolParameterSchema Parameters
	{
		get
		{
			ToolParameterSchema toolParameterSchema = new ToolParameterSchema();
			Dictionary<string, ToolProperty> dictionary = new Dictionary<string, ToolProperty>();
			ToolProperty obj = new ToolProperty
			{
				Type = "string",
				Description = "Programming language: 'csharp', 'python', or 'javascript'"
			};
			int num = 3;
			List<string> list = new List<string>(num);
			CollectionsMarshal.SetCount(list, num);
			Span<string> span = CollectionsMarshal.AsSpan(list);
			span[0] = "csharp";
			span[1] = "python";
			span[2] = "javascript";
			obj.Enum = list;
			dictionary["language"] = obj;
			dictionary["code"] = new ToolProperty
			{
				Type = "string",
				Description = "Source code to execute. For C#: top-level statements or full Program class. For Python: standard script. For JavaScript: Node.js script."
			};
			dictionary["timeout"] = new ToolProperty
			{
				Type = "integer",
				Description = "Timeout in seconds. Default: 30, max: 60."
			};
			toolParameterSchema.Properties = dictionary;
			num = 2;
			List<string> list2 = new List<string>(num);
			CollectionsMarshal.SetCount(list2, num);
			Span<string> span2 = CollectionsMarshal.AsSpan(list2);
			span2[0] = "language";
			span2[1] = "code";
			toolParameterSchema.Required = list2;
			return toolParameterSchema;
		}
	}

	public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default(CancellationToken))
	{
		string language = args.GetProperty("language").GetString() ?? "";
		JsonElement c;
		string code = (args.TryGetProperty("code", out c) ? (c.GetString() ?? "") : "");
		JsonElement to;
		int timeout = (args.TryGetProperty("timeout", out to) ? Math.Min(to.GetInt32(), 60) : 30);
		if (string.IsNullOrWhiteSpace(code))
		{
			return ToolResult.Fail("code가 비어 있습니다.");
		}
		string[] dangerousPatterns = DangerousPatterns;
		foreach (string pattern in dangerousPatterns)
		{
			if (code.Contains(pattern, StringComparison.OrdinalIgnoreCase))
			{
				return ToolResult.Fail("보안 차단: '" + pattern + "' 패턴은 snippet_runner에서 허용되지 않습니다. process 도구를 사용하세요.");
			}
		}
		if (!(await context.CheckWritePermissionAsync(Name, "[" + language + "] " + ((code.Length > 100) ? (code.Substring(0, 100) + "...") : code))))
		{
			return ToolResult.Fail("코드 실행 권한 거부");
		}
		if (!((Application.Current as App)?.SettingsService?.Settings?.Llm?.Code?.EnableSnippetRunner ?? true))
		{
			return ToolResult.Fail("snippet_runner가 설정에서 비활성화되어 있습니다. 설정 → AX Agent → 기능에서 활성화하세요.");
		}
		if (1 == 0)
		{
		}
		ToolResult result = language switch
		{
			"csharp" => await RunCSharpAsync(code, timeout, context, ct), 
			"python" => await RunScriptAsync("python", ".py", code, timeout, context, ct), 
			"javascript" => await RunScriptAsync("node", ".js", code, timeout, context, ct), 
			_ => ToolResult.Fail("지원하지 않는 언어: " + language + ". csharp, python, javascript 중 선택하세요."), 
		};
		if (1 == 0)
		{
		}
		return result;
	}

	private async Task<ToolResult> RunCSharpAsync(string code, int timeout, AgentContext context, CancellationToken ct)
	{
		string tempDir = Path.Combine(Path.GetTempPath(), $"axcopilot_snippet_{Guid.NewGuid():N}");
		Directory.CreateDirectory(tempDir);
		try
		{
			if (await IsToolAvailableAsync("dotnet-script"))
			{
				string scriptFile = Path.Combine(tempDir, "snippet.csx");
				await File.WriteAllTextAsync(scriptFile, code, ct);
				return await RunProcessAsync("dotnet-script", scriptFile, timeout, tempDir, ct);
			}
			string csprojContent = "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net8.0</TargetFramework>\n    <ImplicitUsings>enable</ImplicitUsings>\n  </PropertyGroup>\n</Project>";
			await File.WriteAllTextAsync(Path.Combine(tempDir, "Snippet.csproj"), csprojContent, ct);
			await File.WriteAllTextAsync(contents: (code.Contains("class ") && code.Contains("static void Main")) ? code : code, path: Path.Combine(tempDir, "Program.cs"), cancellationToken: ct);
			return await RunProcessAsync("dotnet", "run --project \"" + tempDir + "\"", timeout, tempDir, ct);
		}
		finally
		{
			try
			{
				Directory.Delete(tempDir, recursive: true);
			}
			catch
			{
			}
		}
	}

	private async Task<ToolResult> RunScriptAsync(string runtime, string extension, string code, int timeout, AgentContext context, CancellationToken ct)
	{
		if (!(await IsToolAvailableAsync(runtime)))
		{
			return ToolResult.Fail(runtime + "이 설치되지 않았습니다. 시스템에 " + runtime + "을 설치하세요.");
		}
		string tempFile = Path.Combine(Path.GetTempPath(), $"axcopilot_snippet_{Guid.NewGuid():N}{extension}");
		try
		{
			await File.WriteAllTextAsync(tempFile, code, Encoding.UTF8, ct);
			return await RunProcessAsync(runtime, "\"" + tempFile + "\"", timeout, context.WorkFolder, ct);
		}
		finally
		{
			try
			{
				File.Delete(tempFile);
			}
			catch
			{
			}
		}
	}

	private static async Task<ToolResult> RunProcessAsync(string fileName, string arguments, int timeout, string workDir, CancellationToken ct)
	{
		ProcessStartInfo psi = new ProcessStartInfo
		{
			FileName = fileName,
			Arguments = arguments,
			UseShellExecute = false,
			RedirectStandardOutput = true,
			RedirectStandardError = true,
			CreateNoWindow = true,
			StandardOutputEncoding = Encoding.UTF8,
			StandardErrorEncoding = Encoding.UTF8
		};
		if (!string.IsNullOrEmpty(workDir) && Directory.Exists(workDir))
		{
			psi.WorkingDirectory = workDir;
		}
		using Process process = new Process
		{
			StartInfo = psi
		};
		StringBuilder stdout = new StringBuilder();
		StringBuilder stderr = new StringBuilder();
		process.OutputDataReceived += delegate(object _, DataReceivedEventArgs e)
		{
			if (e.Data != null)
			{
				stdout.AppendLine(e.Data);
			}
		};
		process.ErrorDataReceived += delegate(object _, DataReceivedEventArgs e)
		{
			if (e.Data != null)
			{
				stderr.AppendLine(e.Data);
			}
		};
		try
		{
			process.Start();
		}
		catch (Exception ex)
		{
			Exception ex2 = ex;
			return ToolResult.Fail("실행 실패: " + ex2.Message);
		}
		process.BeginOutputReadLine();
		process.BeginErrorReadLine();
		using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
		cts.CancelAfter(TimeSpan.FromSeconds(timeout));
		try
		{
			await process.WaitForExitAsync(cts.Token);
		}
		catch (OperationCanceledException)
		{
			try
			{
				process.Kill(entireProcessTree: true);
			}
			catch
			{
			}
			return ToolResult.Fail($"실행 타임아웃 ({timeout}초 초과)");
		}
		string output = stdout.ToString().TrimEnd();
		string error = stderr.ToString().TrimEnd();
		if (output.Length > 8000)
		{
			output = output.Substring(0, 8000) + "\n... (출력 잘림)";
		}
		StringBuilder result = new StringBuilder();
		StringBuilder stringBuilder = result;
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(13, 1, stringBuilder);
		handler.AppendLiteral("[Exit code: ");
		handler.AppendFormatted(process.ExitCode);
		handler.AppendLiteral("]");
		stringBuilder2.AppendLine(ref handler);
		if (!string.IsNullOrEmpty(output))
		{
			result.AppendLine(output);
		}
		if (!string.IsNullOrEmpty(error))
		{
			stringBuilder = result;
			StringBuilder stringBuilder3 = stringBuilder;
			handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder);
			handler.AppendLiteral("[stderr]\n");
			handler.AppendFormatted(error);
			stringBuilder3.AppendLine(ref handler);
		}
		return ToolResult.Ok(result.ToString());
	}

	private static async Task<bool> IsToolAvailableAsync(string tool)
	{
		try
		{
			ProcessStartInfo psi = new ProcessStartInfo
			{
				FileName = ((Environment.OSVersion.Platform == PlatformID.Win32NT) ? "where" : "which"),
				Arguments = tool,
				UseShellExecute = false,
				RedirectStandardOutput = true,
				CreateNoWindow = true
			};
			using Process process = Process.Start(psi);
			if (process == null)
			{
				return false;
			}
			await process.WaitForExitAsync();
			return process.ExitCode == 0;
		}
		catch
		{
			return false;
		}
	}
}
