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 DiffPreviewTool : IAgentTool
{
	public string Name => "diff_preview";

	public string Description => "Preview file changes before applying them. Shows a unified diff and waits for user approval. If approved, writes the new content to the file. If rejected, no changes are made. The diff output is prefixed with [PREVIEW_PENDING] so the UI can show an approval panel.";

	public ToolParameterSchema Parameters
	{
		get
		{
			ToolParameterSchema obj = new ToolParameterSchema
			{
				Properties = new Dictionary<string, ToolProperty>
				{
					["path"] = new ToolProperty
					{
						Type = "string",
						Description = "File path to modify"
					},
					["new_content"] = new ToolProperty
					{
						Type = "string",
						Description = "Proposed new content for the file"
					},
					["description"] = new ToolProperty
					{
						Type = "string",
						Description = "Description of the changes (optional)"
					}
				}
			};
			int num = 2;
			List<string> list = new List<string>(num);
			CollectionsMarshal.SetCount(list, num);
			Span<string> span = CollectionsMarshal.AsSpan(list);
			span[0] = "path";
			span[1] = "new_content";
			obj.Required = list;
			return obj;
		}
	}

	public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default(CancellationToken))
	{
		string rawPath = args.GetProperty("path").GetString() ?? "";
		string newContent = args.GetProperty("new_content").GetString() ?? "";
		JsonElement d;
		string description = (args.TryGetProperty("description", out d) ? (d.GetString() ?? "") : "");
		string path = (Path.IsPathRooted(rawPath) ? rawPath : Path.Combine(context.WorkFolder, rawPath));
		if (!context.IsPathAllowed(path))
		{
			return ToolResult.Fail("경로 접근 차단: " + path);
		}
		try
		{
			string originalContent = "";
			bool isNewFile = !File.Exists(path);
			if (!isNewFile)
			{
				originalContent = await File.ReadAllTextAsync(path, ct);
			}
			string[] originalLines = (from l in originalContent.Split('\n')
				select l.TrimEnd('\r')).ToArray();
			string[] newLines = (from l in newContent.Split('\n')
				select l.TrimEnd('\r')).ToArray();
			string diff = GenerateUnifiedDiff(originalLines, newLines, path);
			StringBuilder sb = new StringBuilder();
			sb.AppendLine("[PREVIEW_PENDING]");
			StringBuilder stringBuilder;
			StringBuilder.AppendInterpolatedStringHandler handler;
			if (!string.IsNullOrEmpty(description))
			{
				stringBuilder = sb;
				StringBuilder stringBuilder2 = stringBuilder;
				handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder);
				handler.AppendLiteral("변경 설명: ");
				handler.AppendFormatted(description);
				stringBuilder2.AppendLine(ref handler);
			}
			stringBuilder = sb;
			StringBuilder stringBuilder3 = stringBuilder;
			handler = new StringBuilder.AppendInterpolatedStringHandler(4, 1, stringBuilder);
			handler.AppendLiteral("파일: ");
			handler.AppendFormatted(path);
			stringBuilder3.AppendLine(ref handler);
			sb.AppendLine(isNewFile ? "상태: 새 파일 생성" : "상태: 기존 파일 수정");
			sb.AppendLine();
			if (string.IsNullOrEmpty(diff))
			{
				sb.AppendLine("변경 사항 없음 — 내용이 동일합니다.");
			}
			else
			{
				sb.Append(diff);
			}
			if (!(await context.CheckWritePermissionAsync("diff_preview", path)))
			{
				return ToolResult.Ok($"사용자가 파일 변경을 거부했습니다.\n\n{sb}");
			}
			string dir = Path.GetDirectoryName(path);
			if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
			{
				Directory.CreateDirectory(dir);
			}
			await File.WriteAllTextAsync(path, newContent, ct);
			return ToolResult.Ok($"변경 사항이 적용되었습니다: {path}\n\n{sb}", path);
		}
		catch (Exception ex)
		{
			return ToolResult.Fail("미리보기 오류: " + ex.Message);
		}
	}

	private static string GenerateUnifiedDiff(string[] original, string[] modified, string filePath)
	{
		StringBuilder stringBuilder = new StringBuilder();
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder stringBuilder3 = stringBuilder2;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
		handler.AppendLiteral("--- ");
		handler.AppendFormatted(filePath);
		handler.AppendLiteral(" (원본)");
		stringBuilder3.AppendLine(ref handler);
		stringBuilder2 = stringBuilder;
		StringBuilder stringBuilder4 = stringBuilder2;
		handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
		handler.AppendLiteral("+++ ");
		handler.AppendFormatted(filePath);
		handler.AppendLiteral(" (수정)");
		stringBuilder4.AppendLine(ref handler);
		List<string> list = ComputeLcs(original, modified);
		int i = 0;
		int j = 0;
		int num = 0;
		List<(int, int, int, int)> list2 = new List<(int, int, int, int)>();
		while (i < original.Length || j < modified.Length)
		{
			if (num < list.Count && i < original.Length && j < modified.Length && original[i] == list[num] && modified[j] == list[num])
			{
				i++;
				j++;
				num++;
				continue;
			}
			int item = i;
			int item2 = j;
			for (; i < original.Length && (num >= list.Count || original[i] != list[num]); i++)
			{
			}
			for (; j < modified.Length && (num >= list.Count || modified[j] != list[num]); j++)
			{
			}
			list2.Add((item, i, item2, j));
		}
		if (list2.Count == 0)
		{
			return "";
		}
		foreach (var item7 in list2)
		{
			int item3 = item7.Item1;
			int item4 = item7.Item2;
			int item5 = item7.Item3;
			int item6 = item7.Item4;
			stringBuilder2 = stringBuilder;
			StringBuilder stringBuilder5 = stringBuilder2;
			handler = new StringBuilder.AppendInterpolatedStringHandler(11, 4, stringBuilder2);
			handler.AppendLiteral("@@ -");
			handler.AppendFormatted(item3 + 1);
			handler.AppendLiteral(",");
			handler.AppendFormatted(item4 - item3);
			handler.AppendLiteral(" +");
			handler.AppendFormatted(item5 + 1);
			handler.AppendLiteral(",");
			handler.AppendFormatted(item6 - item5);
			handler.AppendLiteral(" @@");
			stringBuilder5.AppendLine(ref handler);
			for (int k = item3; k < item4; k++)
			{
				stringBuilder2 = stringBuilder;
				StringBuilder stringBuilder6 = stringBuilder2;
				handler = new StringBuilder.AppendInterpolatedStringHandler(1, 1, stringBuilder2);
				handler.AppendLiteral("-");
				handler.AppendFormatted(original[k]);
				stringBuilder6.AppendLine(ref handler);
			}
			for (int l = item5; l < item6; l++)
			{
				stringBuilder2 = stringBuilder;
				StringBuilder stringBuilder7 = stringBuilder2;
				handler = new StringBuilder.AppendInterpolatedStringHandler(1, 1, stringBuilder2);
				handler.AppendLiteral("+");
				handler.AppendFormatted(modified[l]);
				stringBuilder7.AppendLine(ref handler);
			}
		}
		return stringBuilder.ToString();
	}

	private static List<string> ComputeLcs(string[] a, string[] b)
	{
		int num = a.Length;
		int num2 = b.Length;
		if ((long)num * (long)num2 > 10000000)
		{
			return new List<string>();
		}
		int[,] array = new int[num + 1, num2 + 1];
		for (int num3 = num - 1; num3 >= 0; num3--)
		{
			for (int num4 = num2 - 1; num4 >= 0; num4--)
			{
				array[num3, num4] = ((a[num3] == b[num4]) ? (array[num3 + 1, num4 + 1] + 1) : Math.Max(array[num3 + 1, num4], array[num3, num4 + 1]));
			}
		}
		List<string> list = new List<string>();
		int num5 = 0;
		int num6 = 0;
		while (num5 < num && num6 < num2)
		{
			if (a[num5] == b[num6])
			{
				list.Add(a[num5]);
				num5++;
				num6++;
			}
			else if (array[num5 + 1, num6] >= array[num5, num6 + 1])
			{
				num5++;
			}
			else
			{
				num6++;
			}
		}
		return list;
	}
}
