package net.sergeych.toread.fb2

data class Fb2Book(
    val title: String,
    val authors: List<Fb2Author> = emptyList(),
    val language: String? = null,
    val sourceLanguage: String? = null,
    val genres: List<String> = emptyList(),
    val annotation: String? = null,
    val keywords: String? = null,
    val date: String? = null,
    val translators: List<Fb2Author> = emptyList(),
    val sequences: List<Fb2Sequence> = emptyList(),
    val coverImages: List<Fb2ImageRef> = emptyList(),
    val documentInfo: Fb2DocumentInfo = Fb2DocumentInfo(),
    val bodyTitle: List<String> = emptyList(),
    val bodyImages: List<Fb2ImageRef> = emptyList(),
    val bodyEpigraphs: List<Fb2Epigraph> = emptyList(),
    val sections: List<Fb2Section> = emptyList(),
    val notes: Map<String, Fb2Section> = emptyMap(),
    val binaries: List<Fb2Binary> = emptyList(),
) {
    fun binaryFor(image: Fb2ImageRef): Fb2Binary? =
        binaries.firstOrNull { it.id == image.binaryId }
}

data class Fb2Author(
    val firstName: String? = null,
    val middleName: String? = null,
    val lastName: String? = null,
    val nickname: String? = null,
) {
    val displayName: String
        get() = listOfNotNull(firstName, middleName, lastName).joinToString(" ")
            .ifBlank { nickname.orEmpty() }
}

data class Fb2Sequence(
    val name: String,
    val number: String? = null,
)

data class Fb2DocumentInfo(
    val authors: List<Fb2Author> = emptyList(),
    val date: String? = null,
    val id: String? = null,
    val version: String? = null,
)

data class Fb2Section(
    val id: String? = null,
    val title: String? = null,
    val paragraphs: List<String> = emptyList(),
    val images: List<Fb2ImageRef> = emptyList(),
    val sections: List<Fb2Section> = emptyList(),
    val blocks: List<Fb2Block> = emptyList(),
)

data class Fb2ImageRef(
    val href: String,
    val alt: String? = null,
) {
    val binaryId: String = href.removePrefix("#")
}

data class Fb2Binary(
    val id: String,
    val contentType: String,
    val base64: String,
)

sealed interface Fb2Block {
    data class Paragraph(val content: Fb2Text) : Fb2Block
    data class Subtitle(val content: Fb2Text) : Fb2Block
    data class Epigraph(val epigraph: Fb2Epigraph) : Fb2Block
    data class Cite(val cite: Fb2Cite) : Fb2Block
    data class Poem(val poem: Fb2Poem) : Fb2Block
    data class Image(val image: Fb2ImageRef) : Fb2Block
    data object EmptyLine : Fb2Block
}

data class Fb2Epigraph(
    val blocks: List<Fb2EpigraphBlock> = emptyList(),
    val textAuthors: List<Fb2Text> = emptyList(),
)

data class Fb2Cite(
    val blocks: List<Fb2EpigraphBlock> = emptyList(),
    val textAuthors: List<Fb2Text> = emptyList(),
)

sealed interface Fb2EpigraphBlock {
    data class Paragraph(val content: Fb2Text) : Fb2EpigraphBlock
    data class Subtitle(val content: Fb2Text) : Fb2EpigraphBlock
    data class Poem(val poem: Fb2Poem) : Fb2EpigraphBlock
    data class Cite(val cite: Fb2Cite) : Fb2EpigraphBlock
    data object EmptyLine : Fb2EpigraphBlock
}

data class Fb2Poem(
    val title: List<Fb2Text> = emptyList(),
    val epigraphs: List<Fb2Epigraph> = emptyList(),
    val blocks: List<Fb2PoemBlock> = emptyList(),
    val textAuthors: List<Fb2Text> = emptyList(),
    val date: String? = null,
)

sealed interface Fb2PoemBlock {
    data class Subtitle(val content: Fb2Text) : Fb2PoemBlock
    data class Stanza(val stanza: Fb2Stanza) : Fb2PoemBlock
}

data class Fb2Stanza(
    val title: List<Fb2Text> = emptyList(),
    val subtitle: Fb2Text? = null,
    val verses: List<Fb2Text> = emptyList(),
)

data class Fb2Text(
    val spans: List<Fb2TextSpan>,
) {
    val plainText: String
        get() = spans.joinToString(separator = "") { it.text }.trim()
}

data class Fb2TextSpan(
    val text: String,
    val styles: Set<Fb2TextStyle> = emptySet(),
    val href: String? = null,
    val linkType: String? = null,
)

enum class Fb2TextStyle {
    Emphasis,
    Strong,
    Code,
    Strikethrough,
    Superscript,
    Subscript,
}

class Fb2ParseException(message: String) : IllegalArgumentException(message)
