package net.sergeych.toread.fb2

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class Fb2FormatTest {
    @Test
    fun parsesPlainXml() {
        val book = Fb2Format.parseXml(sampleXml)

        assertEquals("The Test Book", book.title)
        assertEquals("Ada Lovelace", book.authors.single().displayName)
        assertEquals("en", book.language)
        assertEquals(listOf("sf"), book.genres)
        assertEquals("Chapter 1", book.sections.single().title)
        assertEquals(listOf("Hello & welcome."), book.sections.single().paragraphs)
    }

    @Test
    fun exportsXmlThatCanBeParsedAgain() {
        val book = Fb2Book(
            title = "Exported",
            authors = listOf(Fb2Author(firstName = "Ann", lastName = "Writer")),
            language = "en",
            genres = listOf("prose"),
            sections = listOf(Fb2Section(title = "Start", paragraphs = listOf("One < two & three."))),
        )

        val reparsed = Fb2Format.parseXml(Fb2Format.exportXml(book))

        assertEquals(book.title, reparsed.title)
        assertEquals(book.authors.single().displayName, reparsed.authors.single().displayName)
        assertEquals(book.sections.single().paragraphs, reparsed.sections.single().paragraphs)
    }

    @Test
    fun parsesStoredZip() {
        val zip = Fb2Format.exportZip(Fb2Format.parseXml(sampleXml), "sample.fb2")
        val book = Fb2Format.parse(zip, "sample.fb2.zip")

        assertEquals("The Test Book", book.title)
        assertTrue(zip.copyOfRange(0, 4).contentEquals(byteArrayOf(0x50, 0x4b, 0x03, 0x04)))
    }

    @Test
    fun preservesReadableBlocksAndInlineStyles() {
        val book = Fb2Format.parseXml(richXml)
        val section = book.sections.single()

        assertEquals("Part", section.blocks[0].let { (it as Fb2Block.Subtitle).content.plainText })
        val paragraph = section.blocks[1] as Fb2Block.Paragraph
        assertEquals("Plain styled tail.", paragraph.content.plainText)
        assertEquals(setOf(Fb2TextStyle.Emphasis), paragraph.content.spans[1].styles)
        assertTrue(section.blocks[2] is Fb2Block.EmptyLine)
        assertEquals("pic.png", (section.blocks[3] as Fb2Block.Image).image.binaryId)
    }

    private val sampleXml = """
        <?xml version="1.0" encoding="UTF-8"?>
        <FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:xlink="http://www.w3.org/1999/xlink">
          <description>
            <title-info>
              <genre>sf</genre>
              <author><first-name>Ada</first-name><last-name>Lovelace</last-name></author>
              <book-title>The Test Book</book-title>
              <lang>en</lang>
            </title-info>
            <document-info>
              <author><nickname>Toread</nickname></author>
              <date>2026-05-12</date>
              <id>sample</id>
              <version>1.0</version>
            </document-info>
          </description>
          <body>
            <section>
              <title><p>Chapter 1</p></title>
              <p>Hello &amp; welcome.</p>
            </section>
          </body>
        </FictionBook>
    """.trimIndent()

    private val richXml = """
        <?xml version="1.0" encoding="UTF-8"?>
        <FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:xlink="http://www.w3.org/1999/xlink">
          <description>
            <title-info>
              <author><nickname>A</nickname></author>
              <book-title>Rich</book-title>
              <lang>en</lang>
            </title-info>
            <document-info>
              <author><nickname>Toread</nickname></author>
              <date>2026-05-12</date>
              <id>rich</id>
              <version>1.0</version>
            </document-info>
          </description>
          <body>
            <section>
              <subtitle>Part</subtitle>
              <p>Plain <emphasis>styled</emphasis> tail.</p>
              <empty-line/>
              <image xlink:href="#pic.png"/>
            </section>
          </body>
        </FictionBook>
    """.trimIndent()
}
