package bou.amine.apps.readerforselfossv2.android

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.core.view.doOnNextLayout
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import bou.amine.apps.readerforselfossv2.android.adapters.ItemCardAdapter
import bou.amine.apps.readerforselfossv2.android.adapters.ItemListAdapter
import bou.amine.apps.readerforselfossv2.android.adapters.ItemsAdapter
import bou.amine.apps.readerforselfossv2.android.background.LoadingWorker
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityHomeBinding
import bou.amine.apps.readerforselfossv2.android.fragments.FilterSheetFragment
import bou.amine.apps.readerforselfossv2.android.settings.SettingsActivity
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge
import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.ItemType
import com.ashokvarma.bottomnavigation.BottomNavigationBar
import com.ashokvarma.bottomnavigation.BottomNavigationItem
import com.ashokvarma.bottomnavigation.TextBadgeItem
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.kodein.di.DIAware
import org.kodein.di.android.closestDI
import org.kodein.di.instance
import java.security.MessageDigest
import java.util.concurrent.TimeUnit

class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAware {
    private var items: ArrayList<SelfossModel.Item> = ArrayList()

    private var elementsShown: ItemType = ItemType.UNREAD
    private var lastFetchDone: Boolean = false

    private lateinit var tabNewBadge: TextBadgeItem
    private lateinit var tabArchiveBadge: TextBadgeItem
    private lateinit var tabStarredBadge: TextBadgeItem
    private var offset: Int = 0
    private var firstVisible: Int = 0
    private lateinit var recyclerViewScrollListener: RecyclerView.OnScrollListener
    private lateinit var binding: ActivityHomeBinding

    private var recyclerAdapter: ItemsAdapter<out RecyclerView.ViewHolder>? = null

    private var fromTabShortcut: Boolean = false

    private val settingsLauncher =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            appSettingsService.refreshUserSettings()
        }

    override val di by closestDI()
    private val repository: Repository by instance()
    private val appSettingsService: AppSettingsService by instance()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityHomeBinding.inflate(layoutInflater)
        val view = binding.root

        fromTabShortcut = intent.getIntExtra("shortcutTab", -1) != -1
        repository.offlineOverride = intent.getBooleanExtra("startOffline", false)

        if (fromTabShortcut) {
            elementsShown = ItemType.fromInt(intent.getIntExtra("shortcutTab", ItemType.UNREAD.position))
        }

        setContentView(view)

        setSupportActionBar(binding.toolBar)

        handleBottomBar()

        handleSwipeRefreshLayout()

        if (appSettingsService.isItemCachingEnabled()) {
            CoroutineScope(Dispatchers.Main).launch {
                repository.tryToCacheItemsAndGetNewOnes()
            }
        }
    }

    private fun handleSwipeRefreshLayout() {
        binding.swipeRefreshLayout.setColorSchemeResources(
            R.color.refresh_progress_1,
            R.color.refresh_progress_2,
            R.color.refresh_progress_3,
        )
        binding.swipeRefreshLayout.setOnRefreshListener {
            repository.offlineOverride = false
            lastFetchDone = false
            CoroutineScope(Dispatchers.Main).launch {
                getElementsAccordingToTab()
                binding.swipeRefreshLayout.isRefreshing = false
            }
        }

        val swipeDirs =
            if (appSettingsService.getPublicAccess()) {
                0
            } else {
                ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
            }

        val simpleItemTouchCallback =
            object : ItemTouchHelper.SimpleCallback(
                0,
                swipeDirs,
            ) {
                override fun getSwipeDirs(
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder,
                ): Int =
                    if (elementsShown == ItemType.STARRED) {
                        0
                    } else {
                        super.getSwipeDirs(
                            recyclerView,
                            viewHolder,
                        )
                    }

                override fun onMove(
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder,
                    target: RecyclerView.ViewHolder,
                ): Boolean = false

                override fun onSwiped(
                    viewHolder: RecyclerView.ViewHolder,
                    swipeDir: Int,
                ) {
                    val position = viewHolder.bindingAdapterPosition
                    val i = items.elementAtOrNull(position)

                    if (i != null) {
                        val adapter = binding.recyclerView.adapter as ItemsAdapter<*>

                        adapter.handleItemAtIndex(position)

                        // Just load everythin
                        if (items.size <= 0) {
                            getElementsAccordingToTab()
                        }
                    } else {
                        Toast.makeText(
                            this@HomeActivity,
                            "Found null when swiping at positon $position.",
                            Toast.LENGTH_LONG,
                        ).show()
                    }
                }
            }

        ItemTouchHelper(simpleItemTouchCallback).attachToRecyclerView(binding.recyclerView)
    }

    private fun updateBottomBarBadgeCount(
        badge: TextBadgeItem,
        count: Int,
    ) {
        if (count > 0) {
            badge
                .setText(count.toString())
                .maybeShow()
        } else {
            badge.removeBadge()
        }
    }

    private fun handleBottomBar() {
        tabNewBadge =
            TextBadgeItem()
                .setText("")
                .setHideOnSelect(false).hide(false)
        tabArchiveBadge =
            TextBadgeItem()
                .setText("")
                .setHideOnSelect(false).hide(false)
        tabStarredBadge =
            TextBadgeItem()
                .setText("")
                .setHideOnSelect(false).hide(false)

        if (appSettingsService.isDisplayUnreadCountEnabled()) {
            lifecycleScope.launch {
                repository.badgeUnread.collect {
                    updateBottomBarBadgeCount(tabNewBadge, it)
                }
            }
        }

        if (appSettingsService.isDisplayAllCountEnabled()) {
            lifecycleScope.launch {
                repository.badgeAll.collect {
                    updateBottomBarBadgeCount(tabArchiveBadge, it)
                }
            }

            lifecycleScope.launch {
                repository.badgeStarred.collect {
                    updateBottomBarBadgeCount(tabStarredBadge, it)
                }
            }
        }

        val tabNew =
            BottomNavigationItem(
                R.drawable.ic_tab_fiber_new_black_24dp,
                getString(R.string.tab_new),
            )
                .setBadgeItem(tabNewBadge)
        val tabArchive =
            BottomNavigationItem(
                R.drawable.ic_tab_archive_black_24dp,
                getString(R.string.tab_read),
            )
                .setBadgeItem(tabArchiveBadge)
        val tabStarred =
            BottomNavigationItem(
                R.drawable.ic_tab_favorite_black_24dp,
                getString(R.string.tab_favs),
            ).setActiveColorResource(R.color.pink)
                .setBadgeItem(tabStarredBadge)

        binding.bottomBar
            .addItem(tabNew)
            .addItem(tabArchive)
            .addItem(tabStarred)
            .setFirstSelectedPosition(0)
            .initialise()
        binding.bottomBar.setMode(BottomNavigationBar.MODE_SHIFTING)

        if (fromTabShortcut) {
            binding.bottomBar.selectTab(elementsShown.position - 1)
        }
    }

    override fun onResume() {
        super.onResume()

        reloadLayoutManager()

        if (appSettingsService.isInfiniteLoadingEnabled()) {
            handleInfiniteScroll()
        } else {
            binding.recyclerView.setHasFixedSize(true)
        }

        handleBottomBarActions()

        handleGDPRDialog(appSettingsService.settings.getBoolean("GDPR_shown", false))

        handleRecurringTask()

        CoroutineScope(Dispatchers.Main).launch {
            repository.handleDBActions()
        }

        getElementsAccordingToTab()
    }

    private fun handleGDPRDialog(GDPRShown: Boolean) {
        val messageDigest: MessageDigest = MessageDigest.getInstance("SHA-256")
        messageDigest.update(appSettingsService.getBaseUrl().toByteArray())
        if (!GDPRShown) {
            val alertDialog = AlertDialog.Builder(this).create()
            alertDialog.setTitle(getString(R.string.gdpr_dialog_title))
            alertDialog.setMessage(getString(R.string.gdpr_dialog_message))
            alertDialog.setButton(
                AlertDialog.BUTTON_NEUTRAL,
                "OK",
            ) { dialog, _ ->
                appSettingsService.settings.putBoolean("GDPR_shown", true)
                dialog.dismiss()
            }
            alertDialog.show()
        }
    }

    private fun reloadLayoutManager() {
        val currentManager = binding.recyclerView.layoutManager
        val layoutManager: RecyclerView.LayoutManager

        // This will only update the layout manager if settings changed
        when (currentManager) {
            is StaggeredGridLayoutManager ->
                if (!appSettingsService.isCardViewEnabled()) {
                    layoutManager =
                        GridLayoutManager(
                            this,
                            calculateNoOfColumns(),
                        )
                    binding.recyclerView.layoutManager = layoutManager
                }
            is GridLayoutManager ->
                if (appSettingsService.isCardViewEnabled()) {
                    layoutManager =
                        StaggeredGridLayoutManager(
                            calculateNoOfColumns(),
                            StaggeredGridLayoutManager.VERTICAL,
                        )
                    layoutManager.gapStrategy =
                        StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
                    binding.recyclerView.layoutManager = layoutManager
                }
            else ->
                if (currentManager == null) {
                    if (!appSettingsService.isCardViewEnabled()) {
                        layoutManager =
                            GridLayoutManager(
                                this,
                                calculateNoOfColumns(),
                            )
                        binding.recyclerView.layoutManager = layoutManager
                    } else {
                        layoutManager =
                            StaggeredGridLayoutManager(
                                calculateNoOfColumns(),
                                StaggeredGridLayoutManager.VERTICAL,
                            )
                        layoutManager.gapStrategy =
                            StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
                        binding.recyclerView.layoutManager = layoutManager
                    }
                }
        }
    }

    private fun handleBottomBarActions() {
        binding.bottomBar.setTabSelectedListener(
            object : BottomNavigationBar.OnTabSelectedListener {
                override fun onTabUnselected(position: Int) = Unit

                override fun onTabReselected(position: Int) {
                    when (val layoutManager = binding.recyclerView.adapter) {
                        is StaggeredGridLayoutManager ->
                            if (layoutManager.findFirstCompletelyVisibleItemPositions(null)[0] == 0) {
                                getElementsAccordingToTab()
                            } else {
                                layoutManager.scrollToPositionWithOffset(0, 0)
                            }
                        is GridLayoutManager ->
                            if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
                                getElementsAccordingToTab()
                            } else {
                                layoutManager.scrollToPositionWithOffset(0, 0)
                            }
                        else -> Unit
                    }
                }

                override fun onTabSelected(position: Int) {
                    offset = 0
                    lastFetchDone = false

                    elementsShown = ItemType.fromInt(position + 1)
                    getElementsAccordingToTab()
                    binding.recyclerView.scrollToPosition(0)

                    fetchOnEmptyList()
                }
            },
        )
    }

    fun fetchOnEmptyList() {
        binding.recyclerView.doOnNextLayout {
            getElementsAccordingToTab(true)
        }
    }

    private fun handleInfiniteScroll() {
        recyclerViewScrollListener =
            object : RecyclerView.OnScrollListener() {
                override fun onScrolled(
                    localRecycler: RecyclerView,
                    dx: Int,
                    dy: Int,
                ) {
                    if (dy > 0) {
                        val lastVisibleItem = getLastVisibleItem()

                        if (lastVisibleItem == (items.size - 1) && items.size < maxItemNumber()) {
                            getElementsAccordingToTab(appendResults = true)
                        }
                    }
                }
            }

        binding.recyclerView.clearOnScrollListeners()
        binding.recyclerView.addOnScrollListener(recyclerViewScrollListener)
    }

    private fun getLastVisibleItem(): Int {
        return when (val manager = binding.recyclerView.layoutManager) {
            is StaggeredGridLayoutManager ->
                manager.findLastCompletelyVisibleItemPositions(
                    null,
                ).last()
            is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition()
            else -> 0
        }
    }

    private fun mayBeEmpty() =
        if (items.isEmpty()) {
            binding.emptyText.visibility = View.VISIBLE
        } else {
            binding.emptyText.visibility = View.GONE
        }

    fun getElementsAccordingToTab(appendResults: Boolean = false) {
        offset =
            if (appendResults && items.size > 0) {
                items.size - 1
            } else {
                0
            }
        firstVisible = if (appendResults) firstVisible else 0

        getItems(appendResults, elementsShown)
    }

    private fun getItems(
        appendResults: Boolean,
        itemType: ItemType,
    ) {
        CoroutineScope(Dispatchers.Main).launch {
            binding.swipeRefreshLayout.isRefreshing = true
            repository.displayedItems = itemType
            items =
                if (appendResults) {
                    repository.getOlderItems()
                } else {
                    repository.getNewerItems()
                }
            binding.swipeRefreshLayout.isRefreshing = false
            handleListResult()
        }
    }

    private fun handleListResult(appendResults: Boolean = false) {
        if (appendResults) {
            val oldManager = binding.recyclerView.layoutManager
            firstVisible =
                when (oldManager) {
                    is StaggeredGridLayoutManager ->
                        oldManager.findFirstCompletelyVisibleItemPositions(null).last()
                    is GridLayoutManager ->
                        oldManager.findFirstCompletelyVisibleItemPosition()
                    else -> 0
                }
        }

        if (recyclerAdapter == null) {
            if (appSettingsService.isCardViewEnabled()) {
                recyclerAdapter =
                    ItemCardAdapter(
                        this,
                        items,
                    ) {
                        updateItems(it)
                    }
            } else {
                recyclerAdapter =
                    ItemListAdapter(
                        this,
                        items,
                    ) {
                        updateItems(it)
                    }

                binding.recyclerView.addItemDecoration(
                    DividerItemDecoration(
                        this@HomeActivity,
                        DividerItemDecoration.VERTICAL,
                    ),
                )
            }
            binding.recyclerView.adapter = recyclerAdapter
        } else {
            recyclerAdapter!!.updateAllItems(items)
        }

        reloadBadges()
        mayBeEmpty()
    }

    private fun reloadBadges() {
        if (appSettingsService.isDisplayUnreadCountEnabled() || appSettingsService.isDisplayAllCountEnabled()) {
            CoroutineScope(Dispatchers.IO).launch {
                repository.reloadBadges()
            }
        }
    }

    private fun calculateNoOfColumns(): Int {
        val displayMetrics = resources.displayMetrics
        val dpWidth = displayMetrics.widthPixels / displayMetrics.density
        return (dpWidth / 300).toInt()
    }

    override fun onQueryTextChange(p0: String?): Boolean {
        if (p0.isNullOrBlank()) {
            repository.searchFilter = null
            getElementsAccordingToTab()
            fetchOnEmptyList()
        }
        return false
    }

    override fun onQueryTextSubmit(p0: String?): Boolean {
        repository.searchFilter = p0
        getElementsAccordingToTab()
        fetchOnEmptyList()
        return false
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        val inflater = menuInflater
        inflater.inflate(R.menu.home_menu, menu)
        if (appSettingsService.getPublicAccess()) {
            menu.removeItem(R.id.readAll)
            menu.removeItem(R.id.action_sources)
        }

        val searchItem = menu.findItem(R.id.action_search)
        val searchView = searchItem.getActionView() as SearchView
        searchView.setOnQueryTextListener(this)

        return true
    }

    private fun needsConfirmation(
        titleRes: Int,
        messageRes: Int,
        doFn: () -> Unit,
    ) {
        AlertDialog.Builder(this@HomeActivity)
            .setMessage(messageRes)
            .setTitle(titleRes)
            .setPositiveButton(android.R.string.ok) { _, _ -> doFn() }
            .setNegativeButton(android.R.string.cancel) { _, _ -> }
            .create()
            .show()
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.issue_tracker -> {
                val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
                startActivity(browserIntent)
                return true
            }
            R.id.action_filter -> {
                val filterSheetFragment = FilterSheetFragment()
                filterSheetFragment.show(supportFragmentManager, FilterSheetFragment.TAG)
                return true
            }
            R.id.refresh -> {
                needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) {
                    Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
                    CoroutineScope(Dispatchers.Main).launch {
                        val updatedRemote = repository.updateRemote()
                        if (updatedRemote) {
                            Toast.makeText(
                                this@HomeActivity,
                                R.string.refresh_success_response,
                                Toast.LENGTH_LONG,
                            )
                                .show()
                        } else {
                            Toast.makeText(
                                this@HomeActivity,
                                R.string.refresh_failer_message,
                                Toast.LENGTH_SHORT,
                            ).show()
                        }
                    }
                }
                return true
            }
            R.id.readAll -> {
                if (elementsShown == ItemType.UNREAD) {
                    needsConfirmation(R.string.readAll, R.string.markall_dialog_message) {
                        binding.swipeRefreshLayout.isRefreshing = true

                        CoroutineScope(Dispatchers.Main).launch {
                            val success = repository.markAllAsRead(items)
                            if (success) {
                                Toast.makeText(
                                    this@HomeActivity,
                                    R.string.all_posts_read,
                                    Toast.LENGTH_SHORT,
                                ).show()
                                tabNewBadge.removeBadge()

                                getElementsAccordingToTab()
                            } else {
                                Toast.makeText(
                                    this@HomeActivity,
                                    R.string.all_posts_not_read,
                                    Toast.LENGTH_SHORT,
                                ).show()
                            }
                            handleListResult()
                            binding.swipeRefreshLayout.isRefreshing = false
                        }
                    }
                }
                return true
            }
            R.id.action_disconnect -> {
                needsConfirmation(R.string.confirm_disconnect_title, R.string.confirm_disconnect_description) {
                    runBlocking {
                        repository.logout()
                    }
                    val intent = Intent(this, LoginActivity::class.java)
                    this.startActivity(intent)
                    finish()
                }
                return true
            }
            R.id.action_settings -> {
                settingsLauncher.launch(Intent(this, SettingsActivity::class.java))
                return true
            }
            R.id.action_sources -> {
                startActivity(Intent(this, SourcesActivity::class.java))
                return true
            }
            else -> return super.onOptionsItemSelected(item)
        }
    }

    private fun maxItemNumber(): Int =
        when (elementsShown) {
            ItemType.UNREAD -> repository.badgeUnread.value
            ItemType.ALL -> repository.badgeAll.value
            ItemType.STARRED -> repository.badgeStarred.value
            else -> repository.badgeUnread.value // if !elementsShown then unread are fetched.
        }

    private fun updateItems(adapterItems: ArrayList<SelfossModel.Item>) {
        items = adapterItems
    }

    private fun handleRecurringTask() {
        if (appSettingsService.isPeriodicRefreshEnabled()) {
            val myConstraints =
                Constraints.Builder()
                    .setRequiresBatteryNotLow(true)
                    .setRequiresCharging(appSettingsService.isRefreshWhenChargingOnlyEnabled())
                    .setRequiresStorageNotLow(true)
                    .build()

            val backgroundWork =
                PeriodicWorkRequestBuilder<LoadingWorker>(appSettingsService.getRefreshMinutes(), TimeUnit.MINUTES)
                    .setConstraints(myConstraints)
                    .addTag("selfoss-loading")
                    .build()

            WorkManager.getInstance(
                baseContext,
            ).enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork)
        }
    }
}
