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

namespace AxCopilot.Services.Agent;

/// <summary>
/// 여러 섹션의 내용을 하나의 완성된 문서로 조립하는 도구.
/// 멀티패스 문서 생성의 3단계: 개별 생성된 섹션들을 최종 문서로 결합합니다.
/// </summary>
public class DocumentAssemblerTool : IAgentTool
{

    public string Name => "document_assemble";
    public string Description =>
        "Assemble multiple individually-written sections into a single complete document. " +
        "Use this after writing each section separately with document_plan. " +
        "Supports HTML, DOCX, and Markdown output. " +
        "Automatically adds table of contents, cover page, and section numbering for HTML. " +
        "After assembly, the document is auto-validated for quality issues.";

    public ToolParameterSchema Parameters => new()
    {
        Properties = new()
        {
            ["path"] = new() { Type = "string", Description = "Output file path. Relative to work folder." },
            ["title"] = new() { Type = "string", Description = "Document title" },
            ["sections"] = new()
            {
                Type = "array",
                Description = "Array of section objects: [{\"heading\": \"1. 개요\", \"content\": \"HTML or markdown body...\", \"level\": 1}]. " +
                    "content should be the detailed text for each section.",
                Items = new() { Type = "object" }
            },
            ["format"] = new()
            {
                Type = "string",
                Description = "Output format: html, docx, markdown. Default: html",
                Enum = ["html", "docx", "markdown"]
            },
            ["mood"] = new()
            {
                Type = "string",
                Description = "Design theme for HTML output: modern, professional, creative, corporate, dashboard, etc. Default: professional"
            },
            ["toc"] = new() { Type = "boolean", Description = "Auto-generate table of contents. Default: true" },
            ["cover_subtitle"] = new() { Type = "string", Description = "Subtitle for cover page. If provided, a cover page is added." },
            ["header"] = new() { Type = "string", Description = "Header text for DOCX output." },
            ["footer"] = new() { Type = "string", Description = "Footer text for DOCX output. Use {page} for page number." },
        },
        Required = ["path", "title", "sections"]
    };

    public async Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct)
    {
        var path = args.GetProperty("path").GetString() ?? "";
        var title = args.GetProperty("title").GetString() ?? "Document";
        var format = args.TryGetProperty("format", out var fmt) ? fmt.GetString() ?? "html" : "html";
        var mood = args.TryGetProperty("mood", out var m) ? m.GetString() ?? "professional" : "professional";
        var useToc = !args.TryGetProperty("toc", out var tocVal) || tocVal.GetBoolean(); // default true
        var coverSubtitle = args.TryGetProperty("cover_subtitle", out var cs) ? cs.GetString() : null;
        var headerText = args.TryGetProperty("header", out var hdr) ? hdr.GetString() : null;
        var footerText = args.TryGetProperty("footer", out var ftr) ? ftr.GetString() : null;

        if (!args.TryGetProperty("sections", out var sectionsEl) || sectionsEl.ValueKind != JsonValueKind.Array)
            return ToolResult.Fail("sections 배열이 필요합니다.");

        var sections = new List<(string Heading, string Content, int Level)>();
        foreach (var sec in sectionsEl.EnumerateArray())
        {
            var heading = sec.TryGetProperty("heading", out var h) ? h.GetString() ?? "" : "";
            var content = sec.TryGetProperty("content", out var c) ? c.GetString() ?? "" : "";
            var level = sec.TryGetProperty("level", out var lv) ? lv.GetInt32() : 1;
            if (!string.IsNullOrWhiteSpace(heading) || !string.IsNullOrWhiteSpace(content))
                sections.Add((heading, content, level));
        }

        if (sections.Count == 0)
            return ToolResult.Fail("조립할 섹션이 없습니다.");

        var fullPath = FileReadTool.ResolvePath(path, context.WorkFolder);
        if (context.ActiveTab == "Cowork") fullPath = AgentContext.EnsureTimestampedPath(fullPath);

        // 확장자 자동 추가
        var ext = format switch
        {
            "docx" => ".docx",
            "markdown" => ".md",
            _ => ".html"
        };
        if (!fullPath.EndsWith(ext, StringComparison.OrdinalIgnoreCase))
            fullPath += ext;

        if (!context.IsPathAllowed(fullPath))
            return ToolResult.Fail($"경로 접근 차단: {fullPath}");
        if (!await context.CheckWritePermissionAsync(Name, fullPath))
            return ToolResult.Fail($"쓰기 권한 거부: {fullPath}");

        var dir = Path.GetDirectoryName(fullPath);
        if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);

        try
        {
            string resultMsg;
            switch (format)
            {
                case "docx":
                    resultMsg = AssembleDocx(fullPath, title, sections, headerText, footerText);
                    break;
                case "markdown":
                    resultMsg = AssembleMarkdown(fullPath, title, sections);
                    break;
                default:
                    resultMsg = AssembleHtml(fullPath, title, sections, mood, useToc, coverSubtitle);
                    break;
            }

            // 품질 요약 통계
            var totalChars = sections.Sum(s => s.Content.Length);
            var totalWords = sections.Sum(s => EstimateWordCount(s.Content));
            var pageEstimate = Math.Max(1, totalWords / 500);

            return ToolResult.Ok(
                $"✅ 문서 조립 완료: {Path.GetFileName(fullPath)}\n" +
                $"  섹션: {sections.Count}개 | 글자: {totalChars:N0} | 단어: ~{totalWords:N0} | 예상 페이지: ~{pageEstimate}\n" +
                $"{resultMsg}", fullPath);
        }
        catch (Exception ex)
        {
            return ToolResult.Fail($"문서 조립 실패: {ex.Message}");
        }
    }

    private string AssembleHtml(string path, string title, List<(string Heading, string Content, int Level)> sections,
        string mood, bool toc, string? coverSubtitle)
    {
        var sb = new StringBuilder();
        var style = TemplateService.GetCss(mood);
        var moodInfo = TemplateService.GetMood(mood);

        sb.AppendLine("<!DOCTYPE html>");
        sb.AppendLine("<html lang=\"ko\">");
        sb.AppendLine("<head>");
        sb.AppendLine("<meta charset=\"utf-8\">");
        sb.AppendLine($"<title>{Escape(title)}</title>");
        sb.AppendLine("<style>");
        sb.AppendLine(style);
        // 추가 조립용 스타일
        sb.AppendLine(@"
.assembled-doc { max-width: 900px; margin: 0 auto; padding: 40px 30px 60px; }
.assembled-doc h1 { font-size: 28px; margin-bottom: 8px; }
.assembled-doc h2 { font-size: 22px; margin-top: 36px; margin-bottom: 12px; border-bottom: 2px solid var(--accent, #4B5EFC); padding-bottom: 6px; }
.assembled-doc h3 { font-size: 18px; margin-top: 24px; margin-bottom: 8px; }
.assembled-doc .section-content { line-height: 1.8; margin-bottom: 20px; }
.assembled-doc .toc { background: #f8f9fa; border-radius: 12px; padding: 20px 28px; margin: 24px 0 32px; }
.assembled-doc .toc h3 { margin-top: 0; }
.assembled-doc .toc a { color: inherit; text-decoration: none; }
.assembled-doc .toc a:hover { text-decoration: underline; }
.assembled-doc .toc ul { list-style: none; padding-left: 0; }
.assembled-doc .toc li { padding: 4px 0; }
.cover-page { text-align: center; padding: 120px 40px 80px; page-break-after: always; }
.cover-page h1 { font-size: 36px; margin-bottom: 16px; }
.cover-page .subtitle { font-size: 18px; color: #666; margin-bottom: 40px; }
.cover-page .date { font-size: 14px; color: #999; }
@media print { .cover-page { page-break-after: always; } .assembled-doc h2 { page-break-before: auto; } }
");
        sb.AppendLine("</style>");
        sb.AppendLine("</head>");
        sb.AppendLine("<body>");

        // 커버 페이지
        if (!string.IsNullOrWhiteSpace(coverSubtitle))
        {
            sb.AppendLine("<div class=\"cover-page\">");
            sb.AppendLine($"<h1>{Escape(title)}</h1>");
            sb.AppendLine($"<div class=\"subtitle\">{Escape(coverSubtitle)}</div>");
            sb.AppendLine($"<div class=\"date\">{DateTime.Now:yyyy년 MM월 dd일}</div>");
            sb.AppendLine("</div>");
        }

        sb.AppendLine("<div class=\"assembled-doc\">");

        if (string.IsNullOrWhiteSpace(coverSubtitle))
            sb.AppendLine($"<h1>{Escape(title)}</h1>");

        // TOC 생성
        if (toc && sections.Count > 1)
        {
            sb.AppendLine("<div class=\"toc\">");
            sb.AppendLine("<h3>📋 목차</h3>");
            sb.AppendLine("<ul>");
            for (int i = 0; i < sections.Count; i++)
            {
                var indent = sections[i].Level > 1 ? " style=\"padding-left:20px\"" : "";
                sb.AppendLine($"<li{indent}><a href=\"#section-{i + 1}\">{Escape(sections[i].Heading)}</a></li>");
            }
            sb.AppendLine("</ul>");
            sb.AppendLine("</div>");
        }

        // 섹션 본문
        for (int i = 0; i < sections.Count; i++)
        {
            var (heading, content, level) = sections[i];
            var tag = level <= 1 ? "h2" : "h3";
            sb.AppendLine($"<{tag} id=\"section-{i + 1}\">{Escape(heading)}</{tag}>");
            sb.AppendLine($"<div class=\"section-content\">{content}</div>");
        }

        sb.AppendLine("</div>");
        sb.AppendLine("</body>");
        sb.AppendLine("</html>");

        File.WriteAllText(path, sb.ToString(), Encoding.UTF8);

        var issues = ValidateBasic(sb.ToString());
        return issues.Count > 0
            ? $"  ⚠ 품질 검증 이슈 {issues.Count}건: {string.Join("; ", issues)}"
            : "  ✓ 품질 검증 통과";
    }

    private string AssembleDocx(string path, string title, List<(string Heading, string Content, int Level)> sections,
        string? headerText, string? footerText)
    {
        // DOCX 조립: DocxSkill의 sections 형식으로 변환하여 OpenXML 사용
        using var doc = DocumentFormat.OpenXml.Packaging.WordprocessingDocument.Create(
            path, DocumentFormat.OpenXml.WordprocessingDocumentType.Document);

        var mainPart = doc.AddMainDocumentPart();
        mainPart.Document = new DocumentFormat.OpenXml.Wordprocessing.Document();
        var body = mainPart.Document.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Body());

        // 제목
        var titlePara = new DocumentFormat.OpenXml.Wordprocessing.Paragraph();
        var titleRun = new DocumentFormat.OpenXml.Wordprocessing.Run();
        titleRun.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.RunProperties
        {
            Bold = new DocumentFormat.OpenXml.Wordprocessing.Bold(),
            FontSize = new DocumentFormat.OpenXml.Wordprocessing.FontSize { Val = "48" }
        });
        titleRun.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Text(title));
        titlePara.AppendChild(titleRun);
        body.AppendChild(titlePara);

        // 빈 줄
        body.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Paragraph());

        // 각 섹션
        foreach (var (heading, content, level) in sections)
        {
            // 섹션 제목
            var headPara = new DocumentFormat.OpenXml.Wordprocessing.Paragraph();
            var headRun = new DocumentFormat.OpenXml.Wordprocessing.Run();
            headRun.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.RunProperties
            {
                Bold = new DocumentFormat.OpenXml.Wordprocessing.Bold(),
                FontSize = new DocumentFormat.OpenXml.Wordprocessing.FontSize { Val = level <= 1 ? "32" : "28" },
                Color = new DocumentFormat.OpenXml.Wordprocessing.Color { Val = "2B579A" },
            });
            headRun.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Text(heading));
            headPara.AppendChild(headRun);
            body.AppendChild(headPara);

            // 섹션 본문 (줄 단위 분할)
            var lines = StripHtmlTags(content).Split('\n', StringSplitOptions.RemoveEmptyEntries);
            foreach (var line in lines)
            {
                var para = new DocumentFormat.OpenXml.Wordprocessing.Paragraph();
                var run = new DocumentFormat.OpenXml.Wordprocessing.Run();
                run.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Text(line.Trim())
                {
                    Space = DocumentFormat.OpenXml.SpaceProcessingModeValues.Preserve
                });
                para.AppendChild(run);
                body.AppendChild(para);
            }

            // 섹션 간 빈 줄
            body.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Paragraph());
        }

        return "  ✓ DOCX 조립 완료";
    }

    private string AssembleMarkdown(string path, string title, List<(string Heading, string Content, int Level)> sections)
    {
        var sb = new StringBuilder();
        sb.AppendLine($"# {title}");
        sb.AppendLine();
        sb.AppendLine($"*작성일: {DateTime.Now:yyyy-MM-dd}*");
        sb.AppendLine();

        // TOC
        if (sections.Count > 1)
        {
            sb.AppendLine("## 목차");
            sb.AppendLine();
            foreach (var (heading, _, _) in sections)
            {
                var anchor = heading.Replace(" ", "-").ToLowerInvariant();
                sb.AppendLine($"- [{heading}](#{anchor})");
            }
            sb.AppendLine();
            sb.AppendLine("---");
            sb.AppendLine();
        }

        foreach (var (heading, content, level) in sections)
        {
            var prefix = level <= 1 ? "##" : "###";
            sb.AppendLine($"{prefix} {heading}");
            sb.AppendLine();
            sb.AppendLine(StripHtmlTags(content));
            sb.AppendLine();
        }

        File.WriteAllText(path, sb.ToString(), Encoding.UTF8);
        return "  ✓ Markdown 조립 완료";
    }

    private static int EstimateWordCount(string text)
    {
        if (string.IsNullOrWhiteSpace(text)) return 0;
        var plain = StripHtmlTags(text);
        // 한국어: 글자 수 / 3 ≈ 단어 수, 영어: 공백 분리
        var spaces = plain.Count(c => c == ' ');
        var koreanChars = plain.Count(c => c >= 0xAC00 && c <= 0xD7A3);
        return spaces + 1 + koreanChars / 3;
    }

    private static string StripHtmlTags(string html)
    {
        if (string.IsNullOrEmpty(html)) return "";
        return System.Text.RegularExpressions.Regex.Replace(html, "<[^>]+>", " ")
            .Replace("&nbsp;", " ")
            .Replace("&amp;", "&")
            .Replace("&lt;", "<")
            .Replace("&gt;", ">")
            .Replace("  ", " ")
            .Trim();
    }

    private static List<string> ValidateBasic(string html)
    {
        var issues = new List<string>();
        if (html.Length < 500)
            issues.Add("문서 내용이 매우 짧습니다 (500자 미만)");

        // 빈 섹션 검사
        var emptySectionPattern = new System.Text.RegularExpressions.Regex(
            @"<h[23][^>]*>[^<]+</h[23]>\s*<div class=""section-content"">\s*</div>",
            System.Text.RegularExpressions.RegexOptions.IgnoreCase);
        var emptyMatches = emptySectionPattern.Matches(html);
        if (emptyMatches.Count > 0)
            issues.Add($"빈 섹션 {emptyMatches.Count}개 발견");

        // 플레이스홀더 검사
        if (html.Contains("[TODO]", StringComparison.OrdinalIgnoreCase) ||
            html.Contains("[PLACEHOLDER]", StringComparison.OrdinalIgnoreCase) ||
            html.Contains("Lorem ipsum", StringComparison.OrdinalIgnoreCase))
            issues.Add("플레이스홀더 텍스트가 남아있습니다");

        return issues;
    }

    private static string Escape(string text)
    {
        return text.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;");
    }
}
