package cy.agorise.bitsybitshareswallet.ui.transactions

import android.graphics.Point
import android.os.Bundle
import android.view.*
import androidx.activity.result.contract.ActivityResultContracts.OpenDocumentTree
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.viewModels
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsMultiChoice
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.jakewharton.rxbinding3.appcompat.queryTextChangeEvents
import cy.agorise.bitsybitshareswallet.R
import cy.agorise.bitsybitshareswallet.databinding.FragmentTransactionsBinding
import cy.agorise.bitsybitshareswallet.utils.BounceTouchListener
import cy.agorise.bitsybitshareswallet.utils.Constants
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import java.util.*
import java.util.concurrent.TimeUnit

/**
 * Shows the list of transactions as well as options to filter and export those transactions
 * to PDF and CSV files
 */
class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSelectedListener {

    private val viewModel: TransactionsViewModel by viewModels()

    private var _binding: FragmentTransactionsBinding? = null
    private val binding get() = _binding!!

    private var isPdfRequested: Boolean = false
    private var isCsvRequested: Boolean = false

    private var mDisposables = CompositeDisposable()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        setHasOptionsMenu(true)

        _binding = FragmentTransactionsBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

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

        val crashlytics = FirebaseCrashlytics.getInstance()
        crashlytics.setCustomKey(Constants.CRASHLYTICS_KEY_LAST_SCREEN, TAG)

        val userId = PreferenceManager.getDefaultSharedPreferences(context)
            .getString(Constants.KEY_CURRENT_ACCOUNT_ID, "") ?: ""

        val transfersDetailsAdapter = TransfersDetailsAdapter(requireContext())
        binding.rvTransactions.adapter = transfersDetailsAdapter
        binding.rvTransactions.layoutManager = LinearLayoutManager(context)

        // Configure TransactionsViewModel to fetch the transaction history
        viewModel.getFilteredTransactions(userId).observe(viewLifecycleOwner) { transactions ->
            if (transactions.isEmpty()) {
                binding.rvTransactions.visibility = View.GONE
                binding.tvEmpty.visibility = View.VISIBLE
            } else {
                binding.rvTransactions.visibility = View.VISIBLE
                binding.tvEmpty.visibility = View.GONE

                val shouldScrollUp = transactions.size - transfersDetailsAdapter.itemCount == 1
                transfersDetailsAdapter.replaceAll(transactions)

                // Scroll to the top only if the difference between old and new items is 1
                // which most likely means a new transaction was received/sent.
                if (shouldScrollUp)
                    binding.rvTransactions.scrollToPosition(0)
            }
        }

        // Set custom touch listener to handle bounce/stretch effect
        val bounceTouchListener = BounceTouchListener(binding.rvTransactions)
        binding.rvTransactions.setOnTouchListener(bounceTouchListener)
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.menu_transactions, menu)

        // Adds listener for the SearchView
        val searchItem = menu.findItem(R.id.menu_search)
        val searchView = searchItem.actionView as SearchView
        mDisposables.add(
            searchView.queryTextChangeEvents()
                .skipInitialValue()
                .debounce(500, TimeUnit.MILLISECONDS)
                .map { it.queryText.toString().toLowerCase(Locale.getDefault()) }
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe {
                    viewModel.setFilterQuery(it)
                }
        )

        // Adjust SearchView width to avoid pushing other menu items out of the screen
        searchView.maxWidth = getScreenWidth(activity) * 3 / 5
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.menu_filter -> {
                val filterOptionsDialog = FilterOptionsDialog()
                val args = Bundle()
                args.putParcelable(
                    FilterOptionsDialog.KEY_FILTER_OPTIONS,
                    viewModel.getFilterOptions()
                )
                filterOptionsDialog.arguments = args
                filterOptionsDialog.show(childFragmentManager, "filter-options-tag")
                true
            }
            R.id.menu_export -> {
                showExportOptionsDialog()
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }

    /**
     * Returns the screen width in pixels for the given [FragmentActivity]
     */
    private fun getScreenWidth(activity: FragmentActivity?): Int {
        if (activity == null)
            return 200

        val size = Point()
        activity.windowManager.defaultDisplay.getSize(size)
        return size.x
    }

    /**
     * Gets called when the user selects some filter options in the [FilterOptionsDialog] and wants to apply them.
     */
    override fun onFilterOptionsSelected(filterOptions: FilterOptions) {
        viewModel.applyFilterOptions(filterOptions)
    }

    private fun showExportOptionsDialog() {
        // Make export options selected by default
        val indices = intArrayOf(0, 1)

        MaterialDialog(requireContext()).show {
            title(R.string.title_export_transactions)
            listItemsMultiChoice(
                R.array.export_options,
                initialSelection = indices,
                waitForPositiveButton = true
            ) { _, indices, _ ->
                // Update export options selected
                isPdfRequested = indices.contains(0)
                isCsvRequested = indices.contains(1)
            }
            negativeButton(android.R.string.cancel) { dismiss() }
            positiveButton(R.string.title_export) { getFolderForExport.launch(null) }
        }
    }

    private val getFolderForExport = registerForActivityResult(OpenDocumentTree()) { folderUri ->
        viewModel.exportFilteredTransactions(folderUri, isPdfRequested, isCsvRequested)
    }

    override fun onDestroy() {
        super.onDestroy()

        if (!mDisposables.isDisposed) mDisposables.dispose()
    }

    companion object {
        private const val TAG = "TransactionsFragment"
    }
}
