package com.pixelized.biblib.network.factory

import com.pixelized.biblib.model.book.Author
import com.pixelized.biblib.model.book.Book
import com.pixelized.biblib.model.book.Genre
import com.pixelized.biblib.model.book.Language
import com.pixelized.biblib.model.book.Series
import com.pixelized.biblib.network.data.response.BookDetailResponse
import com.pixelized.biblib.network.data.response.BookListResponse
import com.pixelized.biblib.utils.exception.missingField
import com.pixelized.biblib.utils.extention.toBoolean
import java.text.DateFormat

class BookFactory(
    private val parser: DateFormat,
) {
    fun fromListResponseToBook(
        response: BookListResponse.Book,
        seriesHash: SeriesHash,
        genreHash: Map<Int, List<Genre>>,
        isNew: Boolean = false,
        newOrder: Int? = null
    ): Book {
        fun error(name: String) = missingField("#fromListResponseToBook()", name, response)

        val id: Int? = response.book_id
        val title: String? = response.book_title
        val sort: String? = response.book_sort
        val authorId: List<Int>? = response.author_id?.mapNotNull { it.toIntOrNull() }
        val authorName: List<String>? = response.author_name
        val authorSort: List<String>? = response.author_sort
        val authorIdSize = authorId?.size ?: 0
        val authorNameSize = authorName?.size ?: 0
        val authorSortSize = authorSort?.size ?: 0
        val author: List<Author>? =
            if (authorId != null && authorName != null && authorSort != null) {
                if (authorIdSize == authorNameSize && authorNameSize == authorSortSize) {
                    (authorId.indices).map { index ->
                        Author(
                            authorId[index],
                            authorName[index],
                            authorSort[index]
                        )
                    }
                } else if (authorIdSize == 1) {
                    listOf(
                        Author(
                            authorId.first(),
                            authorName.joinToString { it },
                            authorSort.joinToString { it })
                    )
                } else if (authorIdSize == authorSortSize) {
                    val contactedAuthorNames = authorSort.map { sortItem ->
                        authorName.filter { name -> sortItem.contains(name) }
                    }
                    (authorId.indices).map { index ->
                        Author(
                            authorId[index],
                            contactedAuthorNames[index].joinToString { it },
                            authorSort[index]
                        )
                    }
                } else {
                    null
                }
            } else {
                null
            }
        val cover: Boolean? = response.book_has_cover?.toBoolean()
        val releaseDate = response.book_date?.let { parser.parse(it) }
        val languageId = response.lang_id?.toIntOrNull()
        val languageCode = response.lang_code
        val language = if (languageId != null && languageCode != null) {
            Language(languageId, languageCode)
        } else {
            null
        }
        val rating = response.rating?.toIntOrNull()

        return Book(
            id = id ?: throw error("id"),
            title = title ?: throw error("title"),
            sort = sort ?: throw error("sort"),
            author = author ?: throw error("author"),
            haveCover = cover ?: throw error("haveCover"),
            releaseDate = releaseDate ?: throw error("releaseDate"),
            series = seriesHash[id]?.second,
            seriesIndex = seriesHash[id]?.first,
            language = language,
            rating = rating,
            genre = genreHash[id],
            isNew = isNew,
            newOrder = if (isNew) newOrder else null,
        )
    }

    fun fromDetailResponseToBook(response: BookDetailResponse, isNew: Boolean = false): Book {
        fun error(name: String) = missingField("#fromDetailResponseToBook()", name, response)

        val book = response.book ?: throw error("book")

        val id: Int? = book.book_id
        val title: String? = book.book_title
        val sort: String? = book.book_sort
        val authorId: List<Int>? = book.author_id?.mapNotNull { it.toIntOrNull() }
        val authorName: List<String>? = book.author_name
        val authorSort: List<String>? = book.author_sort
        val authorIdSize = authorId?.size
        val authorNameSize = authorName?.size
        val authorSortSize = authorSort?.size
        val author: List<Author>? =
            if (authorId != null && authorName != null && authorSort != null && authorIdSize == authorNameSize && authorNameSize == authorSortSize) {
                authorId.indices.map { index ->
                    Author(
                        authorId[index],
                        authorName[index],
                        authorSort[index]
                    )
                }
            } else {
                null
            }
        val cover: Boolean? = book.book_has_cover?.toBoolean()
        val releaseDate = book.book_date?.let { parser.parse(it) }
        val languageId = book.lang_id?.toIntOrNull()
        val languageCode = book.lang_code
        val language = if (languageId != null && languageCode != null) {
            Language(languageId, languageCode)
        } else {
            null
        }
        val rating = book.rating?.toIntOrNull()
        val seriesId = book.series_id?.toIntOrNull()
        val seriesName = book.series_name
        val seriesSort = book.series_sort
        val seriesIndex: Int? = book.book_series_index?.toInt()
        val series: Series? = if (seriesId != null && seriesName != null && seriesSort != null) {
            Series(id = seriesId, name = seriesName, sort = seriesSort)
        } else {
            null
        }
        val tagId: List<Int>? = book.tag_id?.mapNotNull { it.toIntOrNull() }
        val tagName: List<String>? = book.tag_name
        val tagIdSize: Int? = book.tag_id?.size
        val tagNameSize: Int? = book.tag_name?.size
        val tag: List<Genre>? = if (tagId != null && tagName != null && tagIdSize == tagNameSize) {
            tagId.indices.map { Genre(tagId[it], tagName[it]) }
        } else {
            null
        }
        val synopsis: String? = book.comment

        return Book(
            id = id ?: throw error("id"),
            title = title ?: throw error("title"),
            sort = sort ?: throw error("sort"),
            author = author ?: throw error("author"),
            haveCover = cover ?: throw error("cover"),
            releaseDate = releaseDate ?: throw error("releaseDate"),
            language = language ?: throw error("language"),
            rating = rating,
            series = series,
            seriesIndex = seriesIndex,
            genre = tag,
            synopsis = synopsis,
            isNew = isNew
        )
    }
}