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

namespace AxCopilot.Services.Agent;

public class TemplateRenderTool : IAgentTool
{
	public string Name => "template_render";

	public string Description => "Render a template file with variable substitution and loops. Supports Mustache-style syntax: {{variable}}, {{#list}}...{{/list}} loops, {{^variable}}...{{/variable}} inverted sections (if empty/false). Useful for generating repetitive documents like emails, reports, invoices from templates.";

	public ToolParameterSchema Parameters
	{
		get
		{
			ToolParameterSchema obj = new ToolParameterSchema
			{
				Properties = new Dictionary<string, ToolProperty>
				{
					["template_path"] = new ToolProperty
					{
						Type = "string",
						Description = "Path to template file (.html, .md, .txt). Relative to work folder."
					},
					["template_text"] = new ToolProperty
					{
						Type = "string",
						Description = "Inline template text (used if template_path is not provided)."
					},
					["variables"] = new ToolProperty
					{
						Type = "object",
						Description = "Key-value pairs for substitution. Values can be strings, numbers, or arrays of objects for loops. Example: {\"name\": \"홍길동\", \"items\": [{\"product\": \"A\", \"qty\": 10}]}"
					},
					["output_path"] = new ToolProperty
					{
						Type = "string",
						Description = "Output file path. If not provided, returns rendered text."
					}
				}
			};
			int num = 1;
			List<string> list = new List<string>(num);
			CollectionsMarshal.SetCount(list, num);
			CollectionsMarshal.AsSpan(list)[0] = "variables";
			obj.Required = list;
			return obj;
		}
	}

	public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct)
	{
		string template;
		if (args.TryGetProperty("template_path", out var tpEl) && !string.IsNullOrEmpty(tpEl.GetString()))
		{
			string templatePath = FileReadTool.ResolvePath(tpEl.GetString(), context.WorkFolder);
			if (!context.IsPathAllowed(templatePath))
			{
				return ToolResult.Fail("경로 접근 차단: " + templatePath);
			}
			if (!File.Exists(templatePath))
			{
				return ToolResult.Fail("템플릿 파일 없음: " + templatePath);
			}
			template = await File.ReadAllTextAsync(templatePath, ct);
		}
		else
		{
			if (!args.TryGetProperty("template_text", out var ttEl) || string.IsNullOrEmpty(ttEl.GetString()))
			{
				return ToolResult.Fail("template_path 또는 template_text가 필요합니다.");
			}
			template = ttEl.GetString();
		}
		if (!args.TryGetProperty("variables", out var varsEl))
		{
			return ToolResult.Fail("variables가 필요합니다.");
		}
		try
		{
			string rendered = Render(template, varsEl);
			if (args.TryGetProperty("output_path", out var opEl) && !string.IsNullOrEmpty(opEl.GetString()))
			{
				string outputPath = FileReadTool.ResolvePath(opEl.GetString(), context.WorkFolder);
				if (context.ActiveTab == "Cowork")
				{
					outputPath = AgentContext.EnsureTimestampedPath(outputPath);
				}
				if (!context.IsPathAllowed(outputPath))
				{
					return ToolResult.Fail("경로 접근 차단: " + outputPath);
				}
				if (!(await context.CheckWritePermissionAsync(Name, outputPath)))
				{
					return ToolResult.Fail("쓰기 권한 거부: " + outputPath);
				}
				string dir = Path.GetDirectoryName(outputPath);
				if (!string.IsNullOrEmpty(dir))
				{
					Directory.CreateDirectory(dir);
				}
				await File.WriteAllTextAsync(outputPath, rendered, Encoding.UTF8, ct);
				return ToolResult.Ok($"✅ 템플릿 렌더링 완료: {Path.GetFileName(outputPath)} ({rendered.Length:N0}자)", outputPath);
			}
			if (rendered.Length > 4000)
			{
				return ToolResult.Ok($"✅ 렌더링 완료 ({rendered.Length:N0}자):\n{rendered.Substring(0, 3900)}...\n[이하 생략]");
			}
			return ToolResult.Ok($"✅ 렌더링 완료 ({rendered.Length:N0}자):\n{rendered}");
		}
		catch (Exception ex)
		{
			return ToolResult.Fail("템플릿 렌더링 실패: " + ex.Message);
		}
	}

	internal static string Render(string template, JsonElement variables)
	{
		string input = template;
		input = Regex.Replace(input, "\\{\\{#(\\w+)\\}\\}(.*?)\\{\\{/\\1\\}\\}", delegate(Match match)
		{
			string value = match.Groups[1].Value;
			string value2 = match.Groups[2].Value;
			if (!variables.TryGetProperty(value, out var value3))
			{
				return "";
			}
			if (value3.ValueKind == JsonValueKind.Array)
			{
				StringBuilder stringBuilder = new StringBuilder();
				int num = 0;
				foreach (JsonElement item in value3.EnumerateArray())
				{
					string text = value2;
					if (item.ValueKind == JsonValueKind.Object)
					{
						foreach (JsonProperty item2 in item.EnumerateObject())
						{
							text = text.Replace("{{" + item2.Name + "}}", item2.Value.ToString()).Replace("{{." + item2.Name + "}}", item2.Value.ToString());
						}
					}
					else
					{
						text = text.Replace("{{.}}", item.ToString());
					}
					text = text.Replace("{{@index}}", (num + 1).ToString());
					stringBuilder.Append(text);
					num++;
				}
				return stringBuilder.ToString();
			}
			if (value3.ValueKind == JsonValueKind.True)
			{
				return RenderSimpleVars(value2, variables);
			}
			return (value3.ValueKind != JsonValueKind.False && value3.ValueKind != JsonValueKind.Null && value3.ValueKind != JsonValueKind.Undefined) ? RenderSimpleVars(value2, variables) : "";
		}, RegexOptions.Singleline);
		input = Regex.Replace(input, "\\{\\{\\^(\\w+)\\}\\}(.*?)\\{\\{/\\1\\}\\}", delegate(Match match)
		{
			string value = match.Groups[1].Value;
			string value2 = match.Groups[2].Value;
			if (!variables.TryGetProperty(value, out var value3))
			{
				return value2;
			}
			return (value3.ValueKind == JsonValueKind.False || value3.ValueKind == JsonValueKind.Null || (value3.ValueKind == JsonValueKind.Array && value3.GetArrayLength() == 0) || (value3.ValueKind == JsonValueKind.String && string.IsNullOrEmpty(value3.GetString()))) ? value2 : "";
		}, RegexOptions.Singleline);
		return RenderSimpleVars(input, variables);
	}

	private static string RenderSimpleVars(string text, JsonElement variables)
	{
		return Regex.Replace(text, "\\{\\{(\\w+)\\}\\}", delegate(Match match)
		{
			string value = match.Groups[1].Value;
			if (!variables.TryGetProperty(value, out var value2))
			{
				return match.Value;
			}
			JsonValueKind valueKind = value2.ValueKind;
			if (1 == 0)
			{
			}
			string result = valueKind switch
			{
				JsonValueKind.String => value2.GetString() ?? "", 
				JsonValueKind.Number => value2.ToString(), 
				JsonValueKind.True => "true", 
				JsonValueKind.False => "false", 
				_ => value2.ToString(), 
			};
			if (1 == 0)
			{
			}
			return result;
		});
	}
}
