using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.Json;

namespace AxCopilot.Services.Agent;

/// <summary>cmd/powershell 명령을 실행하는 도구. 타임아웃 및 위험 명령 차단 포함.</summary>
public class ProcessTool : IAgentTool
{
    public string Name => "process";
    public string Description => "Execute a shell command (cmd or powershell). Returns stdout and stderr. Has a timeout limit.";

    public ToolParameterSchema Parameters => new()
    {
        Properties = new()
        {
            ["command"] = new() { Type = "string", Description = "Command to execute" },
            ["shell"] = new() { Type = "string", Description = "Shell to use: 'cmd' or 'powershell'. Default: 'cmd'.",
                                Enum = ["cmd", "powershell"] },
            ["timeout"] = new() { Type = "integer", Description = "Timeout in seconds. Default: 30, max: 120." },
        },
        Required = ["command"]
    };

    // 위험 명령 패턴 (대소문자 무시)
    private static readonly string[] DangerousPatterns =
    [
        "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 async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct)
    {
        if (!args.TryGetProperty("command", out var cmdEl))
            return ToolResult.Fail("command가 필요합니다.");
        var command = cmdEl.GetString() ?? "";
        var shell = args.TryGetProperty("shell", out var sh) ? sh.GetString() ?? "cmd" : "cmd";
        var timeout = args.TryGetProperty("timeout", out var to) ? Math.Min(to.GetInt32(), 120) : 30;

        if (string.IsNullOrWhiteSpace(command))
            return ToolResult.Fail("명령이 비어 있습니다.");

        // 위험 명령 차단
        foreach (var pattern in DangerousPatterns)
        {
            if (command.Contains(pattern, StringComparison.OrdinalIgnoreCase))
                return ToolResult.Fail($"위험 명령 차단: '{pattern}' 패턴이 감지되었습니다.");
        }

        // 권한 확인
        if (!await context.CheckWritePermissionAsync(Name, command))
            return ToolResult.Fail("명령 실행 권한 거부");

        try
        {
            var (fileName, arguments) = shell == "powershell"
                ? ("powershell.exe", $"-NoProfile -NonInteractive -Command \"{command.Replace("\"", "\\\"")}\"")
                : ("cmd.exe", $"/C {command}");

            var psi = new ProcessStartInfo
            {
                FileName = fileName,
                Arguments = arguments,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                CreateNoWindow = true,
                StandardOutputEncoding = Encoding.UTF8,
                StandardErrorEncoding = Encoding.UTF8,
            };

            // 작업 폴더 설정
            if (!string.IsNullOrEmpty(context.WorkFolder) && Directory.Exists(context.WorkFolder))
                psi.WorkingDirectory = context.WorkFolder;

            using var process = new Process { StartInfo = psi };
            var stdout = new StringBuilder();
            var stderr = new StringBuilder();

            process.OutputDataReceived += (_, e) => { if (e.Data != null) stdout.AppendLine(e.Data); };
            process.ErrorDataReceived += (_, e) => { if (e.Data != null) stderr.AppendLine(e.Data); };

            process.Start();
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();

            using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
            cts.CancelAfter(TimeSpan.FromSeconds(timeout));

            try
            {
                await process.WaitForExitAsync(cts.Token);
            }
            catch (OperationCanceledException)
            {
                try { process.Kill(entireProcessTree: true); } catch (Exception) { }
                return ToolResult.Fail($"명령 실행 타임아웃 ({timeout}초 초과)");
            }

            var output = stdout.ToString().TrimEnd();
            var error = stderr.ToString().TrimEnd();

            // 출력 크기 제한 (8000자)
            if (output.Length > 8000)
                output = output[..8000] + "\n... (출력 잘림)";

            var result = new StringBuilder();
            result.AppendLine($"[Exit code: {process.ExitCode}]");
            if (!string.IsNullOrEmpty(output))
                result.AppendLine(output);
            if (!string.IsNullOrEmpty(error))
                result.AppendLine($"[stderr]\n{error}");

            return process.ExitCode == 0
                ? ToolResult.Ok(result.ToString())
                : ToolResult.Ok(result.ToString()); // 비정상 종료도 결과로 반환 (LLM이 판단)
        }
        catch (Exception ex)
        {
            return ToolResult.Fail($"명령 실행 실패: {ex.Message}");
        }
    }
}
