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 BuildRunTool : IAgentTool
{
	private record ProjectInfo(string Type, string Marker, string BuildCommand, string TestCommand, string RunCommand, string LintCommand, string FormatCommand);

	private static readonly string[] DangerousPatterns = new string[16]
	{
		"format ", "del /s", "rd /s", "rmdir /s", "rm -rf", "Remove-Item -Recurse -Force", "Stop-Computer", "Restart-Computer", "shutdown", "taskkill /f",
		"reg delete", "reg add", "net user", "net localgroup", "schtasks /create", "schtasks /delete"
	};

	public string Name => "build_run";

	public string Description => "Detect project type and run build/test commands. Supports: .NET (dotnet), Maven (mvn), Gradle, Node.js (npm), Python (pytest), CMake, Make. Actions: detect (show project type), build, test, run, custom (run arbitrary command with longer timeout).";

	public ToolParameterSchema Parameters
	{
		get
		{
			ToolParameterSchema toolParameterSchema = new ToolParameterSchema();
			Dictionary<string, ToolProperty> dictionary = new Dictionary<string, ToolProperty>();
			ToolProperty obj = new ToolProperty
			{
				Type = "string",
				Description = "Action to perform: detect, build, test, run, lint, format, custom"
			};
			int num = 7;
			List<string> list = new List<string>(num);
			CollectionsMarshal.SetCount(list, num);
			Span<string> span = CollectionsMarshal.AsSpan(list);
			span[0] = "detect";
			span[1] = "build";
			span[2] = "test";
			span[3] = "run";
			span[4] = "lint";
			span[5] = "format";
			span[6] = "custom";
			obj.Enum = list;
			dictionary["action"] = obj;
			dictionary["command"] = new ToolProperty
			{
				Type = "string",
				Description = "Custom command to execute (required for action='custom')"
			};
			dictionary["project_path"] = new ToolProperty
			{
				Type = "string",
				Description = "Subdirectory within work folder (optional, defaults to work folder root)"
			};
			toolParameterSchema.Properties = dictionary;
			num = 1;
			List<string> list2 = new List<string>(num);
			CollectionsMarshal.SetCount(list2, num);
			CollectionsMarshal.AsSpan(list2)[0] = "action";
			toolParameterSchema.Required = list2;
			return toolParameterSchema;
		}
	}

	public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct)
	{
		string action = args.GetProperty("action").GetString() ?? "detect";
		JsonElement cmd;
		string customCmd = (args.TryGetProperty("command", out cmd) ? (cmd.GetString() ?? "") : "");
		JsonElement pp;
		string subPath = (args.TryGetProperty("project_path", out pp) ? (pp.GetString() ?? "") : "");
		string workDir = context.WorkFolder;
		if (!string.IsNullOrEmpty(subPath))
		{
			workDir = Path.Combine(workDir, subPath);
		}
		if (string.IsNullOrEmpty(workDir) || !Directory.Exists(workDir))
		{
			return ToolResult.Fail("작업 폴더가 유효하지 않습니다: " + workDir);
		}
		ProjectInfo project = DetectProjectType(workDir);
		if (action == "detect")
		{
			if (project == null)
			{
				return ToolResult.Ok("프로젝트 감지 실패: " + workDir + "\n알려진 프로젝트 마커 파일이 없습니다.");
			}
			return ToolResult.Ok($"프로젝트 감지 완료:\n  타입: {project.Type}\n  마커: {project.Marker}\n  빌드: {project.BuildCommand}\n  테스트: {project.TestCommand}\n  실행: {project.RunCommand}\n  린트: {(string.IsNullOrEmpty(project.LintCommand) ? "(미지원)" : project.LintCommand)}\n  포맷: {(string.IsNullOrEmpty(project.FormatCommand) ? "(미지원)" : project.FormatCommand)}\n  경로: {workDir}");
		}
		string command;
		if (action == "custom")
		{
			if (string.IsNullOrWhiteSpace(customCmd))
			{
				return ToolResult.Fail("custom 액션에는 command 파라미터가 필요합니다.");
			}
			command = customCmd;
		}
		else
		{
			if (project == null)
			{
				return ToolResult.Fail("프로젝트 타입을 감지할 수 없습니다. action='custom'으로 직접 명령을 지정하세요.");
			}
			if (1 == 0)
			{
			}
			string text = action switch
			{
				"build" => project.BuildCommand, 
				"test" => project.TestCommand, 
				"run" => project.RunCommand, 
				"lint" => string.IsNullOrEmpty(project.LintCommand) ? null : project.LintCommand, 
				"format" => string.IsNullOrEmpty(project.FormatCommand) ? null : project.FormatCommand, 
				_ => project.BuildCommand, 
			};
			if (1 == 0)
			{
			}
			command = text;
			if (command == null)
			{
				return ToolResult.Fail($"이 프로젝트 타입({project.Type})에서 '{action}' 작업은 지원되지 않습니다.");
			}
		}
		string[] dangerousPatterns = DangerousPatterns;
		foreach (string pattern in dangerousPatterns)
		{
			if (command.Contains(pattern, StringComparison.OrdinalIgnoreCase))
			{
				return ToolResult.Fail("차단된 명령 패턴: " + pattern);
			}
		}
		if (!(await context.CheckWritePermissionAsync(Name, workDir)))
		{
			return ToolResult.Fail("빌드 실행 권한이 거부되었습니다.");
		}
		int timeout = (Application.Current as App)?.SettingsService?.Settings.Llm.Code.BuildTimeout ?? 120;
		try
		{
			ProcessStartInfo psi = new ProcessStartInfo("cmd.exe", "/C " + command)
			{
				WorkingDirectory = workDir,
				RedirectStandardOutput = true,
				RedirectStandardError = true,
				UseShellExecute = false,
				CreateNoWindow = true,
				StandardOutputEncoding = Encoding.UTF8,
				StandardErrorEncoding = Encoding.UTF8
			};
			using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
			cts.CancelAfter(TimeSpan.FromSeconds(timeout));
			using Process proc = Process.Start(psi);
			if (proc == null)
			{
				return ToolResult.Fail("프로세스 시작 실패");
			}
			Task<string> stdoutTask = proc.StandardOutput.ReadToEndAsync(cts.Token);
			Task<string> stderrTask = proc.StandardError.ReadToEndAsync(cts.Token);
			await proc.WaitForExitAsync(cts.Token);
			string stdout = await stdoutTask;
			string stderr = await stderrTask;
			if (stdout.Length > 8000)
			{
				stdout = stdout.Substring(0, 8000) + "\n... (출력 잘림)";
			}
			if (stderr.Length > 4000)
			{
				stderr = stderr.Substring(0, 4000) + "\n... (출력 잘림)";
			}
			StringBuilder sb = new StringBuilder();
			StringBuilder stringBuilder = sb;
			StringBuilder stringBuilder2 = stringBuilder;
			StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(3, 2, stringBuilder);
			handler.AppendLiteral("[");
			handler.AppendFormatted(action);
			handler.AppendLiteral("] ");
			handler.AppendFormatted(command);
			stringBuilder2.AppendLine(ref handler);
			stringBuilder = sb;
			StringBuilder stringBuilder3 = stringBuilder;
			handler = new StringBuilder.AppendInterpolatedStringHandler(13, 1, stringBuilder);
			handler.AppendLiteral("[Exit code: ");
			handler.AppendFormatted(proc.ExitCode);
			handler.AppendLiteral("]");
			stringBuilder3.AppendLine(ref handler);
			if (!string.IsNullOrWhiteSpace(stdout))
			{
				sb.AppendLine(stdout);
			}
			if (!string.IsNullOrWhiteSpace(stderr))
			{
				stringBuilder = sb;
				StringBuilder stringBuilder4 = stringBuilder;
				handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder);
				handler.AppendLiteral("[stderr]\n");
				handler.AppendFormatted(stderr);
				stringBuilder4.AppendLine(ref handler);
			}
			return (proc.ExitCode == 0) ? ToolResult.Ok(sb.ToString()) : ToolResult.Fail(sb.ToString());
		}
		catch (OperationCanceledException)
		{
			return ToolResult.Fail($"빌드 타임아웃 ({timeout}초 초과): {command}");
		}
		catch (Exception ex2)
		{
			return ToolResult.Fail("실행 오류: " + ex2.Message);
		}
	}

	private static ProjectInfo? DetectProjectType(string dir)
	{
		if (Directory.GetFiles(dir, "*.sln").Length != 0)
		{
			return new ProjectInfo(".NET Solution", "*.sln", "dotnet build", "dotnet test", "dotnet run", "dotnet format --verify-no-changes", "dotnet format");
		}
		if (Directory.GetFiles(dir, "*.csproj").Length != 0)
		{
			return new ProjectInfo(".NET Project", "*.csproj", "dotnet build", "dotnet test", "dotnet run", "dotnet format --verify-no-changes", "dotnet format");
		}
		if (File.Exists(Path.Combine(dir, "pom.xml")))
		{
			return new ProjectInfo("Maven", "pom.xml", "mvn compile", "mvn test", "mvn exec:java", "mvn checkstyle:check", "");
		}
		if (Directory.GetFiles(dir, "build.gradle*").Length != 0)
		{
			return new ProjectInfo("Gradle", "build.gradle", "gradle build", "gradle test", "gradle run", "gradle check", "");
		}
		if (File.Exists(Path.Combine(dir, "package.json")))
		{
			return new ProjectInfo("Node.js", "package.json", "npm run build", "npm test", "npm start", "npx eslint .", "npx prettier --write .");
		}
		if (File.Exists(Path.Combine(dir, "CMakeLists.txt")))
		{
			return new ProjectInfo("CMake", "CMakeLists.txt", "cmake --build build", "ctest --test-dir build", "", "", "");
		}
		if (File.Exists(Path.Combine(dir, "pyproject.toml")))
		{
			return new ProjectInfo("Python (pyproject)", "pyproject.toml", "python -m build", "python -m pytest", "python -m", "python -m ruff check .", "python -m black .");
		}
		if (File.Exists(Path.Combine(dir, "setup.py")))
		{
			return new ProjectInfo("Python (setup.py)", "setup.py", "python setup.py build", "python -m pytest", "python", "python -m ruff check .", "python -m black .");
		}
		if (File.Exists(Path.Combine(dir, "Makefile")))
		{
			return new ProjectInfo("Make", "Makefile", "make", "make test", "make run", "make lint", "make format");
		}
		if (Directory.GetFiles(dir, "*.py").Length != 0)
		{
			return new ProjectInfo("Python Scripts", "*.py", "", "python -m pytest", "python", "python -m ruff check .", "python -m black .");
		}
		return null;
	}
}
