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

namespace AxCopilot.Services.Agent;

public class FileEditTool : IAgentTool
{
	public string Name => "file_edit";

	public string Description => "Edit a file by replacing an exact string match. Set replace_all=true to replace all occurrences; otherwise old_string must be unique.";

	public ToolParameterSchema Parameters
	{
		get
		{
			ToolParameterSchema obj = new ToolParameterSchema
			{
				Properties = new Dictionary<string, ToolProperty>
				{
					["path"] = new ToolProperty
					{
						Type = "string",
						Description = "File path to edit"
					},
					["old_string"] = new ToolProperty
					{
						Type = "string",
						Description = "Exact string to find and replace"
					},
					["new_string"] = new ToolProperty
					{
						Type = "string",
						Description = "Replacement string"
					},
					["replace_all"] = new ToolProperty
					{
						Type = "boolean",
						Description = "Replace all occurrences (default false). If false, old_string must be unique."
					}
				}
			};
			int num = 3;
			List<string> list = new List<string>(num);
			CollectionsMarshal.SetCount(list, num);
			Span<string> span = CollectionsMarshal.AsSpan(list);
			span[0] = "path";
			span[1] = "old_string";
			span[2] = "new_string";
			obj.Required = list;
			return obj;
		}
	}

	public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct)
	{
		string path = args.GetProperty("path").GetString() ?? "";
		string oldStr = args.GetProperty("old_string").GetString() ?? "";
		string newStr = args.GetProperty("new_string").GetString() ?? "";
		JsonElement ra;
		bool replaceAll = args.TryGetProperty("replace_all", out ra) && ra.GetBoolean();
		string fullPath = FileReadTool.ResolvePath(path, context.WorkFolder);
		if (!context.IsPathAllowed(fullPath))
		{
			return ToolResult.Fail("경로 접근 차단: " + fullPath);
		}
		if (!File.Exists(fullPath))
		{
			return ToolResult.Fail("파일이 존재하지 않습니다: " + fullPath);
		}
		if (!(await context.CheckWritePermissionAsync(Name, fullPath)))
		{
			return ToolResult.Fail("쓰기 권한 거부: " + fullPath);
		}
		try
		{
			string content = await File.ReadAllTextAsync(fullPath, Encoding.UTF8, ct);
			int count = CountOccurrences(content, oldStr);
			if (count == 0)
			{
				return ToolResult.Fail("old_string을 파일에서 찾을 수 없습니다.");
			}
			if (!replaceAll && count > 1)
			{
				return ToolResult.Fail($"old_string이 {count}번 발견됩니다. replace_all=true로 전체 교체하거나, 고유한 문자열을 지정하세요.");
			}
			string diffPreview = GenerateDiff(content, oldStr, newStr, fullPath);
			string updated = content.Replace(oldStr, newStr);
			await File.WriteAllTextAsync(fullPath, updated, Encoding.UTF8, ct);
			string msg = ((replaceAll && count > 1) ? $"파일 수정 완료: {fullPath} ({count}곳 전체 교체)" : ("파일 수정 완료: " + fullPath));
			return ToolResult.Ok(msg + "\n\n" + diffPreview, fullPath);
		}
		catch (Exception ex)
		{
			return ToolResult.Fail("파일 수정 실패: " + ex.Message);
		}
	}

	private static string GenerateDiff(string content, string oldStr, string newStr, string filePath)
	{
		string[] array = content.Split('\n');
		int num = content.IndexOf(oldStr, StringComparison.Ordinal);
		if (num < 0)
		{
			return "";
		}
		int num2 = content.Substring(0, num).Count((char c) => c == '\n');
		string[] array2 = oldStr.Split('\n');
		string[] array3 = newStr.Split('\n');
		StringBuilder stringBuilder = new StringBuilder();
		string fileName = Path.GetFileName(filePath);
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder stringBuilder3 = stringBuilder2;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(13, 1, stringBuilder2);
		handler.AppendLiteral("--- ");
		handler.AppendFormatted(fileName);
		handler.AppendLiteral(" (before)");
		stringBuilder3.AppendLine(ref handler);
		stringBuilder2 = stringBuilder;
		StringBuilder stringBuilder4 = stringBuilder2;
		handler = new StringBuilder.AppendInterpolatedStringHandler(12, 1, stringBuilder2);
		handler.AppendLiteral("+++ ");
		handler.AppendFormatted(fileName);
		handler.AppendLiteral(" (after)");
		stringBuilder4.AppendLine(ref handler);
		int num3 = Math.Max(0, num2 - 2);
		int num4 = Math.Min(array.Length - 1, num2 + array2.Length - 1 + 2);
		stringBuilder2 = stringBuilder;
		StringBuilder stringBuilder5 = stringBuilder2;
		handler = new StringBuilder.AppendInterpolatedStringHandler(8, 2, stringBuilder2);
		handler.AppendLiteral("@@ -");
		handler.AppendFormatted(num3 + 1);
		handler.AppendLiteral(",");
		handler.AppendFormatted(num4 - num3 + 1);
		handler.AppendLiteral(" @@");
		stringBuilder5.AppendLine(ref handler);
		for (int num5 = num3; num5 < num2; num5++)
		{
			stringBuilder2 = stringBuilder;
			StringBuilder stringBuilder6 = stringBuilder2;
			handler = new StringBuilder.AppendInterpolatedStringHandler(1, 1, stringBuilder2);
			handler.AppendLiteral(" ");
			handler.AppendFormatted(array[num5].TrimEnd('\r'));
			stringBuilder6.AppendLine(ref handler);
		}
		string[] array4 = array2;
		foreach (string text in array4)
		{
			stringBuilder2 = stringBuilder;
			StringBuilder stringBuilder7 = stringBuilder2;
			handler = new StringBuilder.AppendInterpolatedStringHandler(1, 1, stringBuilder2);
			handler.AppendLiteral("-");
			handler.AppendFormatted(text.TrimEnd('\r'));
			stringBuilder7.AppendLine(ref handler);
		}
		string[] array5 = array3;
		foreach (string text2 in array5)
		{
			stringBuilder2 = stringBuilder;
			StringBuilder stringBuilder8 = stringBuilder2;
			handler = new StringBuilder.AppendInterpolatedStringHandler(1, 1, stringBuilder2);
			handler.AppendLiteral("+");
			handler.AppendFormatted(text2.TrimEnd('\r'));
			stringBuilder8.AppendLine(ref handler);
		}
		int num8 = num2 + array2.Length;
		for (int num9 = num8; num9 <= num4 && num9 < array.Length; num9++)
		{
			stringBuilder2 = stringBuilder;
			StringBuilder stringBuilder9 = stringBuilder2;
			handler = new StringBuilder.AppendInterpolatedStringHandler(1, 1, stringBuilder2);
			handler.AppendLiteral(" ");
			handler.AppendFormatted(array[num9].TrimEnd('\r'));
			stringBuilder9.AppendLine(ref handler);
		}
		return stringBuilder.ToString().TrimEnd();
	}

	private static int CountOccurrences(string text, string search)
	{
		if (string.IsNullOrEmpty(search))
		{
			return 0;
		}
		int num = 0;
		int startIndex = 0;
		while ((startIndex = text.IndexOf(search, startIndex, StringComparison.Ordinal)) != -1)
		{
			num++;
			startIndex += search.Length;
		}
		return num;
	}
}
