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

namespace AxCopilot.Services.Agent;

public class CodeReviewTool : IAgentTool
{
	private class DiffFile
	{
		public string Path { get; set; } = "";

		public string Status { get; set; } = "modified";

		public int Added { get; set; }

		public int Removed { get; set; }

		public List<string> Hunks { get; } = new List<string>();
	}

	private record ReviewIssue(int Line, string Severity, string Message);

	public string Name => "code_review";

	public string Description => "코드 리뷰를 수행합니다. Git diff 분석, 파일 정적 검사, PR 요약을 생성합니다.\naction별 기능:\n- diff_review: git diff 출력을 분석하여 이슈/개선점을 구조화\n- file_review: 특정 파일의 코드 품질을 정적 검사\n- pr_summary: 변경사항을 PR 설명 형식으로 요약";

	public ToolParameterSchema Parameters
	{
		get
		{
			ToolParameterSchema toolParameterSchema = new ToolParameterSchema();
			Dictionary<string, ToolProperty> dictionary = new Dictionary<string, ToolProperty>();
			ToolProperty obj = new ToolProperty
			{
				Type = "string",
				Description = "리뷰 유형: diff_review (diff 분석), file_review (파일 검사), pr_summary (PR 요약)"
			};
			int num = 3;
			List<string> list = new List<string>(num);
			CollectionsMarshal.SetCount(list, num);
			Span<string> span = CollectionsMarshal.AsSpan(list);
			span[0] = "diff_review";
			span[1] = "file_review";
			span[2] = "pr_summary";
			obj.Enum = list;
			dictionary["action"] = obj;
			dictionary["target"] = new ToolProperty
			{
				Type = "string",
				Description = "대상 지정. diff_review: '--staged' 또는 빈값(working tree). file_review: 파일 경로. pr_summary: 브랜치명(선택)."
			};
			ToolProperty obj2 = new ToolProperty
			{
				Type = "string",
				Description = "리뷰 초점: all (전체), bugs (버그), performance (성능), security (보안), style (스타일). 기본 all."
			};
			num = 5;
			List<string> list2 = new List<string>(num);
			CollectionsMarshal.SetCount(list2, num);
			Span<string> span2 = CollectionsMarshal.AsSpan(list2);
			span2[0] = "all";
			span2[1] = "bugs";
			span2[2] = "performance";
			span2[3] = "security";
			span2[4] = "style";
			obj2.Enum = list2;
			dictionary["focus"] = obj2;
			toolParameterSchema.Properties = dictionary;
			num = 1;
			List<string> list3 = new List<string>(num);
			CollectionsMarshal.SetCount(list3, num);
			CollectionsMarshal.AsSpan(list3)[0] = "action";
			toolParameterSchema.Required = list3;
			return toolParameterSchema;
		}
	}

	public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default(CancellationToken))
	{
		if (!((Application.Current as App)?.SettingsService?.Settings.Llm.Code.EnableCodeReview ?? true))
		{
			return ToolResult.Ok("코드 리뷰가 비활성 상태입니다. 설정 → AX Agent → 코드에서 활성화하세요.");
		}
		JsonElement a;
		string action = (args.TryGetProperty("action", out a) ? (a.GetString() ?? "") : "");
		JsonElement t;
		string target = (args.TryGetProperty("target", out t) ? (t.GetString() ?? "") : "");
		JsonElement f;
		string focus = (args.TryGetProperty("focus", out f) ? (f.GetString() ?? "all") : "all");
		if (string.IsNullOrEmpty(context.WorkFolder))
		{
			return ToolResult.Fail("작업 폴더가 설정되어 있지 않습니다.");
		}
		if (1 == 0)
		{
		}
		ToolResult result = action switch
		{
			"diff_review" => await DiffReviewAsync(context, target, focus, ct), 
			"file_review" => await FileReviewAsync(context, target, focus, ct), 
			"pr_summary" => await PrSummaryAsync(context, target, ct), 
			_ => ToolResult.Fail("지원하지 않는 action: " + action + ". diff_review, file_review, pr_summary 중 선택하세요."), 
		};
		if (1 == 0)
		{
		}
		return result;
	}

	private async Task<ToolResult> DiffReviewAsync(AgentContext ctx, string target, string focus, CancellationToken ct)
	{
		string diffResult = await RunGitAsync(args: string.IsNullOrEmpty(target) ? "diff" : ("diff " + target), workDir: ctx.WorkFolder, ct: ct);
		if (diffResult == null)
		{
			return ToolResult.Fail("Git을 찾을 수 없습니다.");
		}
		if (string.IsNullOrWhiteSpace(diffResult))
		{
			return ToolResult.Ok("변경사항이 없습니다. (clean working tree)");
		}
		List<DiffFile> files = ParseDiffFiles(diffResult);
		StringBuilder sb = new StringBuilder();
		sb.AppendLine("═══ Code Review Report (diff_review) ═══\n");
		int totalAdded = 0;
		int totalRemoved = 0;
		foreach (DiffFile file in files)
		{
			totalAdded += file.Added;
			totalRemoved += file.Removed;
		}
		StringBuilder stringBuilder = sb;
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(26, 3, stringBuilder);
		handler.AppendLiteral("\ud83d\udcca 파일 ");
		handler.AppendFormatted(files.Count);
		handler.AppendLiteral("개 변경  |  +");
		handler.AppendFormatted(totalAdded);
		handler.AppendLiteral(" 추가  -");
		handler.AppendFormatted(totalRemoved);
		handler.AppendLiteral(" 삭제\n");
		stringBuilder2.AppendLine(ref handler);
		stringBuilder = sb;
		StringBuilder stringBuilder3 = stringBuilder;
		handler = new StringBuilder.AppendInterpolatedStringHandler(11, 1, stringBuilder);
		handler.AppendLiteral("\ud83d\udd0d 리뷰 초점: ");
		handler.AppendFormatted(focus);
		handler.AppendLiteral("\n");
		stringBuilder3.AppendLine(ref handler);
		foreach (DiffFile file2 in files)
		{
			stringBuilder = sb;
			StringBuilder stringBuilder4 = stringBuilder;
			handler = new StringBuilder.AppendInterpolatedStringHandler(11, 2, stringBuilder);
			handler.AppendLiteral("─── ");
			handler.AppendFormatted(file2.Path);
			handler.AppendLiteral(" (");
			handler.AppendFormatted(file2.Status);
			handler.AppendLiteral(") ───");
			stringBuilder4.AppendLine(ref handler);
			stringBuilder = sb;
			StringBuilder stringBuilder5 = stringBuilder;
			handler = new StringBuilder.AppendInterpolatedStringHandler(9, 2, stringBuilder);
			handler.AppendLiteral("  변경: +");
			handler.AppendFormatted(file2.Added);
			handler.AppendLiteral(" -");
			handler.AppendFormatted(file2.Removed);
			stringBuilder5.AppendLine(ref handler);
			List<ReviewIssue> issues = AnalyzeDiffHunks(file2.Hunks, focus);
			if (issues.Count > 0)
			{
				foreach (ReviewIssue issue in issues)
				{
					stringBuilder = sb;
					StringBuilder stringBuilder6 = stringBuilder;
					handler = new StringBuilder.AppendInterpolatedStringHandler(12, 3, stringBuilder);
					handler.AppendLiteral("  [");
					handler.AppendFormatted(issue.Severity);
					handler.AppendLiteral("] Line ");
					handler.AppendFormatted(issue.Line);
					handler.AppendLiteral(": ");
					handler.AppendFormatted(issue.Message);
					stringBuilder6.AppendLine(ref handler);
				}
			}
			else
			{
				sb.AppendLine("  [OK] 정적 검사에서 특이사항 없음");
			}
			sb.AppendLine();
		}
		sb.AppendLine("═══ 위 분석 결과를 바탕으로 상세한 코드 리뷰를 작성하세요. ═══");
		return ToolResult.Ok(sb.ToString());
	}

	private async Task<ToolResult> FileReviewAsync(AgentContext ctx, string target, string focus, CancellationToken ct)
	{
		if (string.IsNullOrEmpty(target))
		{
			return ToolResult.Fail("file_review에는 target(파일 경로)이 필요합니다.");
		}
		string fullPath = (Path.IsPathRooted(target) ? target : Path.Combine(ctx.WorkFolder, target));
		if (!File.Exists(fullPath))
		{
			return ToolResult.Fail("파일을 찾을 수 없습니다: " + target);
		}
		if (!ctx.IsPathAllowed(fullPath))
		{
			return ToolResult.Fail("접근이 차단된 경로입니다: " + target);
		}
		string[] lines = (await File.ReadAllTextAsync(fullPath, ct)).Split('\n');
		StringBuilder sb = new StringBuilder();
		sb.AppendLine("═══ Code Review Report (file_review) ═══\n");
		StringBuilder stringBuilder = sb;
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder);
		handler.AppendLiteral("\ud83d\udcc1 파일: ");
		handler.AppendFormatted(target);
		stringBuilder2.AppendLine(ref handler);
		stringBuilder = sb;
		StringBuilder stringBuilder3 = stringBuilder;
		handler = new StringBuilder.AppendInterpolatedStringHandler(17, 2, stringBuilder);
		handler.AppendLiteral("\ud83d\udccf ");
		handler.AppendFormatted(lines.Length);
		handler.AppendLiteral("줄  |  \ud83d\udd0d 초점: ");
		handler.AppendFormatted(focus);
		handler.AppendLiteral("\n");
		stringBuilder3.AppendLine(ref handler);
		List<ReviewIssue> issues = AnalyzeFile(lines, focus);
		if (issues.Count > 0)
		{
			stringBuilder = sb;
			StringBuilder stringBuilder4 = stringBuilder;
			handler = new StringBuilder.AppendInterpolatedStringHandler(13, 1, stringBuilder);
			handler.AppendLiteral("⚠\ufe0f 발견된 이슈 ");
			handler.AppendFormatted(issues.Count);
			handler.AppendLiteral("개:\n");
			stringBuilder4.AppendLine(ref handler);
			foreach (ReviewIssue issue in issues)
			{
				stringBuilder = sb;
				StringBuilder stringBuilder5 = stringBuilder;
				handler = new StringBuilder.AppendInterpolatedStringHandler(12, 3, stringBuilder);
				handler.AppendLiteral("  [");
				handler.AppendFormatted(issue.Severity);
				handler.AppendLiteral("] Line ");
				handler.AppendFormatted(issue.Line);
				handler.AppendLiteral(": ");
				handler.AppendFormatted(issue.Message);
				stringBuilder5.AppendLine(ref handler);
			}
		}
		else
		{
			sb.AppendLine("✅ 정적 검사에서 특이사항 없음");
		}
		sb.AppendLine("\n─── 파일 내용 (처음 200줄) ───");
		string preview = string.Join('\n', lines.Take(200));
		sb.AppendLine(preview);
		if (lines.Length > 200)
		{
			stringBuilder = sb;
			StringBuilder stringBuilder6 = stringBuilder;
			handler = new StringBuilder.AppendInterpolatedStringHandler(11, 1, stringBuilder);
			handler.AppendLiteral("\n... (");
			handler.AppendFormatted(lines.Length - 200);
			handler.AppendLiteral("줄 생략)");
			stringBuilder6.AppendLine(ref handler);
		}
		sb.AppendLine("\n═══ 위 분석 결과와 코드를 바탕으로 상세한 리뷰를 작성하세요. ═══");
		return ToolResult.Ok(sb.ToString());
	}

	private async Task<ToolResult> PrSummaryAsync(AgentContext ctx, string target, CancellationToken ct)
	{
		StringBuilder sb = new StringBuilder();
		sb.AppendLine("═══ PR Summary Data ═══\n");
		string log = await RunGitAsync(args: string.IsNullOrEmpty(target) ? "log --oneline -20" : ("log --oneline " + target + "..HEAD"), workDir: ctx.WorkFolder, ct: ct);
		if (!string.IsNullOrWhiteSpace(log))
		{
			sb.AppendLine("\ud83d\udccb 커밋 이력:");
			sb.AppendLine(log);
			sb.AppendLine();
		}
		string stat = await RunGitAsync(args: string.IsNullOrEmpty(target) ? "diff --stat" : ("diff --stat " + target + "..HEAD"), workDir: ctx.WorkFolder, ct: ct);
		if (!string.IsNullOrWhiteSpace(stat))
		{
			sb.AppendLine("\ud83d\udcca 변경 통계:");
			sb.AppendLine(stat);
			sb.AppendLine();
		}
		string status = await RunGitAsync(ctx.WorkFolder, "status --short", ct);
		if (!string.IsNullOrWhiteSpace(status))
		{
			sb.AppendLine("\ud83d\udcc1 현재 상태:");
			sb.AppendLine(status);
			sb.AppendLine();
		}
		sb.AppendLine("═══ 위 데이터를 바탕으로 PR 제목과 설명(Summary, Changes, Test Plan)을 작성하세요. ═══");
		return ToolResult.Ok(sb.ToString());
	}

	private static List<ReviewIssue> AnalyzeDiffHunks(List<string> hunks, string focus)
	{
		List<ReviewIssue> list = new List<ReviewIssue>();
		int num = 0;
		foreach (string hunk in hunks)
		{
			Match match = Regex.Match(hunk, "@@ -\\d+(?:,\\d+)? \\+(\\d+)");
			if (match.Success)
			{
				num = int.Parse(match.Groups[1].Value);
			}
			else
			{
				if (!hunk.StartsWith('+') || hunk.StartsWith("+++"))
				{
					continue;
				}
				num++;
				string text = hunk;
				string text2 = text.Substring(1, text.Length - 1);
				if ((focus == "all" || focus == "bugs") ? true : false)
				{
					if (Regex.IsMatch(text2, "catch\\s*\\{?\\s*\\}"))
					{
						list.Add(new ReviewIssue(num, "WARNING", "빈 catch 블록 — 예외가 무시됩니다"));
					}
					if (Regex.IsMatch(text2, "\\.Result\\b|\\.Wait\\(\\)"))
					{
						list.Add(new ReviewIssue(num, "WARNING", "동기 대기 (.Result/.Wait()) — 데드락 위험"));
					}
					if (Regex.IsMatch(text2, "==\\s*null") && text2.Contains('.'))
					{
						list.Add(new ReviewIssue(num, "INFO", "null 비교 — null 조건 연산자(?.) 사용 검토"));
					}
				}
				if ((focus == "all" || focus == "security") ? true : false)
				{
					if (Regex.IsMatch(text2, "(password|secret|token|api_?key)\\s*=\\s*\"[^\"]+\"", RegexOptions.IgnoreCase))
					{
						list.Add(new ReviewIssue(num, "CRITICAL", "하드코딩된 비밀번호/키 감지"));
					}
					if (Regex.IsMatch(text2, "(TODO|FIXME|HACK|XXX)\\b", RegexOptions.IgnoreCase))
					{
						list.Add(new ReviewIssue(num, "INFO", "TODO/FIXME 마커 발견: " + text2.Trim()));
					}
				}
				if ((focus == "all" || focus == "performance") ? true : false)
				{
					if (Regex.IsMatch(text2, "new\\s+List<.*>\\(\\).*\\.Add\\(") || Regex.IsMatch(text2, "\\.ToList\\(\\).*\\.Where\\("))
					{
						list.Add(new ReviewIssue(num, "INFO", "불필요한 컬렉션 할당 가능성"));
					}
					if (Regex.IsMatch(text2, "string\\s*\\+\\s*=|\".*\"\\s*\\+\\s*\""))
					{
						list.Add(new ReviewIssue(num, "INFO", "문자열 연결 — StringBuilder 사용 검토"));
					}
				}
				bool flag = ((focus == "all" || focus == "style") ? true : false);
				if (flag && text2.Length > 150)
				{
					list.Add(new ReviewIssue(num, "STYLE", $"긴 라인 ({text2.Length}자) — 가독성 저하"));
				}
			}
		}
		return list;
	}

	private static List<ReviewIssue> AnalyzeFile(string[] lines, string focus)
	{
		List<ReviewIssue> list = new List<ReviewIssue>();
		int num = 0;
		int num2 = 0;
		bool flag = false;
		for (int i = 0; i < lines.Length; i++)
		{
			string text = lines[i];
			int num3 = i + 1;
			string text2 = text.TrimStart();
			if (Regex.IsMatch(text2, "(public|private|protected|internal|static|async|override)\\s+.*\\(.*\\)\\s*\\{?\\s*$") && !text2.Contains(';'))
			{
				flag = true;
				num2 = num3;
			}
			if (text2.Contains('{'))
			{
				num++;
			}
			bool flag4;
			if (text2.Contains('}'))
			{
				num--;
				if (flag && num <= 1)
				{
					int num4 = num3 - num2;
					bool flag2 = num4 > 60;
					bool flag3 = flag2;
					if (flag3)
					{
						flag4 = ((focus == "all" || focus == "style") ? true : false);
						flag3 = flag4;
					}
					if (flag3)
					{
						list.Add(new ReviewIssue(num2, "STYLE", $"긴 메서드 ({num4}줄) — 분할 검토"));
					}
					flag = false;
				}
			}
			if ((focus == "all" || focus == "bugs") ? true : false)
			{
				if (Regex.IsMatch(text2, "catch\\s*(\\(\\s*Exception)?\\s*\\)?\\s*\\{\\s*\\}"))
				{
					list.Add(new ReviewIssue(num3, "WARNING", "빈 catch 블록"));
				}
				if (Regex.IsMatch(text2, "\\.Result\\b|\\.Wait\\(\\)"))
				{
					list.Add(new ReviewIssue(num3, "WARNING", "동기 대기 (.Result/.Wait()) — 데드락 위험"));
				}
			}
			if ((focus == "all" || focus == "security") ? true : false)
			{
				if (Regex.IsMatch(text2, "(password|secret|token|api_?key)\\s*=\\s*\"[^\"]+\"", RegexOptions.IgnoreCase))
				{
					list.Add(new ReviewIssue(num3, "CRITICAL", "하드코딩된 비밀번호/키"));
				}
				if (Regex.IsMatch(text2, "(TODO|FIXME|HACK|XXX)\\b", RegexOptions.IgnoreCase))
				{
					list.Add(new ReviewIssue(num3, "INFO", "마커: " + text2.Trim()));
				}
			}
			flag4 = ((focus == "all" || focus == "performance") ? true : false);
			if (flag4 && Regex.IsMatch(text2, "string\\s*\\+\\s*="))
			{
				list.Add(new ReviewIssue(num3, "INFO", "루프 내 문자열 연결 — StringBuilder 검토"));
			}
			flag4 = ((focus == "all" || focus == "style") ? true : false);
			if (flag4 && text.Length > 150)
			{
				list.Add(new ReviewIssue(num3, "STYLE", $"긴 라인 ({text.Length}자)"));
			}
		}
		bool flag5 = lines.Length > 500;
		bool flag6 = flag5;
		if (flag6)
		{
			bool flag4 = ((focus == "all" || focus == "style") ? true : false);
			flag6 = flag4;
		}
		if (flag6)
		{
			list.Add(new ReviewIssue(1, "STYLE", $"큰 파일 ({lines.Length}줄) — 클래스 분할 검토"));
		}
		return list;
	}

	private static async Task<string?> RunGitAsync(string workDir, string args, CancellationToken ct)
	{
		string gitPath = FindGit();
		if (gitPath == null)
		{
			return null;
		}
		try
		{
			ProcessStartInfo psi = new ProcessStartInfo(gitPath, args)
			{
				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(30.0));
			using Process proc = Process.Start(psi);
			if (proc == null)
			{
				return null;
			}
			string stdout = await proc.StandardOutput.ReadToEndAsync(cts.Token);
			await proc.WaitForExitAsync(cts.Token);
			return (stdout.Length > 12000) ? (stdout.Substring(0, 12000) + "\n... (출력 잘림)") : stdout;
		}
		catch
		{
			return null;
		}
	}

	private static string? FindGit()
	{
		string[] array = new string[3] { "git", "C:\\Program Files\\Git\\bin\\git.exe", "C:\\Program Files (x86)\\Git\\bin\\git.exe" };
		string[] array2 = array;
		foreach (string text in array2)
		{
			try
			{
				ProcessStartInfo startInfo = new ProcessStartInfo(text, "--version")
				{
					RedirectStandardOutput = true,
					UseShellExecute = false,
					CreateNoWindow = true
				};
				using Process process = Process.Start(startInfo);
				process?.WaitForExit(3000);
				if (process != null && process.ExitCode == 0)
				{
					return text;
				}
			}
			catch
			{
			}
		}
		return null;
	}

	private static List<DiffFile> ParseDiffFiles(string diff)
	{
		List<DiffFile> list = new List<DiffFile>();
		DiffFile diffFile = null;
		string[] array = diff.Split('\n');
		foreach (string text in array)
		{
			if (text.StartsWith("diff --git"))
			{
				diffFile = new DiffFile();
				list.Add(diffFile);
				string[] array2 = text.Split(" b/");
				diffFile.Path = ((array2.Length > 1) ? array2[1].Trim() : text);
			}
			else
			{
				if (diffFile == null)
				{
					continue;
				}
				if (text.StartsWith("new file"))
				{
					diffFile.Status = "added";
				}
				else if (text.StartsWith("deleted file"))
				{
					diffFile.Status = "deleted";
				}
				else if (text.StartsWith("@@") || text.StartsWith("+") || text.StartsWith("-"))
				{
					diffFile.Hunks.Add(text);
					if (text.StartsWith("+") && !text.StartsWith("+++"))
					{
						diffFile.Added++;
					}
					if (text.StartsWith("-") && !text.StartsWith("---"))
					{
						diffFile.Removed++;
					}
				}
			}
		}
		return list;
	}
}
