using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using SolutionErp.Infrastructure.Forms;

namespace SolutionErp.Infrastructure.Tests.Forms;

// S56 #6 (2026-06-09) — DocxRenderer.cs nullable-deref guard (CS8602 fix dòng :30,:40).
// Trước đây 0 test cho DocxRenderer (form-engine render path). Cover:
//   - MainDocumentPart null → InvalidOperationException("Template .docx không có MainDocumentPart")
//     (file .docx hợp lệ package nhưng KHÔNG có main document part — fail-closed message rõ).
//   - Happy path: template có {{placeholder}} → render thay đúng + giữ text ngoài placeholder.
//   - Placeholder không có key trong data → giữ nguyên literal {{...}} (không crash).
//
// DocxRenderer.RenderAsync đọc từ ĐĨA (File.ReadAllBytes) → test ghi file .docx tạm rồi xóa.
// OpenXml 3.5.1: WordprocessingDocument.Create(path, type) tạo package RỖNG (no MainDocumentPart);
// muốn happy path phải AddMainDocumentPart() + gán Document/Body.
public class DocxRendererTests
{
    // Tạo file .docx tạm có MainDocumentPart + 1 paragraph chứa text cho trước. Trả path (caller xóa).
    private static string WriteTemplateWithText(string paragraphText)
    {
        var path = Path.Combine(Path.GetTempPath(), $"se-docx-test-{Guid.NewGuid():N}.docx");
        using (var doc = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
        {
            var main = doc.AddMainDocumentPart();
            main.Document = new Document(
                new Body(
                    new Paragraph(
                        new Run(
                            new Text(paragraphText) { Space = SpaceProcessingModeValues.Preserve }))));
            main.Document.Save();
        }
        return path;
    }

    // Tạo file .docx tạm RỖNG (package hợp lệ nhưng KHÔNG AddMainDocumentPart) → MainDocumentPart == null.
    private static string WriteTemplateWithoutMainPart()
    {
        var path = Path.Combine(Path.GetTempPath(), $"se-docx-nomain-{Guid.NewGuid():N}.docx");
        using (WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
        {
            // Cố ý KHÔNG AddMainDocumentPart() — package zip hợp lệ, main part khuyết.
        }
        return path;
    }

    // Đọc lại bytes kết quả → combine toàn bộ <w:t> text để assert nội dung sau render.
    private static string ExtractBodyText(byte[] docxBytes)
    {
        using var ms = new MemoryStream(docxBytes);
        using var doc = WordprocessingDocument.Open(ms, isEditable: false);
        var body = doc.MainDocumentPart?.Document?.Body;
        body.Should().NotBeNull("kết quả render phải có Body hợp lệ để đọc text");
        var texts = body!.Descendants<Text>().Select(t => t.Text);
        return string.Concat(texts);
    }

    [Fact]
    public async Task RenderAsync_MissingMainDocumentPart_ThrowsInvalidOperationWithClearMessage()
    {
        var path = WriteTemplateWithoutMainPart();
        try
        {
            var renderer = new DocxRenderer();
            var act = async () => await renderer.RenderAsync(
                path,
                new Dictionary<string, string?> { ["name"] = "X" },
                "out.docx");

            (await act.Should().ThrowAsync<InvalidOperationException>())
                .WithMessage("*MainDocumentPart*", "guard fail-closed thay vì NullReferenceException mơ hồ");
        }
        finally
        {
            File.Delete(path);
        }
    }

    [Fact]
    public async Task RenderAsync_ReplacesPlaceholder_WithProvidedValue()
    {
        var path = WriteTemplateWithText("Xin chào {{name}}, dự án {{project}}.");
        try
        {
            var renderer = new DocxRenderer();
            var result = await renderer.RenderAsync(
                path,
                new Dictionary<string, string?> { ["name"] = "Anh Huy", ["project"] = "FLOCK" },
                "ket-qua.docx");

            result.FileName.Should().Be("ket-qua.docx");
            result.ContentType.Should().Be(
                "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
            ExtractBodyText(result.Content).Should().Be("Xin chào Anh Huy, dự án FLOCK.",
                "cả 2 placeholder được thay; text tĩnh giữ nguyên");
        }
        finally
        {
            File.Delete(path);
        }
    }

    [Fact]
    public async Task RenderAsync_UnknownPlaceholder_KeepsLiteralToken_NoCrash()
    {
        var path = WriteTemplateWithText("Mã: {{maHopDong}} — {{khongCoKey}}");
        try
        {
            var renderer = new DocxRenderer();
            var result = await renderer.RenderAsync(
                path,
                new Dictionary<string, string?> { ["maHopDong"] = "FLOCK/01/MB" },
                "out.docx");

            // Key có → thay; key thiếu → giữ literal {{khongCoKey}} (không ném, không rỗng hoá nhầm).
            ExtractBodyText(result.Content).Should().Be("Mã: FLOCK/01/MB — {{khongCoKey}}");
        }
        finally
        {
            File.Delete(path);
        }
    }

    [Fact]
    public async Task RenderAsync_NullDataValue_ReplacesWithEmptyString()
    {
        var path = WriteTemplateWithText("Ghi chú:{{note}}");
        try
        {
            var renderer = new DocxRenderer();
            var result = await renderer.RenderAsync(
                path,
                new Dictionary<string, string?> { ["note"] = null },
                "out.docx");

            ExtractBodyText(result.Content).Should().Be("Ghi chú:", "value null → thay bằng chuỗi rỗng");
        }
        finally
        {
            File.Delete(path);
        }
    }
}
