using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using AxCopilot.Models;

namespace AxCopilot.Services.Agent;

public static class AgentHookRunner
{
	private const int MaxEnvValueLength = 4096;

	public static async Task<List<HookExecutionResult>> RunAsync(IReadOnlyList<AgentHookEntry> hooks, string toolName, string timing, string? toolInput = null, string? toolOutput = null, bool success = true, string? workFolder = null, int timeoutMs = 10000, CancellationToken ct = default(CancellationToken))
	{
		List<HookExecutionResult> results = new List<HookExecutionResult>();
		if (hooks == null || hooks.Count == 0)
		{
			return results;
		}
		foreach (AgentHookEntry hook in hooks)
		{
			if (hook.Enabled && string.Equals(hook.Timing, timing, StringComparison.OrdinalIgnoreCase) && (!(hook.ToolName != "*") || string.Equals(hook.ToolName, toolName, StringComparison.OrdinalIgnoreCase)))
			{
				results.Add(await ExecuteHookAsync(hook, toolName, timing, toolInput, toolOutput, success, workFolder, timeoutMs, ct));
			}
		}
		return results;
	}

	private static async Task<HookExecutionResult> ExecuteHookAsync(AgentHookEntry hook, string toolName, string timing, string? toolInput, string? toolOutput, bool success, string? workFolder, int timeoutMs, CancellationToken ct)
	{
		try
		{
			if (string.IsNullOrWhiteSpace(hook.ScriptPath))
			{
				return new HookExecutionResult(hook.Name, Success: false, "스크립트 경로가 비어 있습니다.");
			}
			string scriptPath = Environment.ExpandEnvironmentVariables(hook.ScriptPath);
			if (!File.Exists(scriptPath))
			{
				return new HookExecutionResult(hook.Name, Success: false, "스크립트를 찾을 수 없습니다: " + scriptPath);
			}
			string ext = Path.GetExtension(scriptPath).ToLowerInvariant();
			string fileName;
			string arguments;
			switch (ext)
			{
			case ".ps1":
				fileName = "powershell.exe";
				arguments = "-NoProfile -ExecutionPolicy Bypass -File \"" + scriptPath + "\"";
				break;
			case ".bat":
			case ".cmd":
				fileName = "cmd.exe";
				arguments = "/c \"" + scriptPath + "\"";
				break;
			default:
				return new HookExecutionResult(hook.Name, Success: false, "지원하지 않는 스크립트 확장자: " + ext + " (.bat/.cmd/.ps1만 허용)");
			}
			if (!string.IsNullOrWhiteSpace(hook.Arguments))
			{
				arguments = arguments + " " + hook.Arguments;
			}
			ProcessStartInfo psi = new ProcessStartInfo
			{
				FileName = fileName,
				Arguments = arguments,
				WorkingDirectory = (workFolder ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)),
				UseShellExecute = false,
				CreateNoWindow = true,
				RedirectStandardOutput = true,
				RedirectStandardError = true
			};
			psi.EnvironmentVariables["AX_TOOL_NAME"] = toolName;
			psi.EnvironmentVariables["AX_TOOL_TIMING"] = timing;
			psi.EnvironmentVariables["AX_TOOL_INPUT"] = Truncate(toolInput, 4096);
			psi.EnvironmentVariables["AX_WORK_FOLDER"] = workFolder ?? "";
			if (string.Equals(timing, "post", StringComparison.OrdinalIgnoreCase))
			{
				psi.EnvironmentVariables["AX_TOOL_OUTPUT"] = Truncate(toolOutput, 4096);
				psi.EnvironmentVariables["AX_TOOL_SUCCESS"] = (success ? "true" : "false");
			}
			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);
				}
			};
			process.Start();
			process.BeginOutputReadLine();
			process.BeginErrorReadLine();
			using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
			cts.CancelAfter(timeoutMs);
			try
			{
				await process.WaitForExitAsync(cts.Token);
			}
			catch (OperationCanceledException)
			{
				try
				{
					process.Kill(entireProcessTree: true);
				}
				catch
				{
				}
				return new HookExecutionResult(hook.Name, Success: false, $"타임아웃 ({timeoutMs}ms 초과)");
			}
			int exitCode = process.ExitCode;
			string output = stdOut.ToString().TrimEnd();
			string error = stdErr.ToString().TrimEnd();
			if (exitCode != 0)
			{
				return new HookExecutionResult(hook.Name, Success: false, $"종료 코드 {exitCode}: {(string.IsNullOrEmpty(error) ? output : error)}");
			}
			return new HookExecutionResult(hook.Name, Success: true, string.IsNullOrEmpty(output) ? "(정상 완료)" : output);
		}
		catch (Exception ex2)
		{
			Exception ex3 = ex2;
			return new HookExecutionResult(hook.Name, Success: false, "훅 실행 예외: " + ex3.Message);
		}
	}

	private static string Truncate(string? value, int maxLen)
	{
		return string.IsNullOrEmpty(value) ? "" : ((value.Length <= maxLen) ? value : value.Substring(0, maxLen));
	}
}
