using System.IO;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;

namespace AxCopilot.Services.Agent;

/// <summary>
/// Mustache 스타일 변수 치환 기반 템플릿 렌더링 도구.
/// {{variable}}, {{#list}}...{{/list}} 반복, {{^cond}}...{{/cond}} 조건부 렌더링을 지원합니다.
/// </summary>
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 => new()
    {
        Properties = new()
        {
            ["template_path"] = new()
            {
                Type = "string",
                Description = "Path to template file (.html, .md, .txt). Relative to work folder."
            },
            ["template_text"] = new()
            {
                Type = "string",
                Description = "Inline template text (used if template_path is not provided)."
            },
            ["variables"] = new()
            {
                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()
            {
                Type = "string",
                Description = "Output file path. If not provided, returns rendered text."
            },
        },
        Required = ["variables"]
    };

    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()))
        {
            var 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()))
        {
            template = ttEl.GetString()!;
        }
        else
        {
            return ToolResult.Fail("template_path 또는 template_text가 필요합니다.");
        }

        if (!args.TryGetProperty("variables", out var varsEl))
            return ToolResult.Fail("variables가 필요합니다.");

        try
        {
            // 렌더링
            var rendered = Render(template, varsEl);

            // 출력
            if (args.TryGetProperty("output_path", out var opEl) && !string.IsNullOrEmpty(opEl.GetString()))
            {
                var 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}");

                var 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[..3900]}...\n[이하 생략]");

            return ToolResult.Ok($"✅ 렌더링 완료 ({rendered.Length:N0}자):\n{rendered}");
        }
        catch (Exception ex)
        {
            return ToolResult.Fail($"템플릿 렌더링 실패: {ex.Message}");
        }
    }

    /// <summary>Mustache 스타일 템플릿을 렌더링합니다.</summary>
    internal static string Render(string template, JsonElement variables)
    {
        var result = template;

        // 1. 반복 섹션 {{#key}}...{{/key}}
        result = Regex.Replace(result, @"\{\{#(\w+)\}\}(.*?)\{\{/\1\}\}", match =>
        {
            var key = match.Groups[1].Value;
            var body = match.Groups[2].Value;

            if (!variables.TryGetProperty(key, out var val)) return "";

            if (val.ValueKind == JsonValueKind.Array)
            {
                var sb = new StringBuilder();
                int index = 0;
                foreach (var item in val.EnumerateArray())
                {
                    var itemBody = body;
                    // {{.key}} 또는 {{key}} 치환
                    if (item.ValueKind == JsonValueKind.Object)
                    {
                        foreach (var prop in item.EnumerateObject())
                        {
                            itemBody = itemBody
                                .Replace($"{{{{{prop.Name}}}}}", prop.Value.ToString())
                                .Replace($"{{{{.{prop.Name}}}}}", prop.Value.ToString());
                        }
                    }
                    else
                    {
                        itemBody = itemBody.Replace("{{.}}", item.ToString());
                    }
                    // {{@index}} 치환
                    itemBody = itemBody.Replace("{{@index}}", (index + 1).ToString());
                    sb.Append(itemBody);
                    index++;
                }
                return sb.ToString();
            }

            // bool true → 섹션 표시
            if (val.ValueKind == JsonValueKind.True)
                return RenderSimpleVars(body, variables);

            // 값이 있으면 표시
            if (val.ValueKind != JsonValueKind.False &&
                val.ValueKind != JsonValueKind.Null &&
                val.ValueKind != JsonValueKind.Undefined)
                return RenderSimpleVars(body, variables);

            return "";
        }, RegexOptions.Singleline);

        // 2. 반전 섹션 {{^key}}...{{/key}} (값이 없거나 false일 때 표시)
        result = Regex.Replace(result, @"\{\{\^(\w+)\}\}(.*?)\{\{/\1\}\}", match =>
        {
            var key = match.Groups[1].Value;
            var body = match.Groups[2].Value;

            if (!variables.TryGetProperty(key, out var val)) return body;
            if (val.ValueKind == JsonValueKind.False ||
                val.ValueKind == JsonValueKind.Null ||
                (val.ValueKind == JsonValueKind.Array && val.GetArrayLength() == 0) ||
                (val.ValueKind == JsonValueKind.String && string.IsNullOrEmpty(val.GetString())))
                return body;

            return "";
        }, RegexOptions.Singleline);

        // 3. 단순 변수 치환 {{key}}
        result = RenderSimpleVars(result, variables);

        return result;
    }

    private static string RenderSimpleVars(string text, JsonElement variables)
    {
        return Regex.Replace(text, @"\{\{(\w+)\}\}", match =>
        {
            var key = match.Groups[1].Value;
            if (!variables.TryGetProperty(key, out var val)) return match.Value;

            return val.ValueKind switch
            {
                JsonValueKind.String => val.GetString() ?? "",
                JsonValueKind.Number => val.ToString(),
                JsonValueKind.True => "true",
                JsonValueKind.False => "false",
                _ => val.ToString()
            };
        });
    }
}
