/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.fenix.settings.deletebrowsingdata

import android.content.DialogInterface
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.fragment_delete_browsing_data.*
import kotlinx.android.synthetic.main.fragment_delete_browsing_data.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import mozilla.components.lib.state.ext.flowScoped
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.utils.Settings

@SuppressWarnings("TooManyFunctions")
class DeleteBrowsingDataFragment : Fragment(R.layout.fragment_delete_browsing_data) {

    private lateinit var controller: DeleteBrowsingDataController
    private var scope: CoroutineScope? = null
    private lateinit var settings: Settings

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        controller = DefaultDeleteBrowsingDataController(requireContext())
        settings = requireContext().settings()

        getCheckboxes().forEach {
            it.onCheckListener = { _ ->
                updateDeleteButton()
                updatePreference(it)
            }
        }

        getCheckboxes().forEach {
            it.isChecked = when (it.id) {
                R.id.open_tabs_item -> settings.deleteOpenTabs
                R.id.browsing_data_item -> settings.deleteBrowsingHistory
                R.id.cookies_item -> settings.deleteCookies
                R.id.cached_files_item -> settings.deleteCache
                R.id.site_permissions_item -> settings.deleteSitePermissions
                else -> true
            }
        }

        view.delete_data?.setOnClickListener {
            askToDelete()
        }
        updateDeleteButton()
    }

    private fun updatePreference(it: DeleteBrowsingDataItem) {
        when (it.id) {
            R.id.open_tabs_item -> settings.deleteOpenTabs = it.isChecked
            R.id.browsing_data_item -> settings.deleteBrowsingHistory = it.isChecked
            R.id.cookies_item -> settings.deleteCookies = it.isChecked
            R.id.cached_files_item -> settings.deleteCache = it.isChecked
            R.id.site_permissions_item -> settings.deleteSitePermissions = it.isChecked
            else -> return
        }
    }

    @ExperimentalCoroutinesApi
    override fun onStart() {
        super.onStart()

        scope = requireComponents.core.store.flowScoped(viewLifecycleOwner) { flow ->
            flow.map { state -> state.tabs.size }
                .ifChanged()
                .collect { openTabs -> updateTabCount(openTabs) }
        }
    }

    override fun onResume() {
        super.onResume()
        showToolbar(getString(R.string.preferences_delete_browsing_data))

        getCheckboxes().forEach {
            it.visibility = View.VISIBLE
        }

        updateItemCounts()
    }

    private fun updateDeleteButton() {
        val enabled = getCheckboxes().any { it.isChecked }

        view?.delete_data?.isEnabled = enabled
        view?.delete_data?.alpha = if (enabled) ENABLED_ALPHA else DISABLED_ALPHA
    }

    private fun askToDelete() {
        context?.let {
            AlertDialog.Builder(it).apply {
                setMessage(
                    it.getString(
                        R.string.delete_browsing_data_prompt_message_3,
                        it.getString(R.string.app_name)
                    )
                )

                setNegativeButton(R.string.delete_browsing_data_prompt_cancel) { it: DialogInterface, _ ->
                    it.cancel()
                }

                setPositiveButton(R.string.delete_browsing_data_prompt_allow) { it: DialogInterface, _ ->
                    it.dismiss()
                    deleteSelected()
                }
                create()
            }.show()
        }
    }

    private fun deleteSelected() {
        startDeletion()
        viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
            getCheckboxes().mapIndexed { i, v ->
                if (v.isChecked) {
                    when (i) {
                        OPEN_TABS_INDEX -> controller.deleteTabs()
                        HISTORY_INDEX -> controller.deleteBrowsingData()
                        COOKIES_INDEX -> controller.deleteCookies()
                        CACHED_INDEX -> controller.deleteCachedFiles()
                        PERMS_INDEX -> controller.deleteSitePermissions()
                    }
                }
            }

            launch(Dispatchers.Main) {
                finishDeletion()
                requireComponents.analytics.metrics.track(Event.ClearedPrivateData)
            }
        }
    }

    private fun startDeletion() {
        progress_bar.visibility = View.VISIBLE
        delete_browsing_data_wrapper.isEnabled = false
        delete_browsing_data_wrapper.isClickable = false
        delete_browsing_data_wrapper.alpha = DISABLED_ALPHA
    }

    private fun finishDeletion() {
        val popAfter = open_tabs_item.isChecked
        progress_bar.visibility = View.GONE
        delete_browsing_data_wrapper.isEnabled = true
        delete_browsing_data_wrapper.isClickable = true
        delete_browsing_data_wrapper.alpha = ENABLED_ALPHA

        updateItemCounts()

        FenixSnackbar.make(
            view = requireView(),
            duration = FenixSnackbar.LENGTH_SHORT,
            isDisplayedWithBrowserToolbar = true
        )
            .setText(resources.getString(R.string.preferences_delete_browsing_data_snackbar))
            .show()

        if (popAfter) viewLifecycleOwner.lifecycleScope.launch(
            Dispatchers.Main
        ) {

            findNavController().apply {
                // If the user deletes all open tabs we need to make sure we remove
                // the BrowserFragment from the backstack.
                popBackStack(R.id.homeFragment, false)
                navigate(DeleteBrowsingDataFragmentDirections.actionGlobalSettingsFragment())
            }
        }
    }

    override fun onPause() {
        super.onPause()
        progress_bar.visibility = View.GONE
    }

    override fun onStop() {
        super.onStop()
        scope?.cancel()
    }

    private fun updateItemCounts() {
        updateTabCount()
        updateHistoryCount()
        updateCookies()
        updateCachedImagesAndFiles()
        updateSitePermissions()
    }

    private fun updateTabCount(openTabs: Int = requireComponents.core.store.state.tabs.size) {
        view?.open_tabs_item?.apply {
            subtitleView.text = resources.getString(
                R.string.preferences_delete_browsing_data_tabs_subtitle,
                openTabs
            )
        }
    }

    private fun updateHistoryCount() {
        view?.browsing_data_item?.subtitleView?.text = ""

        viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
            val historyCount = requireComponents.core.historyStorage.getVisited().size
            launch(Dispatchers.Main) {
                view?.browsing_data_item?.apply {
                    subtitleView.text =
                        resources.getString(
                            R.string.preferences_delete_browsing_data_browsing_data_subtitle,
                            historyCount
                        )
                }
            }
        }
    }

    private fun updateCookies() {
        // NO OP until we have GeckoView methods to count cookies
    }

    private fun updateCachedImagesAndFiles() {
        // NO OP until we have GeckoView methods to count cached images and files
    }

    private fun updateSitePermissions() {
        // NO OP until we have GeckoView methods for cookies and cached files, for consistency
    }

    private fun getCheckboxes(): List<DeleteBrowsingDataItem> {
        val fragmentView = requireView()
        return listOf(
            fragmentView.open_tabs_item,
            fragmentView.browsing_data_item,
            fragmentView.cookies_item,
            fragmentView.cached_files_item,
            fragmentView.site_permissions_item
        )
    }

    companion object {
        private const val ENABLED_ALPHA = 1f
        private const val DISABLED_ALPHA = 0.6f

        private const val OPEN_TABS_INDEX = 0
        private const val HISTORY_INDEX = 1
        private const val COOKIES_INDEX = 2
        private const val CACHED_INDEX = 3
        private const val PERMS_INDEX = 4
    }
}
