package net.sergeych.toread.storage.jdbc

import java.io.File
import java.nio.file.Files
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

class LibraryScannerTest {
    @Test
    fun scansFb2ZipSubtreeIntoLinkedLibraryRecords() {
        val root = findProjectRoot()
        val db = H2LibraryDatabase.openMemory("scansFb2ZipSubtreeIntoLinkedLibraryRecords")
        try {
            val summary = LibraryScanner(db).scanSubtree(File(root, "test_books"))
            val expectedFiles = LibraryScanner.countSupportedBookFiles(File(root, "test_books"))

            assertEquals(expectedFiles, summary.scannedFiles)
            assertEquals(expectedFiles, summary.importedFiles)
            assertEquals(0, summary.skippedFiles)
            assertEquals(0, summary.failedFiles)

            val file = db.files.list().first { it.storageUri?.endsWith(".fb2.zip") == true }
            assertEquals("fb2.zip", file.format)
            assertTrue(file.storageUri?.endsWith(".fb2.zip") == true)
            val book = assertNotNull(file.bookId?.let(db.books::get))
            val coverImage = assertNotNull(book.coverImage)
            assertTrue(coverImage.isNotEmpty())
            assertNotNull(file.bodyId?.let(db.bodies::get))

            val second = LibraryScanner(db).scanSubtree(File(root, "test_books"))
            assertEquals(0, second.importedFiles)
            assertEquals(expectedFiles, second.skippedFiles)
        } finally {
            db.close()
        }
    }

    @Test
    fun scanMarksImportedDuplicateFilesImmediately() {
        val root = findProjectRoot()
        val source = File(root, "test_books/Maraini_Zapiski-Terezy-Numy.G7vc8A.872381.fb2.zip")
        val tempDir = Files.createTempDirectory("yereed-dupe-scan").toFile()
        try {
            source.copyTo(File(tempDir, source.name))
            source.copyTo(File(tempDir, "same-book-different-name.fb2.zip"))

            val db = H2LibraryDatabase.openMemory("scanMarksImportedDuplicateFilesImmediately")
            try {
                val summary = LibraryScanner(db).scanSubtree(tempDir)

                assertEquals(2, summary.scannedFiles)
                assertEquals(2, summary.importedFiles)
                assertEquals(0, summary.skippedFiles)

                val files = db.files.list().sortedBy { it.duplicateOfFileId != null }
                assertEquals(2, files.size)
                assertNull(files.first().duplicateOfFileId)
                assertEquals(files.first().id, files.last().duplicateOfFileId)
                assertEquals(1, db.files.listLibraryFiles().size)

                val second = LibraryScanner(db).scanSubtree(tempDir)
                assertEquals(0, second.importedFiles)
                assertEquals(2, second.skippedFiles)
            } finally {
                db.close()
            }
        } finally {
            tempDir.deleteRecursively()
        }
    }

    @Test
    fun listingLibraryFilesPersistsLegacyDuplicateMarkers() {
        val root = findProjectRoot()
        val bytes = File(root, "test_books/Maraini_Zapiski-Terezy-Numy.G7vc8A.872381.fb2.zip").readBytes()
        val db = H2LibraryDatabase.openMemory("listingLibraryFilesPersistsLegacyDuplicateMarkers")
        try {
            val scanner = LibraryScanner(db)
            assertTrue(scanner.importExternalFile(bytes, "primary.fb2.zip", "/tmp/primary.fb2.zip", bytes.size.toLong(), 1L))
            assertTrue(scanner.importExternalFile(bytes, "secondary.fb2.zip", "/tmp/secondary.fb2.zip", bytes.size.toLong(), 2L))

            val duplicate = db.files.list().single { it.duplicateOfFileId != null }
            db.files.upsert(duplicate.copy(duplicateOfFileId = null))

            assertEquals(2, db.files.list().count { it.duplicateOfFileId == null })
            assertEquals(1, db.files.listLibraryFiles().size)
            assertEquals(1, db.files.list().count { it.duplicateOfFileId != null })
        } finally {
            db.close()
        }
    }

    @Test
    fun importExternalFileResultReturnsExistingFileForExactRepeat() {
        val root = findProjectRoot()
        val bytes = File(root, "test_books/Maraini_Zapiski-Terezy-Numy.G7vc8A.872381.fb2.zip").readBytes()
        val db = H2LibraryDatabase.openMemory("importExternalFileResultReturnsExistingFileForExactRepeat")
        try {
            val scanner = LibraryScanner(db)
            val first = scanner.importExternalFileAndGetResult(
                bytes = bytes,
                displayName = "opened.fb2.zip",
                storageUri = "/tmp/opened.fb2.zip",
                sizeBytes = bytes.size.toLong(),
                lastModifiedMillis = 1L,
            )
            val second = scanner.importExternalFileAndGetResult(
                bytes = bytes,
                displayName = "opened.fb2.zip",
                storageUri = "/tmp/opened.fb2.zip",
                sizeBytes = bytes.size.toLong(),
                lastModifiedMillis = 1L,
            )

            assertTrue(first.imported)
            assertEquals(false, second.imported)
            assertEquals(first.fileId, second.fileId)
        } finally {
            db.close()
        }
    }

    private fun findProjectRoot(): File {
        var current = File(System.getProperty("user.dir")).absoluteFile
        while (true) {
            if (File(current, "test_books").isDirectory) return current
            current = current.parentFile ?: break
        }
        error("Could not locate project root")
    }
}
