package com.swoosh.microblog.data

import com.google.gson.JsonParser
import com.swoosh.microblog.data.model.LinkPreview
import org.junit.Assert.*
import org.junit.Test

class MobiledocBuilderTest {

    // --- Plain text (no link) ---

    @Test
    fun `build with plain text produces valid JSON`() {
        val result = MobiledocBuilder.build("Hello world", null as LinkPreview?)
        // Should parse as valid JSON
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)
    }

    @Test
    fun `build with plain text contains correct version`() {
        val result = MobiledocBuilder.build("Hello", null as LinkPreview?)
        val json = JsonParser.parseString(result).asJsonObject
        assertEquals("0.3.1", json.get("version").asString)
    }

    @Test
    fun `build with plain text has empty atoms array`() {
        val result = MobiledocBuilder.build("Hello", null as LinkPreview?)
        val json = JsonParser.parseString(result).asJsonObject
        assertTrue(json.getAsJsonArray("atoms").isEmpty)
    }

    @Test
    fun `build with plain text has empty cards when no link`() {
        val result = MobiledocBuilder.build("Hello", null as LinkPreview?)
        val json = JsonParser.parseString(result).asJsonObject
        assertTrue(json.getAsJsonArray("cards").isEmpty)
    }

    @Test
    fun `build with plain text has empty markups array`() {
        val result = MobiledocBuilder.build("Hello", null as LinkPreview?)
        val json = JsonParser.parseString(result).asJsonObject
        assertTrue(json.getAsJsonArray("markups").isEmpty)
    }

    @Test
    fun `build with plain text includes text in sections`() {
        val result = MobiledocBuilder.build("Hello world", null as LinkPreview?)
        assertTrue("Should contain the text", result.contains("Hello world"))
    }

    @Test
    fun `build with plain text has one section when no link`() {
        val result = MobiledocBuilder.build("Hello", null as LinkPreview?)
        val json = JsonParser.parseString(result).asJsonObject
        assertEquals(1, json.getAsJsonArray("sections").size())
    }

    // --- Text escaping ---

    @Test
    fun `build escapes backslashes in text`() {
        val result = MobiledocBuilder.build("path\\to\\file", null as LinkPreview?)
        assertTrue(result.contains("path\\\\to\\\\file"))
    }

    @Test
    fun `build escapes double quotes in text`() {
        val result = MobiledocBuilder.build("say \"hello\"", null as LinkPreview?)
        assertTrue(result.contains("say \\\"hello\\\""))
    }

    @Test
    fun `build escapes newlines in text`() {
        val result = MobiledocBuilder.build("line1\nline2", null as LinkPreview?)
        assertTrue(result.contains("line1\\nline2"))
    }

    @Test
    fun `build handles empty text`() {
        val result = MobiledocBuilder.build("", null as LinkPreview?)
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)
    }

    @Test
    fun `build handles text with special chars`() {
        val result = MobiledocBuilder.build("cafe & resume <html>", null as LinkPreview?)
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull("Should produce valid JSON even with special chars", json)
    }

    // --- With LinkPreview ---

    @Test
    fun `build with link preview produces valid JSON`() {
        val preview = LinkPreview(
            url = "https://example.com",
            title = "Example",
            description = "A description",
            imageUrl = null
        )
        val result = MobiledocBuilder.build("Check this out", preview)
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)
    }

    @Test
    fun `build with link preview includes bookmark card`() {
        val preview = LinkPreview(
            url = "https://example.com",
            title = "Example",
            description = "Desc",
            imageUrl = null
        )
        val result = MobiledocBuilder.build("Text", preview)
        assertTrue("Should contain bookmark", result.contains("bookmark"))
        assertTrue("Should contain URL", result.contains("https://example.com"))
    }

    @Test
    fun `build with link preview has two sections`() {
        val preview = LinkPreview(
            url = "https://example.com",
            title = "Title",
            description = null,
            imageUrl = null
        )
        val result = MobiledocBuilder.build("Text", preview)
        val json = JsonParser.parseString(result).asJsonObject
        assertEquals("Should have text section and card section", 2, json.getAsJsonArray("sections").size())
    }

    @Test
    fun `build with link preview has one card`() {
        val preview = LinkPreview(
            url = "https://example.com",
            title = "Title",
            description = "Desc",
            imageUrl = null
        )
        val result = MobiledocBuilder.build("Text", preview)
        val json = JsonParser.parseString(result).asJsonObject
        assertEquals(1, json.getAsJsonArray("cards").size())
    }

    @Test
    fun `build with link preview escapes URL`() {
        val preview = LinkPreview(
            url = "https://example.com/path?q=\"test\"",
            title = "Title",
            description = null,
            imageUrl = null
        )
        val result = MobiledocBuilder.build("Text", preview)
        // Should not break JSON parsing
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)
    }

    @Test
    fun `build with null link title uses empty string`() {
        val preview = LinkPreview(
            url = "https://example.com",
            title = null,
            description = null,
            imageUrl = null
        )
        val result = MobiledocBuilder.build("Text", preview)
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)
        // Title in metadata should be empty
        val card = json.getAsJsonArray("cards").get(0).asJsonArray
        val cardData = card.get(1).asJsonObject
        val metadata = cardData.getAsJsonObject("metadata")
        assertEquals("", metadata.get("title").asString)
    }

    @Test
    fun `build with null link description uses empty string`() {
        val preview = LinkPreview(
            url = "https://example.com",
            title = "Title",
            description = null,
            imageUrl = null
        )
        val result = MobiledocBuilder.build("Text", preview)
        val json = JsonParser.parseString(result).asJsonObject
        val card = json.getAsJsonArray("cards").get(0).asJsonArray
        val cardData = card.get(1).asJsonObject
        val metadata = cardData.getAsJsonObject("metadata")
        assertEquals("", metadata.get("description").asString)
    }

    // --- Overloaded method with separate params ---

    @Test
    fun `build with separate params and no link produces same as null preview`() {
        val resultA = MobiledocBuilder.build("Hello", null as LinkPreview?)
        val resultB = MobiledocBuilder.build("Hello")
        assertEquals(resultA, resultB)
    }

    @Test
    fun `build with separate params includes link data`() {
        val result = MobiledocBuilder.build(
            "Text",
            linkUrl = "https://test.com",
            linkTitle = "Test Title",
            linkDescription = "Test Desc"
        )
        assertTrue(result.contains("https://test.com"))
        assertTrue(result.contains("Test Title"))
        assertTrue(result.contains("Test Desc"))
        assertTrue(result.contains("bookmark"))
    }

    @Test
    fun `build with separate params handles null title and description`() {
        val result = MobiledocBuilder.build("Text", linkUrl = "https://test.com")
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)
        assertTrue(result.contains("bookmark"))
    }

    // --- escapeForJson ---

    @Test
    fun `escapeForJson escapes backslash`() {
        assertEquals("\\\\", MobiledocBuilder.escapeForJson("\\"))
    }

    @Test
    fun `escapeForJson escapes double quote`() {
        assertEquals("\\\"", MobiledocBuilder.escapeForJson("\""))
    }

    @Test
    fun `escapeForJson handles mixed special chars`() {
        assertEquals("a\\\\b\\\"c", MobiledocBuilder.escapeForJson("a\\b\"c"))
    }

    @Test
    fun `escapeForJson leaves normal text unchanged`() {
        assertEquals("hello world", MobiledocBuilder.escapeForJson("hello world"))
    }

    @Test
    fun `escapeForJson handles empty string`() {
        assertEquals("", MobiledocBuilder.escapeForJson(""))
    }

    // --- Image card with alt text (HEAD tests) ---

    @Test
    fun `build with image card produces valid JSON`() {
        val result = MobiledocBuilder.build(
            "Post text",
            imageUrls = listOf("https://example.com/photo.jpg"),
            imageAlt = "A sunset"
        )
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)
    }

    @Test
    fun `build with image card includes image type`() {
        val result = MobiledocBuilder.build(
            "Text",
            imageUrls = listOf("https://example.com/photo.jpg"),
            imageAlt = "Alt text"
        )
        assertTrue("Should contain image card type", result.contains("\"image\""))
    }

    @Test
    fun `build with image card includes src`() {
        val result = MobiledocBuilder.build(
            "Text",
            imageUrls = listOf("https://example.com/photo.jpg"),
            imageAlt = "Alt text"
        )
        assertTrue("Should contain image src", result.contains("https://example.com/photo.jpg"))
    }

    @Test
    fun `build with image card includes alt text`() {
        val result = MobiledocBuilder.build(
            "Text",
            imageUrls = listOf("https://example.com/photo.jpg"),
            imageAlt = "A beautiful sunset"
        )
        val json = JsonParser.parseString(result).asJsonObject
        val cards = json.getAsJsonArray("cards")
        assertEquals(1, cards.size())
        val card = cards.get(0).asJsonArray
        assertEquals("image", card.get(0).asString)
        val cardData = card.get(1).asJsonObject
        assertEquals("A beautiful sunset", cardData.get("alt").asString)
    }

    @Test
    fun `build with image card and null alt uses empty string`() {
        val result = MobiledocBuilder.build(
            "Text",
            imageUrls = listOf("https://example.com/photo.jpg")
        )
        val json = JsonParser.parseString(result).asJsonObject
        val cards = json.getAsJsonArray("cards")
        val card = cards.get(0).asJsonArray
        val cardData = card.get(1).asJsonObject
        assertEquals("", cardData.get("alt").asString)
    }

    @Test
    fun `build with image card includes caption field`() {
        val result = MobiledocBuilder.build(
            "Text",
            imageUrls = listOf("https://example.com/photo.jpg"),
            imageAlt = "Alt"
        )
        val json = JsonParser.parseString(result).asJsonObject
        val card = json.getAsJsonArray("cards").get(0).asJsonArray
        val cardData = card.get(1).asJsonObject
        assertEquals("", cardData.get("caption").asString)
    }

    @Test
    fun `build with image card has card section`() {
        val result = MobiledocBuilder.build(
            "Text",
            imageUrls = listOf("https://example.com/photo.jpg"),
            imageAlt = "Alt"
        )
        val json = JsonParser.parseString(result).asJsonObject
        val sections = json.getAsJsonArray("sections")
        assertEquals("Should have text section and image card section", 2, sections.size())
    }

    @Test
    fun `build with image and link has both cards`() {
        val result = MobiledocBuilder.build(
            "Text",
            imageUrls = listOf("https://example.com/photo.jpg"),
            linkUrl = "https://link.com",
            linkTitle = "Link Title",
            linkDescription = "Link Desc",
            imageAlt = "Image alt"
        )
        val json = JsonParser.parseString(result).asJsonObject
        val cards = json.getAsJsonArray("cards")
        assertEquals("Should have image card and bookmark card", 2, cards.size())
        // Image card comes first
        assertEquals("image", cards.get(0).asJsonArray.get(0).asString)
        assertEquals("bookmark", cards.get(1).asJsonArray.get(0).asString)
    }

    @Test
    fun `build with image and link has three sections`() {
        val result = MobiledocBuilder.build(
            "Text",
            imageUrls = listOf("https://example.com/photo.jpg"),
            linkUrl = "https://link.com",
            linkTitle = "Title",
            linkDescription = "Desc",
            imageAlt = "Alt"
        )
        val json = JsonParser.parseString(result).asJsonObject
        val sections = json.getAsJsonArray("sections")
        assertEquals("Should have text, image card, and bookmark card sections", 3, sections.size())
    }

    @Test
    fun `build with image card escapes alt text`() {
        val result = MobiledocBuilder.build(
            "Text",
            imageUrls = listOf("https://example.com/photo.jpg"),
            imageAlt = "He said \"hello\""
        )
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull("Should produce valid JSON with escaped alt text", json)
    }

    @Test
    fun `build without image produces no image card`() {
        val result = MobiledocBuilder.build("Text")
        val json = JsonParser.parseString(result).asJsonObject
        assertTrue("Should have no cards", json.getAsJsonArray("cards").isEmpty)
    }

    @Test
    fun `build with image card section references correct card index`() {
        val result = MobiledocBuilder.build(
            "Text",
            imageUrls = listOf("https://example.com/photo.jpg"),
            imageAlt = "Alt"
        )
        val json = JsonParser.parseString(result).asJsonObject
        val sections = json.getAsJsonArray("sections")
        val cardSection = sections.get(1).asJsonArray
        assertEquals(10, cardSection.get(0).asInt)
        assertEquals(0, cardSection.get(1).asInt)
    }

    @Test
    fun `build with image and link card sections reference correct indices`() {
        val result = MobiledocBuilder.build(
            "Text",
            imageUrls = listOf("https://example.com/photo.jpg"),
            linkUrl = "https://link.com",
            linkTitle = "Title",
            imageAlt = "Alt"
        )
        val json = JsonParser.parseString(result).asJsonObject
        val sections = json.getAsJsonArray("sections")
        // Image card section at index 0
        val imgSection = sections.get(1).asJsonArray
        assertEquals(10, imgSection.get(0).asInt)
        assertEquals(0, imgSection.get(1).asInt)
        // Bookmark card section at index 1
        val bookmarkSection = sections.get(2).asJsonArray
        assertEquals(10, bookmarkSection.get(0).asInt)
        assertEquals(1, bookmarkSection.get(1).asInt)
    }

    // --- Multi-image support (multi-image branch tests) ---

    @Test
    fun `build with single image produces valid JSON`() {
        val result = MobiledocBuilder.build(
            "Hello", listOf("https://example.com/img.jpg")
        )
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)
    }

    @Test
    fun `build with single image has one image card`() {
        val result = MobiledocBuilder.build(
            "Hello", listOf("https://example.com/img.jpg")
        )
        val json = JsonParser.parseString(result).asJsonObject
        assertEquals(1, json.getAsJsonArray("cards").size())

        val card = json.getAsJsonArray("cards").get(0).asJsonArray
        assertEquals("image", card.get(0).asString)
        assertEquals("https://example.com/img.jpg", card.get(1).asJsonObject.get("src").asString)
    }

    @Test
    fun `build with single image has two sections`() {
        val result = MobiledocBuilder.build(
            "Hello", listOf("https://example.com/img.jpg")
        )
        val json = JsonParser.parseString(result).asJsonObject
        assertEquals(2, json.getAsJsonArray("sections").size())
    }

    @Test
    fun `build with multiple images produces valid JSON`() {
        val images = listOf(
            "https://example.com/img1.jpg",
            "https://example.com/img2.jpg",
            "https://example.com/img3.jpg"
        )
        val result = MobiledocBuilder.build("Hello", images)
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)
    }

    @Test
    fun `build with multiple images has correct number of cards`() {
        val images = listOf(
            "https://example.com/img1.jpg",
            "https://example.com/img2.jpg",
            "https://example.com/img3.jpg"
        )
        val result = MobiledocBuilder.build("Hello", images)
        val json = JsonParser.parseString(result).asJsonObject
        assertEquals(3, json.getAsJsonArray("cards").size())
    }

    @Test
    fun `build with multiple images has correct number of sections`() {
        val images = listOf(
            "https://example.com/img1.jpg",
            "https://example.com/img2.jpg"
        )
        val result = MobiledocBuilder.build("Hello", images)
        val json = JsonParser.parseString(result).asJsonObject
        // 1 text section + 2 card sections
        assertEquals(3, json.getAsJsonArray("sections").size())
    }

    @Test
    fun `build with multiple images all cards are image type`() {
        val images = listOf(
            "https://example.com/img1.jpg",
            "https://example.com/img2.jpg"
        )
        val result = MobiledocBuilder.build("Hello", images)
        val json = JsonParser.parseString(result).asJsonObject
        val cards = json.getAsJsonArray("cards")
        for (i in 0 until cards.size()) {
            val card = cards.get(i).asJsonArray
            assertEquals("image", card.get(0).asString)
        }
    }

    @Test
    fun `build with multiple images preserves correct src URLs`() {
        val images = listOf(
            "https://example.com/img1.jpg",
            "https://example.com/img2.jpg",
            "https://example.com/img3.jpg"
        )
        val result = MobiledocBuilder.build("Hello", images)
        val json = JsonParser.parseString(result).asJsonObject
        val cards = json.getAsJsonArray("cards")
        assertEquals("https://example.com/img1.jpg", cards.get(0).asJsonArray.get(1).asJsonObject.get("src").asString)
        assertEquals("https://example.com/img2.jpg", cards.get(1).asJsonArray.get(1).asJsonObject.get("src").asString)
        assertEquals("https://example.com/img3.jpg", cards.get(2).asJsonArray.get(1).asJsonObject.get("src").asString)
    }

    @Test
    fun `build with images and link has both image and bookmark cards`() {
        val images = listOf("https://example.com/img1.jpg")
        val result = MobiledocBuilder.build(
            "Hello", images, linkUrl = "https://example.com", linkTitle = "Title", linkDescription = "Desc"
        )
        val json = JsonParser.parseString(result).asJsonObject
        val cards = json.getAsJsonArray("cards")
        assertEquals(2, cards.size())

        // First card is image
        assertEquals("image", cards.get(0).asJsonArray.get(0).asString)
        // Second card is bookmark
        assertEquals("bookmark", cards.get(1).asJsonArray.get(0).asString)
    }

    @Test
    fun `build with images and link has correct number of sections`() {
        val images = listOf("https://example.com/img1.jpg", "https://example.com/img2.jpg")
        val result = MobiledocBuilder.build(
            "Hello", images, linkUrl = "https://example.com", linkTitle = "Title", linkDescription = "Desc"
        )
        val json = JsonParser.parseString(result).asJsonObject
        // 1 text section + 2 image card sections + 1 bookmark card section
        assertEquals(4, json.getAsJsonArray("sections").size())
    }

    @Test
    fun `build with images card sections reference correct card indices`() {
        val images = listOf("https://example.com/img1.jpg", "https://example.com/img2.jpg")
        val result = MobiledocBuilder.build("Hello", images)
        val json = JsonParser.parseString(result).asJsonObject
        val sections = json.getAsJsonArray("sections")

        // sections[0] is text: [1, "p", ...]
        assertEquals(1, sections.get(0).asJsonArray.get(0).asInt)

        // sections[1] references card 0: [10, 0]
        assertEquals(10, sections.get(1).asJsonArray.get(0).asInt)
        assertEquals(0, sections.get(1).asJsonArray.get(1).asInt)

        // sections[2] references card 1: [10, 1]
        assertEquals(10, sections.get(2).asJsonArray.get(0).asInt)
        assertEquals(1, sections.get(2).asJsonArray.get(1).asInt)
    }

    @Test
    fun `build with empty image list produces no image cards`() {
        val result = MobiledocBuilder.build("Hello", emptyList())
        val json = JsonParser.parseString(result).asJsonObject
        assertTrue(json.getAsJsonArray("cards").isEmpty)
        assertEquals(1, json.getAsJsonArray("sections").size())
    }

    @Test
    fun `build with empty image list matches no-image build`() {
        val resultA = MobiledocBuilder.build("Hello", null as LinkPreview?)
        val resultB = MobiledocBuilder.build("Hello", emptyList())
        assertEquals(resultA, resultB)
    }

    @Test
    fun `build with image URL containing special chars produces valid JSON`() {
        val images = listOf("https://example.com/img?id=1&name=\"test\"")
        val result = MobiledocBuilder.build("Hello", images)
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)
    }

    // --- Multi-image with alt text (merged feature tests) ---

    @Test
    fun `build with multiple images and alt text applies alt to first image only`() {
        val images = listOf(
            "https://example.com/img1.jpg",
            "https://example.com/img2.jpg"
        )
        val result = MobiledocBuilder.build("Text", images, imageAlt = "First image alt")
        val json = JsonParser.parseString(result).asJsonObject
        val cards = json.getAsJsonArray("cards")

        // First image should have alt text
        val firstCard = cards.get(0).asJsonArray.get(1).asJsonObject
        assertEquals("First image alt", firstCard.get("alt").asString)

        // Second image should have empty alt
        val secondCard = cards.get(1).asJsonArray.get(1).asJsonObject
        assertEquals("", secondCard.get("alt").asString)
    }

    // --- Video card tests ---

    @Test
    fun `build with video only produces valid JSON with video card`() {
        val result = MobiledocBuilder.build(
            "Check this video",
            videoUrl = "https://example.com/video.mp4"
        )
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)

        val cards = json.getAsJsonArray("cards")
        assertEquals(1, cards.size())
        val card = cards.get(0).asJsonArray
        assertEquals("video", card.get(0).asString)
        val cardData = card.get(1).asJsonObject
        assertEquals("https://example.com/video.mp4", cardData.get("src").asString)
        assertFalse(cardData.get("loop").asBoolean)
    }

    @Test
    fun `build with video has correct sections`() {
        val result = MobiledocBuilder.build(
            "Text",
            videoUrl = "https://example.com/video.mp4"
        )
        val json = JsonParser.parseString(result).asJsonObject
        val sections = json.getAsJsonArray("sections")
        assertEquals("Should have text section + video card section", 2, sections.size())

        val videoSection = sections.get(1).asJsonArray
        assertEquals(10, videoSection.get(0).asInt)
        assertEquals(0, videoSection.get(1).asInt)
    }

    // --- Audio card tests ---

    @Test
    fun `build with audio only produces valid JSON with audio card`() {
        val result = MobiledocBuilder.build(
            "Listen to this",
            audioUrl = "https://example.com/audio.mp3"
        )
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)

        val cards = json.getAsJsonArray("cards")
        assertEquals(1, cards.size())
        val card = cards.get(0).asJsonArray
        assertEquals("audio", card.get(0).asString)
        val cardData = card.get(1).asJsonObject
        assertEquals("https://example.com/audio.mp3", cardData.get("src").asString)
    }

    @Test
    fun `build with audio has correct sections`() {
        val result = MobiledocBuilder.build(
            "Text",
            audioUrl = "https://example.com/audio.mp3"
        )
        val json = JsonParser.parseString(result).asJsonObject
        val sections = json.getAsJsonArray("sections")
        assertEquals("Should have text section + audio card section", 2, sections.size())
    }

    // --- Combined: text + images + video + audio + bookmark ---

    @Test
    fun `build with all media types produces valid JSON with correct card order`() {
        val images = listOf("https://example.com/img1.jpg", "https://example.com/img2.jpg")
        val result = MobiledocBuilder.build(
            "Full post", images,
            linkUrl = "https://link.com",
            linkTitle = "Link Title",
            linkDescription = "Link Desc",
            imageAlt = "Alt text",
            videoUrl = "https://example.com/video.mp4",
            audioUrl = "https://example.com/audio.mp3"
        )
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)

        val cards = json.getAsJsonArray("cards")
        // 2 images + 1 video + 1 audio + 1 bookmark = 5 cards
        assertEquals(5, cards.size())

        // Verify card order: image, image, video, audio, bookmark
        assertEquals("image", cards.get(0).asJsonArray.get(0).asString)
        assertEquals("image", cards.get(1).asJsonArray.get(0).asString)
        assertEquals("video", cards.get(2).asJsonArray.get(0).asString)
        assertEquals("audio", cards.get(3).asJsonArray.get(0).asString)
        assertEquals("bookmark", cards.get(4).asJsonArray.get(0).asString)

        // Verify sections: 1 text + 5 card sections = 6
        val sections = json.getAsJsonArray("sections")
        assertEquals(6, sections.size())

        // Verify card section indices are sequential
        for (i in 0 until 5) {
            val cardSection = sections.get(i + 1).asJsonArray
            assertEquals(10, cardSection.get(0).asInt)
            assertEquals(i, cardSection.get(1).asInt)
        }
    }

    @Test
    fun `build with video and audio but no images produces correct cards`() {
        val result = MobiledocBuilder.build(
            "Media post",
            videoUrl = "https://example.com/video.mp4",
            audioUrl = "https://example.com/audio.mp3"
        )
        val json = JsonParser.parseString(result).asJsonObject
        val cards = json.getAsJsonArray("cards")
        assertEquals(2, cards.size())
        assertEquals("video", cards.get(0).asJsonArray.get(0).asString)
        assertEquals("audio", cards.get(1).asJsonArray.get(0).asString)
    }

    @Test
    fun `build with video URL containing special chars produces valid JSON`() {
        val result = MobiledocBuilder.build(
            "Text",
            videoUrl = "https://example.com/video?id=1&name=\"test\""
        )
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)
    }

    // --- File card tests ---

    @Test
    fun `build with file card produces valid JSON`() {
        val result = MobiledocBuilder.build(
            "Post text",
            fileUrl = "https://example.com/files/report.pdf",
            fileName = "report.pdf",
            fileSize = 102400
        )
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull(json)
    }

    @Test
    fun `build with file card includes file type`() {
        val result = MobiledocBuilder.build(
            "Text",
            fileUrl = "https://example.com/files/doc.pdf",
            fileName = "doc.pdf",
            fileSize = 5000
        )
        val json = JsonParser.parseString(result).asJsonObject
        val cards = json.getAsJsonArray("cards")
        assertEquals(1, cards.size())
        val card = cards.get(0).asJsonArray
        assertEquals("file", card.get(0).asString)
    }

    @Test
    fun `build with file card includes src fileName and fileSize`() {
        val result = MobiledocBuilder.build(
            "Text",
            fileUrl = "https://example.com/files/report.pdf",
            fileName = "report.pdf",
            fileSize = 204800
        )
        val json = JsonParser.parseString(result).asJsonObject
        val cards = json.getAsJsonArray("cards")
        val cardData = cards.get(0).asJsonArray.get(1).asJsonObject
        assertEquals("https://example.com/files/report.pdf", cardData.get("src").asString)
        assertEquals("report.pdf", cardData.get("fileName").asString)
        assertEquals(204800, cardData.get("fileSize").asLong)
    }

    @Test
    fun `build with file card has correct section count`() {
        val result = MobiledocBuilder.build(
            "Text",
            fileUrl = "https://example.com/file.pdf",
            fileName = "file.pdf",
            fileSize = 1000
        )
        val json = JsonParser.parseString(result).asJsonObject
        val sections = json.getAsJsonArray("sections")
        assertEquals("Should have text section and file card section", 2, sections.size())
    }

    @Test
    fun `build with file card comes after image and bookmark cards`() {
        val images = listOf("https://example.com/img.jpg")
        val result = MobiledocBuilder.build(
            "Text", images,
            linkUrl = "https://link.com",
            linkTitle = "Link Title",
            linkDescription = "Desc",
            imageAlt = "Alt",
            fileUrl = "https://example.com/file.pdf",
            fileName = "file.pdf",
            fileSize = 1024
        )
        val json = JsonParser.parseString(result).asJsonObject
        val cards = json.getAsJsonArray("cards")
        assertEquals(3, cards.size())
        // Image first, bookmark second, file last
        assertEquals("image", cards.get(0).asJsonArray.get(0).asString)
        assertEquals("bookmark", cards.get(1).asJsonArray.get(0).asString)
        assertEquals("file", cards.get(2).asJsonArray.get(0).asString)
    }

    @Test
    fun `build with file card and all attachments has correct section count`() {
        val images = listOf("https://example.com/img1.jpg", "https://example.com/img2.jpg")
        val result = MobiledocBuilder.build(
            "Text", images,
            linkUrl = "https://link.com",
            linkTitle = "Title",
            linkDescription = "Desc",
            fileUrl = "https://example.com/file.pdf",
            fileName = "file.pdf",
            fileSize = 500
        )
        val json = JsonParser.parseString(result).asJsonObject
        // 1 text + 2 image + 1 bookmark + 1 file = 5
        assertEquals(5, json.getAsJsonArray("sections").size())
    }

    @Test
    fun `build without file produces no file card`() {
        val result = MobiledocBuilder.build("Text")
        val json = JsonParser.parseString(result).asJsonObject
        assertTrue(json.getAsJsonArray("cards").isEmpty)
    }

    @Test
    fun `build with file card escapes fileName`() {
        val result = MobiledocBuilder.build(
            "Text",
            fileUrl = "https://example.com/file.pdf",
            fileName = "my \"special\" file.pdf",
            fileSize = 100
        )
        val json = JsonParser.parseString(result).asJsonObject
        assertNotNull("Should produce valid JSON with escaped file name", json)
    }
}
