package com.aletheia.app.ui.main

import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
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.fragment.app.Fragment
import androidx.fragment.app.commit
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import com.aletheia.app.AletheiaApplication
import com.aletheia.app.R
import com.aletheia.app.databinding.ActivityMainBinding
import com.aletheia.app.model.Book
import com.aletheia.app.ui.books.BookshelfFragment
import com.aletheia.app.ui.home.HomeFragment
import com.aletheia.app.ui.qbooks.QBooksLibraryFragment
import com.aletheia.app.ui.reader.ReaderActivity
import com.aletheia.app.ui.settings.SettingsFragment
import com.aletheia.app.util.setBookCover
import java.io.File
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.math.roundToInt

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private val app by lazy { application as AletheiaApplication }
    private var miniReaderBook: Book? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupSystemBars()

        binding.bottomNavigation.setOnItemSelectedListener { item ->
            openTab(item.itemId)
        }
        binding.bottomNavigation.setOnItemReselectedListener { item ->
            if (item.itemId == R.id.nav_my_books) {
                requestMyBooksRefresh()
            }
        }
        binding.miniReaderPanel.setOnClickListener { openMiniReader() }
        binding.miniReaderOpen.setOnClickListener { openMiniReader() }

        if (savedInstanceState == null) {
            selectOrOpenTab(resolveInitialTab(intent?.getStringExtra(EXTRA_OPEN_TAB)))
        } else if (
            supportFragmentManager.fragments.none { !it.isHidden } ||
            supportFragmentManager.findFragmentByTag(LEGACY_TAG_READER)?.isHidden == false
        ) {
            selectOrOpenTab(R.id.nav_home)
        }
    }

    override fun onResume() {
        super.onResume()
        refreshMiniReader()
    }

    override fun onNewIntent(intent: android.content.Intent) {
        super.onNewIntent(intent)
        setIntent(intent)
        intent.getStringExtra(EXTRA_OPEN_TAB)?.let { tab ->
            selectOrOpenTab(resolveInitialTab(tab))
        }
    }

    private fun selectOrOpenTab(itemId: Int) {
        if (binding.bottomNavigation.selectedItemId == itemId) {
            openTab(itemId)
        } else {
            binding.bottomNavigation.selectedItemId = itemId
        }
    }

    private fun openTab(itemId: Int): Boolean = when (itemId) {
        R.id.nav_home -> showTab(TAG_HOME) { HomeFragment.newInstance() }
        R.id.nav_search -> showTab(TAG_SEARCH) { QBooksLibraryFragment.newInstance() }
        R.id.nav_reader -> {
            openLastReadBook()
            false
        }
        R.id.nav_my_books -> showTab(TAG_MY_BOOKS) { BookshelfFragment.newInstance() }
        R.id.nav_profile -> showTab(TAG_PROFILE) { SettingsFragment.newInstance() }
        else -> false
    }

    private fun showTab(tag: String, factory: () -> Fragment): Boolean {
        val current = supportFragmentManager.fragments.firstOrNull { !it.isHidden }
        val target = supportFragmentManager.findFragmentByTag(tag) ?: factory()
        if (current === target) {
            if (tag == TAG_MY_BOOKS) requestMyBooksRefresh()
            return true
        }

        supportFragmentManager.commit {
            setReorderingAllowed(true)
            current?.let { fragment ->
                hide(fragment)
                setMaxLifecycle(fragment, Lifecycle.State.STARTED)
            }
            if (target.isAdded) {
                show(target)
            } else {
                add(R.id.main_fragment_container, target, tag)
            }
            setMaxLifecycle(target, Lifecycle.State.RESUMED)
        }
        if (tag == TAG_MY_BOOKS) requestMyBooksRefresh()
        return true
    }

    private fun requestMyBooksRefresh() {
        supportFragmentManager.setFragmentResult(
            BookshelfFragment.REQUEST_REFRESH_LIBRARY,
            Bundle.EMPTY
        )
    }

    fun selectTab(itemId: Int) {
        binding.bottomNavigation.selectedItemId = itemId
    }

    private fun refreshMiniReader() {
        lifecycleScope.launch {
            val book = findLastReadableBook()
            miniReaderBook = book
            val visibility = if (book == null) View.GONE else View.VISIBLE
            binding.miniReaderPanel.visibility = visibility
            binding.miniReaderProgress.visibility = visibility
            if (book == null) return@launch

            binding.miniReaderCover.setBookCover(book.coverImage, R.drawable.default_cover)
            binding.miniReaderTitle.text = book.title
            binding.miniReaderAuthor.text = book.author
            binding.miniReaderProgress.progress =
                (book.readingProgress * 100).roundToInt().coerceIn(0, 100)
            binding.miniReaderPanel.contentDescription = getString(R.string.home_open_book, book.title)
        }
    }

    private fun openMiniReader() {
        val book = miniReaderBook ?: return
        openReader(book)
    }

    private fun openLastReadBook() {
        if (supportFragmentManager.fragments.none { fragment -> !fragment.isHidden }) {
            showTab(TAG_HOME) { HomeFragment.newInstance() }
        }
        lifecycleScope.launch {
            val book = findLastReadableBook()
            if (book == null) {
                Toast.makeText(
                    this@MainActivity,
                    R.string.reader_no_book_selected,
                    Toast.LENGTH_SHORT
                ).show()
                return@launch
            }
            miniReaderBook = book
            openReader(book)
        }
    }

    private suspend fun findLastReadableBook(): Book? = withContext(Dispatchers.IO) {
        app.bookRepository.getAllBooks()
            .asSequence()
            .filter { book -> File(book.filePath).isFile }
            .maxByOrNull { book -> book.lastRead }
    }

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

    private fun setupSystemBars() {
        WindowCompat.setDecorFitsSystemWindows(window, false)
        window.statusBarColor = Color.TRANSPARENT
        window.navigationBarColor = Color.TRANSPARENT
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            window.isNavigationBarContrastEnforced = false
        }
        WindowCompat.getInsetsController(window, binding.root).isAppearanceLightStatusBars = true
        WindowCompat.getInsetsController(window, binding.root).isAppearanceLightNavigationBars = true

        val baseContainerTop = binding.mainFragmentContainer.paddingTop
        val baseNavigationLeft = binding.bottomNavigation.paddingLeft
        val baseNavigationTop = binding.bottomNavigation.paddingTop
        val baseNavigationRight = binding.bottomNavigation.paddingRight
        val baseNavigationBottom = binding.bottomNavigation.paddingBottom
        val baseNavigationBottomMargin =
            (binding.bottomNavigation.layoutParams as ConstraintLayout.LayoutParams).bottomMargin

        ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
            val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            binding.mainFragmentContainer.updatePadding(top = baseContainerTop + bars.top)
            binding.bottomNavigation.updatePadding(
                left = baseNavigationLeft,
                top = baseNavigationTop,
                right = baseNavigationRight,
                bottom = baseNavigationBottom + bars.bottom
            )
            binding.bottomNavigation.updateLayoutParams<ConstraintLayout.LayoutParams> {
                bottomMargin = baseNavigationBottomMargin
            }
            insets
        }
        ViewCompat.requestApplyInsets(binding.root)
    }

    private fun resolveInitialTab(tab: String?): Int = when (tab?.trim()?.lowercase()) {
        "search", "catalog", "calibre", "qbooks" -> R.id.nav_search
        "reader" -> R.id.nav_reader
        "library", "books", "my-books" -> R.id.nav_my_books
        "settings", "profile" -> R.id.nav_profile
        else -> R.id.nav_home
    }

    companion object {
        const val EXTRA_OPEN_TAB = "open_tab"
        private const val TAG_HOME = "home"
        private const val TAG_SEARCH = "search"
        private const val LEGACY_TAG_READER = "reader"
        private const val TAG_MY_BOOKS = "my-books"
        private const val TAG_PROFILE = "profile"
    }
}
