package com.aletheia.app.ui.qbooks

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import com.aletheia.app.AletheiaApplication
import com.aletheia.app.R
import com.aletheia.app.data.Result
import com.aletheia.app.databinding.ActivityBookDetailBinding
import com.aletheia.app.model.Book
import com.aletheia.app.model.QBooksBook
import com.aletheia.app.ui.reader.ReaderActivity
import com.aletheia.app.util.BookSharing
import com.aletheia.app.util.setBookCover
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.File
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class BookDetailActivity : AppCompatActivity() {
    private lateinit var binding: ActivityBookDetailBinding
    private val app by lazy { application as AletheiaApplication }
    private lateinit var book: QBooksBook
    private var localBook: Book? = null
    private var isDownloading = false

    override fun onCreate(state: Bundle?) {
        super.onCreate(state)
        binding = ActivityBookDetailBinding.inflate(layoutInflater)
        setContentView(binding.root)
        WindowCompat.getInsetsController(window, binding.root).isAppearanceLightStatusBars = true
        WindowCompat.getInsetsController(window, binding.root).isAppearanceLightNavigationBars = true
        setupSystemBarInsets()

        book = readBook() ?: run {
            finish()
            return
        }
        binding.backButton.setOnClickListener { finish() }
        binding.shareButton.setOnClickListener { shareBook() }
        binding.favoriteButton.setOnClickListener { toggleFavorite() }
        renderBook()
        loadState()
        loadMetadataAndCover()
    }

    override fun onResume() {
        super.onResume()
        if (::book.isInitialized) loadState()
    }

    private fun renderBook() {
        binding.bookCover.setBookCover(book.coverImage, R.drawable.default_cover)
        binding.bookBackdrop.setBookCover(book.coverImage, R.drawable.default_cover)
        binding.bookTitle.text = book.title
        binding.bookAuthor.text = book.author
        binding.bookFormat.text = book.format.uppercase()
        binding.bookDescription.text = book.description?.takeIf(String::isNotBlank)
            ?: getString(R.string.book_detail_no_description)

        val details = buildList {
            book.language?.takeIf(String::isNotBlank)?.let { add(getString(R.string.book_detail_language, it)) }
            book.published?.takeIf(String::isNotBlank)?.let { add(getString(R.string.book_detail_published, it.take(4))) }
            book.publisher?.takeIf(String::isNotBlank)?.let { add(getString(R.string.book_detail_publisher, it)) }
        }
        binding.bookInfo.text = details.joinToString("  •  ")
        binding.bookInfo.visibility = if (details.isEmpty()) View.GONE else View.VISIBLE
        renderFavorite()
    }

    private fun loadMetadataAndCover() {
        lifecycleScope.launch {
            book = app.qBooksService.enrichBookMetadata(book, includeDetails = true)
            renderBook()
            if (book.coverImage != null || book.coverUrl.isNullOrBlank()) return@launch
            val cover = app.qBooksService.fetchCoverImage(book)
            if (cover != null) {
                book = book.copy(coverImage = cover)
                binding.bookCover.setBookCover(cover, R.drawable.default_cover)
                binding.bookBackdrop.setBookCover(cover, R.drawable.default_cover)
            }
        }
    }

    private fun setupSystemBarInsets() {
        val backTopMargin = (binding.backButton.layoutParams as FrameLayout.LayoutParams).topMargin
        val shareTopMargin = (binding.shareButton.layoutParams as FrameLayout.LayoutParams).topMargin
        val favoriteTopMargin = (binding.favoriteButton.layoutParams as FrameLayout.LayoutParams).topMargin
        val actionBottomPadding = binding.bottomActionBar.paddingBottom
        val scrollBottomPadding = binding.bookScroll.paddingBottom
        ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
            val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            binding.backButton.updateLayoutParams<FrameLayout.LayoutParams> {
                topMargin = backTopMargin + bars.top
            }
            binding.shareButton.updateLayoutParams<FrameLayout.LayoutParams> {
                topMargin = shareTopMargin + bars.top
            }
            binding.favoriteButton.updateLayoutParams<FrameLayout.LayoutParams> {
                topMargin = favoriteTopMargin + bars.top
            }
            binding.bottomActionBar.updatePadding(bottom = actionBottomPadding + bars.bottom)
            binding.bookScroll.updatePadding(bottom = scrollBottomPadding + bars.bottom)
            insets
        }
        ViewCompat.requestApplyInsets(binding.root)
    }

    private fun loadState() {
        lifecycleScope.launch {
            localBook = withContext(Dispatchers.IO) { app.bookRepository.getBookByRemoteId(book.id) }
            updatePrimaryAction()
        }
    }

    private fun updatePrimaryAction() {
        if (isDownloading) return
        val downloaded = localBook
        binding.downloadProgress.visibility = View.GONE
        binding.primaryAction.isEnabled = true
        binding.primaryAction.text = getString(
            if (downloaded == null) R.string.book_detail_download else R.string.book_detail_read
        )
        binding.downloadState.text = if (downloaded == null) "" else getString(R.string.book_detail_downloaded)
        binding.downloadState.visibility = if (downloaded == null) View.GONE else View.VISIBLE
        binding.primaryAction.setOnClickListener {
            if (downloaded == null) downloadBook() else openBook(downloaded)
        }
    }

    private fun downloadBook() {
        if (isDownloading) return
        isDownloading = true
        binding.primaryAction.isEnabled = false
        binding.downloadProgress.visibility = View.VISIBLE
        binding.downloadProgress.progress = 0
        binding.primaryAction.text = getString(R.string.book_detail_downloading, 0)

        lifecycleScope.launch {
            val result = withContext(Dispatchers.IO) {
                runCatching {
                    app.bookRepository.getBookByRemoteId(book.id)?.let { return@runCatching it }
                    val progress: (Int) -> Unit = { value ->
                        runOnUiThread {
                            binding.downloadProgress.progress = value
                            binding.primaryAction.text = getString(R.string.book_detail_downloading, value)
                        }
                    }
                    val temp = when (val download = app.qBooksService.downloadBook(
                        book,
                        app.cacheDir,
                        progress
                    )) {
                        is Result.Success -> download.value
                        is Result.Failure -> error(download.message)
                    }
                    try {
                        app.bookRepository.importDownloadedBook(
                            file = temp,
                            originalFileName = safeFileName(book),
                            remoteId = book.id,
                            coverImage = book.coverImage
                        )
                    } finally {
                        temp.takeIf(File::exists)?.delete()
                    }
                }
            }
            isDownloading = false
            result.onSuccess {
                localBook = it
                updatePrimaryAction()
            }.onFailure {
                updatePrimaryAction()
                MaterialAlertDialogBuilder(this@BookDetailActivity)
                    .setTitle(R.string.dialog_error_title)
                    .setMessage(getString(R.string.dialog_download_book_failed, it.message ?: it.javaClass.simpleName))
                    .setPositiveButton(R.string.action_ok, null)
                    .show()
            }
        }
    }

    private fun openBook(book: Book) {
        startActivity(Intent(this, ReaderActivity::class.java).putExtra(ReaderActivity.EXTRA_BOOK_ID, book.id))
    }

    private fun shareBook() {
        lifecycleScope.launch {
            val downloadedBook = withContext(Dispatchers.IO) {
                app.bookRepository.getBookByRemoteId(book.id)
            }
            runCatching {
                if (downloadedBook == null) {
                    BookSharing.shareBookLink(this@BookDetailActivity, book)
                } else {
                    BookSharing.shareDownloadedBook(this@BookDetailActivity, downloadedBook)
                }
            }.onFailure { exception ->
                MaterialAlertDialogBuilder(this@BookDetailActivity)
                    .setTitle(R.string.dialog_error_title)
                    .setMessage(getString(R.string.dialog_share_book_failed, exception.message ?: exception.javaClass.simpleName))
                    .setPositiveButton(R.string.action_ok, null)
                    .show()
            }
        }
    }

    private fun toggleFavorite() {
        val prefs = getSharedPreferences(FAVORITES_PREFS, MODE_PRIVATE)
        val favorites = prefs.getStringSet(FAVORITES_KEY, emptySet()).orEmpty().toMutableSet()
        if (!favorites.add(book.id)) favorites.remove(book.id)
        prefs.edit().putStringSet(FAVORITES_KEY, favorites).apply()
        renderFavorite()
    }

    private fun renderFavorite() {
        val selected = getSharedPreferences(FAVORITES_PREFS, MODE_PRIVATE)
            .getStringSet(FAVORITES_KEY, emptySet())
            .orEmpty()
            .contains(book.id)
        binding.favoriteButton.text = if (selected) "♥" else "♡"
        binding.favoriteButton.contentDescription = getString(
            if (selected) R.string.book_detail_favorite_remove else R.string.book_detail_favorite_add
        )
    }

    @Suppress("DEPRECATION")
    private fun readBook(): QBooksBook? = intent.getSerializableExtra(EXTRA_BOOK) as? QBooksBook

    private fun safeFileName(book: QBooksBook): String {
        val title = book.title.replace(Regex("[\\\\/:*?\"<>|]"), "_")
        return "$title.${book.format.lowercase()}"
    }

    companion object {
        private const val EXTRA_BOOK = "catalog_book"
        private const val FAVORITES_PREFS = "catalog_favorites"
        private const val FAVORITES_KEY = "book_ids"

        fun createIntent(context: Context, book: QBooksBook): Intent =
            Intent(context, BookDetailActivity::class.java)
                .putExtra(EXTRA_BOOK, book.copy(coverImage = null))
    }
}
