using System.Text;
using System.Text.Json;

namespace YMhut.Box.Core.Tools;

public sealed record ToolMarkdownDocument(
    string ToolId,
    ToolResultKind ResultKind,
    string MarkdownText,
    string RawText,
    string Language,
    IReadOnlyDictionary<string, string> Metadata);

public static class ToolMarkdownConverter
{
    public static ToolMarkdownDocument FromResult(IToolModule module, ToolExecutionResult result, string language)
    {
        var raw = result.Ok ? result.Output : result.Error ?? string.Empty;
        var document = result.Document ?? ToolResultDocument.FromOutput(module, raw);
        var metadata = new Dictionary<string, string>(document.Metadata, StringComparer.OrdinalIgnoreCase)
        {
            ["ok"] = result.Ok.ToString(),
            ["language"] = language
        };

        return new ToolMarkdownDocument(
            module.Id,
            document.ResultKind,
            BuildMarkdown(module, document, raw, language),
            raw,
            language,
            metadata);
    }

    private static string BuildMarkdown(IToolModule module, ToolResultDocument document, string raw, string language)
    {
        if (string.IsNullOrWhiteSpace(raw))
        {
            return T(language, "_暂无可展示的结果。_", "_There is no result to display._");
        }

        if (document.ResultKind == ToolResultKind.JsonTree || LooksLikeJson(raw))
        {
            return JsonToMarkdown(raw, language);
        }

        if (document.Blocks.Count == 0)
        {
            return PlainTextToMarkdown(raw);
        }

        var builder = new StringBuilder();
        builder.AppendLine($"## {EscapeInline(module.Metadata.Name)}");
        builder.AppendLine();

        foreach (var block in document.Blocks)
        {
            AppendBlock(builder, block, language);
        }

        return builder.ToString().Trim();
    }

    private static void AppendBlock(StringBuilder builder, ToolResultBlock block, string language)
    {
        switch (block.Kind)
        {
            case ToolResultBlockKind.KeyValue:
            case ToolResultBlockKind.Metric:
                AppendHeading(builder, block.Title, T(language, "详情", "Details"));
                AppendTable(builder, [T(language, "字段", "Field"), T(language, "值", "Value")], block.Pairs.Select(pair => new[] { pair.Key, pair.Value }));
                break;
            case ToolResultBlockKind.Table:
            case ToolResultBlockKind.LineChart:
                AppendHeading(builder, block.Title, T(language, "表格", "Table"));
                AppendRowsAsTable(builder, block.Rows, language);
                break;
            case ToolResultBlockKind.CardList:
            case ToolResultBlockKind.RankedList:
            case ToolResultBlockKind.NewsList:
                AppendHeading(builder, block.Title, T(language, "列表", "List"));
                foreach (var item in block.Items)
                {
                    builder.AppendLine($"{NormalizeOrdinal(item.Leading)}. {EscapeInline(item.Title)}{Subtitle(item.Subtitle)}");
                }
                builder.AppendLine();
                break;
            case ToolResultBlockKind.Timeline:
                AppendHeading(builder, block.Title, T(language, "时间线", "Timeline"));
                foreach (var item in block.Items)
                {
                    builder.AppendLine($"- **{EscapeInline(item.Title)}**{Subtitle(item.Subtitle)}");
                }
                builder.AppendLine();
                break;
            case ToolResultBlockKind.Diff:
                AppendCode(builder, block.Title, string.Join(Environment.NewLine, block.Items.Select(item => item.Title)), "diff");
                break;
            case ToolResultBlockKind.Status:
            case ToolResultBlockKind.Text:
                AppendHeading(builder, block.Title, T(language, "内容", "Content"));
                foreach (var item in block.Items)
                {
                    builder.AppendLine($"- {EscapeInline(item.Title)}{Subtitle(item.Subtitle)}");
                }
                builder.AppendLine();
                break;
            case ToolResultBlockKind.Code:
            case ToolResultBlockKind.Json:
            case ToolResultBlockKind.JsonTree:
            case ToolResultBlockKind.Raw:
                AppendCode(builder, block.Title, block.Text, block.Language);
                break;
            case ToolResultBlockKind.Link:
                builder.AppendLine($"- [{EscapeInline(string.IsNullOrWhiteSpace(block.Text) ? block.Uri : block.Text)}]({block.Uri})");
                break;
            case ToolResultBlockKind.File:
                builder.AppendLine($"- **{T(language, "文件", "File")}**: `{EscapeCode(block.Path)}`");
                break;
            case ToolResultBlockKind.Image:
            case ToolResultBlockKind.Media:
                builder.AppendLine($"- **{T(language, "媒体", "Media")}**: [{EscapeInline(block.Text)}]({block.Uri})");
                break;
            case ToolResultBlockKind.Color:
                builder.AppendLine($"- **{T(language, "颜色", "Color")}**: `{EscapeCode(block.Text)}`");
                break;
            default:
                builder.AppendLine(EscapeInline(block.Text));
                break;
        }
    }

    private static string JsonToMarkdown(string raw, string language)
    {
        try
        {
            using var json = JsonDocument.Parse(raw);
            var root = json.RootElement;
            var builder = new StringBuilder();
            builder.AppendLine($"## {T(language, "JSON 结果", "JSON Result")}");
            builder.AppendLine();

            if (root.ValueKind == JsonValueKind.Array)
            {
                builder.AppendLine($"- **{T(language, "项目数", "Items")}**: {root.GetArrayLength()}");
                builder.AppendLine();
                var rows = root.EnumerateArray().Take(20).ToArray();
                AppendJsonArrayTable(builder, rows, language);
            }
            else if (root.ValueKind == JsonValueKind.Object)
            {
                builder.AppendLine($"- **{T(language, "字段数", "Fields")}**: {root.EnumerateObject().Count()}");
                builder.AppendLine();
                AppendTable(
                    builder,
                    [T(language, "字段", "Field"), T(language, "值", "Value")],
                    root.EnumerateObject().Take(80).Select(property => new[] { property.Name, JsonCell(property.Value) }));
            }

            AppendCode(builder, T(language, "原始 JSON", "Raw JSON"), JsonSerializer.Serialize(root, new JsonSerializerOptions { WriteIndented = true }), "json");
            return builder.ToString().Trim();
        }
        catch (JsonException exception)
        {
            var builder = new StringBuilder();
            builder.AppendLine($"## {T(language, "JSON 解析失败", "JSON Parse Failed")}");
            builder.AppendLine();
            builder.AppendLine($"> {EscapeInline(exception.Message)}");
            builder.AppendLine();
            AppendCode(builder, T(language, "原始内容", "Raw Content"), raw, "json");
            return builder.ToString().Trim();
        }
    }

    private static void AppendJsonArrayTable(StringBuilder builder, IReadOnlyList<JsonElement> rows, string language)
    {
        if (rows.Count == 0)
        {
            builder.AppendLine(T(language, "_数组为空。_", "_The array is empty._"));
            builder.AppendLine();
            return;
        }

        if (rows.Any(row => row.ValueKind != JsonValueKind.Object))
        {
            AppendTable(builder, [T(language, "序号", "Index"), T(language, "值", "Value")], rows.Select((row, index) => new[] { (index + 1).ToString(), JsonCell(row) }));
            return;
        }

        var headers = rows
            .SelectMany(row => row.EnumerateObject().Select(property => property.Name))
            .Distinct(StringComparer.Ordinal)
            .Take(8)
            .ToArray();
        AppendTable(builder, headers, rows.Select(row => headers.Select(header =>
            row.TryGetProperty(header, out var value) ? JsonCell(value) : string.Empty).ToArray()));
    }

    private static void AppendRowsAsTable(StringBuilder builder, IReadOnlyList<string[]> rows, string language)
    {
        if (rows.Count == 0)
        {
            builder.AppendLine(T(language, "_暂无表格行。_", "_No table rows._"));
            builder.AppendLine();
            return;
        }

        var headers = rows[0].Length > 0
            ? rows[0]
            : [T(language, "字段", "Field"), T(language, "值", "Value")];
        var body = rows.Skip(1);
        AppendTable(builder, headers, body);
    }

    private static void AppendTable(StringBuilder builder, IReadOnlyList<string> headers, IEnumerable<IReadOnlyList<string>> rows)
    {
        var normalizedHeaders = headers.Count == 0 ? ["Value"] : headers.Take(8).Select(EscapeTableCell).ToArray();
        builder.AppendLine($"| {string.Join(" | ", normalizedHeaders)} |");
        builder.AppendLine($"| {string.Join(" | ", normalizedHeaders.Select(_ => "---"))} |");

        foreach (var row in rows.Take(80))
        {
            var cells = normalizedHeaders.Select((_, index) => index < row.Count ? EscapeTableCell(row[index]) : string.Empty);
            builder.AppendLine($"| {string.Join(" | ", cells)} |");
        }

        builder.AppendLine();
    }

    private static void AppendHeading(StringBuilder builder, string title, string fallback)
    {
        builder.AppendLine($"### {EscapeInline(string.IsNullOrWhiteSpace(title) ? fallback : title)}");
        builder.AppendLine();
    }

    private static void AppendCode(StringBuilder builder, string title, string code, string language)
    {
        AppendHeading(builder, title, "Code");
        builder.AppendLine($"```{SafeFenceLanguage(language)}");
        builder.AppendLine(code.Replace("```", "``\\`", StringComparison.Ordinal));
        builder.AppendLine("```");
        builder.AppendLine();
    }

    private static string PlainTextToMarkdown(string raw)
    {
        if (raw.Contains('\n'))
        {
            return string.Join(Environment.NewLine, raw.Replace("\r\n", "\n").Split('\n').Select(line => string.IsNullOrWhiteSpace(line) ? string.Empty : EscapeInline(line)));
        }

        return EscapeInline(raw);
    }

    private static bool LooksLikeJson(string raw)
    {
        var trimmed = raw.TrimStart();
        return trimmed.StartsWith('{') || trimmed.StartsWith('[');
    }

    private static string JsonCell(JsonElement value)
    {
        return value.ValueKind switch
        {
            JsonValueKind.String => value.GetString() ?? string.Empty,
            JsonValueKind.Number or JsonValueKind.True or JsonValueKind.False => value.GetRawText(),
            JsonValueKind.Null => "null",
            _ => JsonSerializer.Serialize(value)
        };
    }

    private static string Subtitle(string subtitle)
    {
        return string.IsNullOrWhiteSpace(subtitle) ? string.Empty : $"  \n  {EscapeInline(subtitle)}";
    }

    private static int NormalizeOrdinal(string value)
    {
        return int.TryParse(value, out var number) && number > 0 ? number : 1;
    }

    private static string T(string language, string zh, string en)
    {
        return string.Equals(language, "en-US", StringComparison.OrdinalIgnoreCase) ? en : zh;
    }

    private static string EscapeInline(string value)
    {
        return value.Replace("|", "\\|", StringComparison.Ordinal);
    }

    private static string EscapeTableCell(string value)
    {
        return EscapeInline(value)
            .Replace("\r", " ", StringComparison.Ordinal)
            .Replace("\n", " ", StringComparison.Ordinal)
            .Trim();
    }

    private static string EscapeCode(string value)
    {
        return value.Replace("`", "\\`", StringComparison.Ordinal);
    }

    private static string SafeFenceLanguage(string language)
    {
        return string.IsNullOrWhiteSpace(language) ? string.Empty : new string(language.Where(char.IsLetterOrDigit).ToArray()).ToLowerInvariant();
    }
}
