package com.aletheia.app.data

import java.io.File
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class BookParserServiceSmokeTest {

    @get:Rule
    val temporaryFolder = TemporaryFolder()

    @Test
    fun parseAndStoreBook_readsEpubMetadataAndStoresFile() {
        val sourceFile = temporaryFolder.newFile("source.epub")
        writeMinimalEpub(sourceFile)
        val service = newService()

        val book = service.parseAndStoreBook(sourceFile, "smoke.epub")

        assertEquals("Smoke EPUB", book.title)
        assertEquals("EPUB Author", book.author)
        assertEquals("epub", book.format)
        assertEquals("smoke.epub", book.fileName)
        assertTrue(File(book.filePath).exists())
        assertTrue(File(book.filePath).parentFile?.name == "Books")
    }

    @Test
    fun parseAndStoreBook_readsFb2MetadataAndCoverAndStoresFile() {
        val sourceFile = temporaryFolder.newFile("source.fb2")
        sourceFile.writeText(MINIMAL_FB2.trimIndent())
        val service = newService()

        val book = service.parseAndStoreBook(sourceFile, "smoke.fb2")

        assertEquals("Smoke FB2", book.title)
        assertEquals("FB2 Test Author", book.author)
        assertEquals("fb2", book.format)
        assertEquals("smoke.fb2", book.fileName)
        assertTrue(File(book.filePath).exists())
        assertNotNull(book.coverImage)
    }

    private fun newService(): BookParserService =
        BookParserService(
            booksDirectory = temporaryFolder.newFolder("Books"),
            cacheDirectory = temporaryFolder.newFolder("Cache"),
            base64Decoder = { value -> java.util.Base64.getMimeDecoder().decode(value) }
        )

    private fun writeMinimalEpub(file: File) {
        ZipOutputStream(file.outputStream()).use { zip ->
            zip.putTextEntry(
                "META-INF/container.xml",
                """
                <?xml version="1.0" encoding="UTF-8"?>
                <container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" version="1.0">
                    <rootfiles>
                        <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml" />
                    </rootfiles>
                </container>
                """.trimIndent()
            )
            zip.putTextEntry(
                "OEBPS/content.opf",
                """
                <?xml version="1.0" encoding="UTF-8"?>
                <package xmlns="http://www.idpf.org/2007/opf" version="3.0">
                    <metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
                        <dc:title>Smoke EPUB</dc:title>
                        <dc:creator>EPUB Author</dc:creator>
                    </metadata>
                    <manifest>
                        <item id="chapter" href="chapter.xhtml" media-type="application/xhtml+xml" />
                    </manifest>
                    <spine>
                        <itemref idref="chapter" />
                    </spine>
                </package>
                """.trimIndent()
            )
        }
    }

    private fun ZipOutputStream.putTextEntry(name: String, value: String) {
        putNextEntry(ZipEntry(name))
        write(value.toByteArray(Charsets.UTF_8))
        closeEntry()
    }

    private companion object {
        private const val MINIMAL_FB2 = """
            <?xml version="1.0" encoding="UTF-8"?>
            <FictionBook xmlns:l="http://www.w3.org/1999/xlink">
                <description>
                    <title-info>
                        <author>
                            <first-name>FB2</first-name>
                            <middle-name>Test</middle-name>
                            <last-name>Author</last-name>
                        </author>
                        <book-title>Smoke FB2</book-title>
                        <coverpage>
                            <image l:href="#cover.jpg" />
                        </coverpage>
                    </title-info>
                </description>
                <body>
                    <section>
                        <p>Smoke body</p>
                    </section>
                </body>
                <binary id="cover.jpg" content-type="image/jpeg">AQIDBA==</binary>
            </FictionBook>
        """
    }
}
