package eu.kanade.tachiyomi.extension.en.dilbert

import android.os.Build.VERSION
import eu.kanade.tachiyomi.extension.BuildConfig
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.OkHttpClient
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale

class Dilbert : ParsedHttpSource() {

    override val name = "Dilbert"

    override val baseUrl = "https://dilbert.com"

    override val lang = "en"

    override val supportsLatest = false

    override val client: OkHttpClient = network.client.newBuilder()
        .addNetworkInterceptor(RateLimitInterceptor(4)).build()

    private val userAgent = "Mozilla/5.0 " +
        "(Android ${VERSION.RELEASE}; Mobile) " +
        "Tachiyomi/${BuildConfig.VERSION_NAME}"

    private val dateFormat = SimpleDateFormat("EEEE MMMM dd, yyyy", Locale.US)

    override fun headersBuilder() = Headers.Builder().apply {
        add("User-Agent", userAgent)
        add("Referer", baseUrl)
    }

    override fun fetchPopularManga(page: Int): Observable<MangasPage> {
        val currentYear = Calendar.getInstance().get(Calendar.YEAR)
        return Observable.just(
            MangasPage(
                (currentYear downTo 1989).map {
                    SManga.create().apply {
                        url = "?$it"
                        title = "$name ($it)"
                        artist = "Scott Adams"
                        author = "Scott Adams"
                        status = if (it < currentYear) SManga.COMPLETED else SManga.ONGOING
                        description =
                            """
                A satirical comic strip featuring Dilbert, a competent, but seldom recognized engineer.
                (This entry includes all the chapters published in $it.)
                            """.trimIndent()
                        thumbnail_url = "https://dilbert.com/assets/favicon/favicon-196x196-cf4d86b485e628a034ab8b961c1c3520b5969252400a80b9eed544d99403e037.png"
                    }
                },
                false
            )
        )
    }

    override fun fetchSearchManga(page: Int, query: String, filters: FilterList) = fetchPopularManga(page)

    override fun fetchMangaDetails(manga: SManga): Observable<SManga> =
        Observable.just(manga.apply { initialized = true })

    private fun chapterListRequest(manga: SManga, page: Int = 1) =
        GET("$baseUrl/search_results?sort=date_asc&year=${manga.year}&page=$page", headers)

    override fun chapterFromElement(element: Element) = SChapter.create().apply {
        val date = element.first(".comic-title-date").text()
        url = element.first(".img-comic-link").attr("href")
        name = element.first(".comic-title-name").text().ifBlank { date }
        date_upload = dateFormat.parse(date)?.time ?: 0L
    }

    override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
        val chapters = mutableListOf<SChapter>()
        fun getChapters(page: Int = 1): Document {
            val res = client.newCall(chapterListRequest(manga, page)).execute()
            if (!res.isSuccessful) {
                res.close()
                throw Exception("HTTP error ${res.code()}")
            }
            return res.asJsoup().also {
                chapters.addAll(it.select(".comic-item").map(::chapterFromElement))
            }
        }
        val pages = getChapters().first(".pagination > li:nth-last-child(2) > a")?.text()?.toIntOrNull()
        if (pages != null) for (page in 2..pages) getChapters(page)
        return Observable.just(
            chapters.sortedBy(SChapter::date_upload).mapIndexed {
                i, ch ->
                ch.apply { chapter_number = i + 1f }
            }.reversed()
        )
    }

    override fun fetchPageList(chapter: SChapter) =
        Observable.just(listOf(Page(0, chapter.url)))

    override fun imageUrlRequest(page: Page) = GET(page.url, headers)

    override fun imageUrlParse(document: Document) =
        "https:" + document.first(".img-comic").attr("src")

    private val SManga.year: Int
        get() = url.substringAfterLast('?').toInt()

    private fun Element.first(selector: String) = select(selector).first()

    override fun popularMangaSelector() = ""

    override fun popularMangaNextPageSelector() = ""

    override fun searchMangaSelector() = ""

    override fun searchMangaNextPageSelector() = ""

    override fun latestUpdatesSelector() = ""

    override fun latestUpdatesNextPageSelector() = ""

    override fun chapterListSelector() = ""

    override fun popularMangaRequest(page: Int) =
        throw UnsupportedOperationException("This method should not be called!")

    override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
        throw UnsupportedOperationException("This method should not be called!")

    override fun latestUpdatesRequest(page: Int) =
        throw UnsupportedOperationException("This method should not be called!")

    override fun chapterListRequest(manga: SManga) =
        throw UnsupportedOperationException("This method should not be called!")

    override fun mangaDetailsParse(document: Document) =
        throw UnsupportedOperationException("This method should not be called!")

    override fun pageListParse(document: Document) =
        throw UnsupportedOperationException("This method should not be called!")

    override fun popularMangaFromElement(element: Element) =
        throw UnsupportedOperationException("This method should not be called!")

    override fun searchMangaFromElement(element: Element) =
        throw UnsupportedOperationException("This method should not be called!")

    override fun latestUpdatesFromElement(element: Element) =
        throw UnsupportedOperationException("This method should not be called!")
}
