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

namespace AxCopilot.Services.Agent;

public class LspTool : IAgentTool, IDisposable
{
	private readonly Dictionary<string, LspClientService> _clients = new Dictionary<string, LspClientService>();

	public string Name => "lsp_code_intel";

	public string Description => "코드 인텔리전스 도구. 정의 이동, 참조 검색, 심볼 목록을 제공합니다.\n- action=\"goto_definition\": 심볼의 정의 위치를 찾습니다 (파일, 라인, 컬럼)\n- action=\"find_references\": 심볼이 사용된 모든 위치를 찾습니다\n- action=\"symbols\": 파일 내 모든 심볼(클래스, 메서드, 필드 등)을 나열합니다\nfile_path, line, character 파라미터가 필요합니다 (line과 character는 0-based).";

	public ToolParameterSchema Parameters => new ToolParameterSchema
	{
		Properties = new Dictionary<string, ToolProperty>
		{
			["action"] = new ToolProperty
			{
				Type = "string",
				Description = "수행할 작업: goto_definition | find_references | symbols",
				Enum = new List<string> { "goto_definition", "find_references", "symbols" }
			},
			["file_path"] = new ToolProperty
			{
				Type = "string",
				Description = "대상 파일 경로 (절대 또는 작업 폴더 기준 상대 경로)"
			},
			["line"] = new ToolProperty
			{
				Type = "integer",
				Description = "대상 라인 번호 (0-based). symbols 액션에서는 불필요."
			},
			["character"] = new ToolProperty
			{
				Type = "integer",
				Description = "라인 내 문자 위치 (0-based). symbols 액션에서는 불필요."
			}
		},
		Required = new List<string> { "action", "file_path" }
	};

	public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default(CancellationToken))
	{
		if (!((Application.Current as App)?.SettingsService?.Settings.Llm.Code.EnableLsp ?? true))
		{
			return ToolResult.Ok("LSP 코드 인텔리전스가 비활성 상태입니다. 설정 → AX Agent → 코드에서 활성화하세요.");
		}
		JsonElement a;
		string action = (args.TryGetProperty("action", out a) ? (a.GetString() ?? "") : "");
		JsonElement f;
		string filePath = (args.TryGetProperty("file_path", out f) ? (f.GetString() ?? "") : "");
		JsonElement l;
		int line = (args.TryGetProperty("line", out l) ? l.GetInt32() : 0);
		JsonElement ch;
		int character = (args.TryGetProperty("character", out ch) ? ch.GetInt32() : 0);
		if (string.IsNullOrEmpty(filePath))
		{
			return ToolResult.Fail("file_path가 필요합니다.");
		}
		if (!Path.IsPathRooted(filePath) && !string.IsNullOrEmpty(context.WorkFolder))
		{
			filePath = Path.Combine(context.WorkFolder, filePath);
		}
		if (!File.Exists(filePath))
		{
			return ToolResult.Fail("파일을 찾을 수 없습니다: " + filePath);
		}
		string language = DetectLanguage(filePath);
		if (language == null)
		{
			return ToolResult.Fail("지원하지 않는 파일 형식: " + Path.GetExtension(filePath));
		}
		LspClientService client = await GetOrCreateClientAsync(language, context.WorkFolder, ct);
		if (client == null || !client.IsConnected)
		{
			return ToolResult.Fail(language + " 언어 서버를 시작할 수 없습니다. 해당 언어 서버가 설치되어 있는지 확인하세요.");
		}
		try
		{
			if (1 == 0)
			{
			}
			ToolResult result = action switch
			{
				"goto_definition" => await GotoDefinitionAsync(client, filePath, line, character, ct), 
				"find_references" => await FindReferencesAsync(client, filePath, line, character, ct), 
				"symbols" => await GetSymbolsAsync(client, filePath, ct), 
				_ => ToolResult.Fail("알 수 없는 액션: " + action + ". goto_definition | find_references | symbols 중 선택하세요."), 
			};
			if (1 == 0)
			{
			}
			return result;
		}
		catch (Exception ex)
		{
			return ToolResult.Fail("LSP 오류: " + ex.Message);
		}
	}

	private async Task<ToolResult> GotoDefinitionAsync(LspClientService client, string filePath, int line, int character, CancellationToken ct)
	{
		LspLocation loc = await client.GotoDefinitionAsync(filePath, line, character, ct);
		if (loc == null)
		{
			return ToolResult.Ok("정의를 찾을 수 없습니다 (해당 위치에 심볼이 없거나 외부 라이브러리일 수 있습니다).");
		}
		string contextCode = ReadCodeContext(loc.FilePath, loc.Line, 3);
		return ToolResult.Ok($"정의 위치: {loc}\n\n```\n{contextCode}\n```", loc.FilePath);
	}

	private async Task<ToolResult> FindReferencesAsync(LspClientService client, string filePath, int line, int character, CancellationToken ct)
	{
		List<LspLocation> locations = await client.FindReferencesAsync(filePath, line, character, ct);
		if (locations.Count == 0)
		{
			return ToolResult.Ok("참조를 찾을 수 없습니다.");
		}
		StringBuilder sb = new StringBuilder();
		StringBuilder stringBuilder = sb;
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder);
		handler.AppendLiteral("총 ");
		handler.AppendFormatted(locations.Count);
		handler.AppendLiteral("개 참조:");
		stringBuilder2.AppendLine(ref handler);
		foreach (LspLocation loc in locations.Take(30))
		{
			stringBuilder = sb;
			StringBuilder stringBuilder3 = stringBuilder;
			handler = new StringBuilder.AppendInterpolatedStringHandler(2, 1, stringBuilder);
			handler.AppendLiteral("  ");
			handler.AppendFormatted(loc);
			stringBuilder3.AppendLine(ref handler);
		}
		if (locations.Count > 30)
		{
			stringBuilder = sb;
			StringBuilder stringBuilder4 = stringBuilder;
			handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder);
			handler.AppendLiteral("  ... 외 ");
			handler.AppendFormatted(locations.Count - 30);
			handler.AppendLiteral("개");
			stringBuilder4.AppendLine(ref handler);
		}
		return ToolResult.Ok(sb.ToString());
	}

	private async Task<ToolResult> GetSymbolsAsync(LspClientService client, string filePath, CancellationToken ct)
	{
		List<LspSymbol> symbols = await client.GetDocumentSymbolsAsync(filePath, ct);
		if (symbols.Count == 0)
		{
			return ToolResult.Ok("심볼을 찾을 수 없습니다.");
		}
		StringBuilder sb = new StringBuilder();
		StringBuilder stringBuilder = sb;
		StringBuilder stringBuilder2 = stringBuilder;
		StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder);
		handler.AppendLiteral("총 ");
		handler.AppendFormatted(symbols.Count);
		handler.AppendLiteral("개 심볼:");
		stringBuilder2.AppendLine(ref handler);
		foreach (LspSymbol sym in symbols)
		{
			stringBuilder = sb;
			StringBuilder stringBuilder3 = stringBuilder;
			handler = new StringBuilder.AppendInterpolatedStringHandler(2, 1, stringBuilder);
			handler.AppendLiteral("  ");
			handler.AppendFormatted(sym);
			stringBuilder3.AppendLine(ref handler);
		}
		return ToolResult.Ok(sb.ToString());
	}

	private async Task<LspClientService?> GetOrCreateClientAsync(string language, string workFolder, CancellationToken ct)
	{
		if (_clients.TryGetValue(language, out LspClientService existing) && existing.IsConnected)
		{
			return existing;
		}
		LspClientService client = new LspClientService(language);
		if (!(await client.StartAsync(workFolder, ct)))
		{
			client.Dispose();
			return null;
		}
		_clients[language] = client;
		return client;
	}

	private static string? DetectLanguage(string filePath)
	{
		string text = Path.GetExtension(filePath).ToLowerInvariant();
		if (1 == 0)
		{
		}
		string result;
		switch (text)
		{
		case ".cs":
			result = "csharp";
			break;
		case ".ts":
		case ".tsx":
			result = "typescript";
			break;
		case ".js":
		case ".jsx":
			result = "javascript";
			break;
		case ".py":
			result = "python";
			break;
		case ".cpp":
		case ".cc":
		case ".cxx":
		case ".c":
		case ".h":
		case ".hpp":
			result = "cpp";
			break;
		case ".java":
			result = "java";
			break;
		default:
			result = null;
			break;
		}
		if (1 == 0)
		{
		}
		return result;
	}

	public void Dispose()
	{
		foreach (LspClientService value in _clients.Values)
		{
			value.Dispose();
		}
		_clients.Clear();
	}

	private static string ReadCodeContext(string filePath, int targetLine, int contextLines)
	{
		try
		{
			string[] array = File.ReadAllLines(filePath);
			int num = Math.Max(0, targetLine - contextLines);
			int num2 = Math.Min(array.Length - 1, targetLine + contextLines);
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = num; i <= num2; i++)
			{
				string value = ((i == targetLine) ? ">>>" : "   ");
				StringBuilder stringBuilder2 = stringBuilder;
				StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(3, 3, stringBuilder2);
				handler.AppendFormatted(value);
				handler.AppendLiteral(" ");
				handler.AppendFormatted(i + 1, 4);
				handler.AppendLiteral(": ");
				handler.AppendFormatted(array[i]);
				stringBuilder2.AppendLine(ref handler);
			}
			return stringBuilder.ToString().TrimEnd();
		}
		catch
		{
			return "(코드를 읽을 수 없습니다)";
		}
	}
}
