package org.tasks.preferences.fragments

import android.content.Intent
import android.os.Bundle
import androidx.core.content.ContextCompat
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import com.todoroo.astrid.gtasks.auth.GtasksLoginActivity
import com.todoroo.astrid.service.TaskDeleter
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.tasks.BuildConfig
import org.tasks.R
import org.tasks.auth.SignInActivity
import org.tasks.billing.BillingClient
import org.tasks.billing.Inventory
import org.tasks.billing.Purchase
import org.tasks.billing.PurchaseActivity
import org.tasks.caldav.BaseCaldavAccountSettingsActivity
import org.tasks.caldav.CaldavAccountSettingsActivity
import org.tasks.data.CaldavAccount
import org.tasks.etebase.EtebaseAccountSettingsActivity
import org.tasks.extensions.Context.openUri
import org.tasks.extensions.Context.toast
import org.tasks.injection.InjectingPreferenceFragment
import org.tasks.preferences.IconPreference
import org.tasks.preferences.MainPreferences
import org.tasks.preferences.Preferences
import org.tasks.preferences.PreferencesViewModel
import org.tasks.preferences.fragments.GoogleTasksAccount.Companion.newGoogleTasksAccountPreference
import org.tasks.preferences.fragments.MicrosoftAccount.Companion.newMicrosoftAccountPreference
import org.tasks.preferences.fragments.TasksAccount.Companion.newTasksAccountPreference
import org.tasks.sync.AddAccountDialog
import org.tasks.sync.AddAccountDialog.Companion.EXTRA_SELECTED
import org.tasks.sync.AddAccountDialog.Companion.newAccountDialog
import org.tasks.sync.AddAccountDialog.Platform
import org.tasks.sync.microsoft.MicrosoftSignInViewModel
import org.tasks.widget.AppWidgetManager
import javax.inject.Inject

@AndroidEntryPoint
class MainSettingsFragment : InjectingPreferenceFragment() {

    @Inject lateinit var appWidgetManager: AppWidgetManager
    @Inject lateinit var preferences: Preferences
    @Inject lateinit var taskDeleter: TaskDeleter
    @Inject lateinit var inventory: Inventory
    @Inject lateinit var billingClient: BillingClient

    private val viewModel: PreferencesViewModel by activityViewModels()
    private val microsoftVM: MicrosoftSignInViewModel by viewModels()

    override fun getPreferenceXml() = R.xml.preferences

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        findPreference(R.string.add_account).setOnPreferenceClickListener { addAccount() }

        findPreference(R.string.name_your_price).setOnPreferenceClickListener {
            startActivity(Intent(context, PurchaseActivity::class.java))
            false
        }

        findPreference(R.string.button_unsubscribe).setOnPreferenceClickListener {
            inventory.unsubscribe(requireActivity())
        }

        findPreference(R.string.refresh_purchases).setOnPreferenceClickListener {
            lifecycleScope.launch {
                try {
                    billingClient.queryPurchases(throwError = true)
                    if (inventory.subscription.value == null) {
                        activity?.toast(R.string.no_google_play_subscription)
                    }
                } catch (e: Exception) {
                    activity?.toast(e.message)
                }
            }
            false
        }

        viewModel.lastBackup.observe(this) { updateBackupWarning() }
        viewModel.lastAndroidBackup.observe(this) { updateBackupWarning() }
        viewModel.lastDriveBackup.observe(this) { updateBackupWarning() }
        viewModel.caldavAccounts.observe(this) { refreshAccounts() }
        if (BuildConfig.FLAVOR == "generic") {
            remove(R.string.upgrade_to_pro)
        } else {
            inventory.subscription.observe(this) { refreshSubscription(it) }
        }

        parentFragmentManager.setFragmentResultListener(
            AddAccountDialog.ADD_ACCOUNT,
            this
        ) { _, result ->
            val platform =
                result.getSerializable(EXTRA_SELECTED) as? Platform
                    ?: return@setFragmentResultListener
            when (platform) {
                Platform.TASKS_ORG ->
                    startActivityForResult(
                        Intent(requireContext(), SignInActivity::class.java),
                        REQUEST_TASKS_ORG
                    )
                Platform.GOOGLE_TASKS ->
                    startActivityForResult(
                        Intent(requireContext(), GtasksLoginActivity::class.java),
                        REQUEST_GOOGLE_TASKS
                    )
                Platform.MICROSOFT ->
                    microsoftVM.signIn(requireActivity())
                Platform.DAVX5 ->
                    context?.openUri(R.string.url_davx5)
                Platform.CALDAV ->
                    startActivityForResult(
                        Intent(requireContext(), CaldavAccountSettingsActivity::class.java),
                        REQUEST_CALDAV_SETTINGS
                    )
                Platform.ETESYNC ->
                    startActivityForResult(
                        Intent(requireContext(), EtebaseAccountSettingsActivity::class.java),
                        REQUEST_CALDAV_SETTINGS
                    )
                Platform.DECSYNC_CC ->
                    context?.openUri(R.string.url_decsync)
            }
        }
    }

    override fun onResume() {
        super.onResume()

        updateBackupWarning()
        updateWidgetVisibility()
    }

    private fun refreshAccounts() {
        val caldavAccounts = viewModel.caldavAccounts.value ?: emptyList()
        val addAccount = findPreference(R.string.add_account)
        val index = preferenceScreen.indexOf(addAccount)
        var current = 0
        caldavAccounts.forEach {
            setup(it, if (current < index) {
                preferenceScreen.getPreference(current++) as IconPreference
            } else {
                preferenceScreen.insertAt(current++)
            })
        }
        preferenceScreen.removeAt(current, index - current)
        if (caldavAccounts.isEmpty()) {
            addAccount.setTitle(R.string.not_signed_in)
            addAccount.setIcon(R.drawable.ic_outline_cloud_off_24px)
        } else {
            addAccount.setTitle(R.string.add_account)
            addAccount.setIcon(R.drawable.ic_outline_add_24px)
        }
        tintIcons(addAccount, requireContext().getColor(R.color.icon_tint_with_alpha))
    }

    private fun addAccount(): Boolean {
        newAccountDialog(hasTasksAccount = viewModel.tasksAccount() != null)
            .show(parentFragmentManager, FRAG_TAG_ADD_ACCOUNT)
        return false
    }

    private fun updateWidgetVisibility() {
        findPreference(R.string.widget_settings).isVisible = appWidgetManager.widgetIds.isNotEmpty()
    }

    override suspend fun setupPreferences(savedInstanceState: Bundle?) {
        requires(BuildConfig.DEBUG, R.string.debug)

        updateWidgetVisibility()
    }

    private fun updateBackupWarning() {
        val backupWarning =
                preferences.showBackupWarnings()
                        && (viewModel.usingPrivateStorage
                        || viewModel.staleLocalBackup
                        || viewModel.staleRemoteBackup)
        (findPreference(R.string.backup_BPr_header) as IconPreference).iconVisible = backupWarning
    }

    private fun setup(account: CaldavAccount, pref: IconPreference) {
        pref.setTitle(account.prefTitle)
        pref.summary = account.name
        pref.setIcon(account.prefIcon)
        if (account.isCaldavAccount) {
            tintIcons(pref, requireContext().getColor(R.color.icon_tint_with_alpha))
        }
        pref.setOnPreferenceClickListener {
            if (account.isTasksOrg) {
                (activity as MainPreferences).startPreference(
                    this,
                    newTasksAccountPreference(account),
                    getString(R.string.tasks_org)
                )
            } else if (account.isMicrosoft) {
                (activity as MainPreferences).startPreference(
                    this,
                    newMicrosoftAccountPreference(account),
                    getString(R.string.microsoft)
                )
            } else if (account.isGoogleTasks) {
                (activity as MainPreferences).startPreference(
                    this,
                    newGoogleTasksAccountPreference(account),
                    getString(R.string.gtasks_GPr_header)
                )
            } else {
                val intent = Intent(context, account.accountSettingsClass).apply {
                    putExtra(BaseCaldavAccountSettingsActivity.EXTRA_CALDAV_DATA, account)
                }
                startActivityForResult(intent, REQUEST_CALDAV_SETTINGS)
            }
            false
        }
        when {
            account.isTasksOrg -> {
                pref.setOnPreferenceClickListener {
                    (activity as MainPreferences).startPreference(
                            this,
                            newTasksAccountPreference(account),
                            getString(R.string.tasks_org)
                    )
                    false
                }
            }
        }
        setupErrorIcon(pref, account.hasError, account.isEteSyncAccount)
    }

    private fun setupErrorIcon(
            pref: IconPreference,
            hasError: Boolean,
            hasWarning: Boolean = false
    ) {
        pref.drawable = ContextCompat
                .getDrawable(requireContext(), when {
                    hasError -> R.drawable.ic_outline_error_outline_24px
                    hasWarning -> R.drawable.ic_outline_error_outline_24px
                    else -> R.drawable.ic_keyboard_arrow_right_24px
                })
                ?.mutate()
        pref.tint = context?.getColor(when {
            hasError -> R.color.overdue
            hasWarning -> R.color.orange_500
            else -> R.color.icon_tint_with_alpha
        })
    }

    private fun refreshSubscription(subscription: Purchase?) {
        findPreference(R.string.upgrade_to_pro).setTitle(if (subscription == null) {
            R.string.upgrade_to_pro
        } else {
            R.string.subscription
        })
        findPreference(R.string.name_your_price).apply {
            if (subscription == null) {
                title = getString(R.string.name_your_price)
                summary = null
            } else {
                val interval = if (subscription.isMonthly) {
                    R.string.price_per_month
                } else {
                    R.string.price_per_year
                }
                val price = (subscription.subscriptionPrice!! - .01).toString()
                title = getString(R.string.manage_subscription)
                summary = getString(R.string.current_subscription, getString(interval, price))
            }
        }
        findPreference(R.string.button_unsubscribe).isVisible = subscription != null
    }

    companion object {
        private const val FRAG_TAG_ADD_ACCOUNT = "frag_tag_add_account"
        const val REQUEST_CALDAV_SETTINGS = 10013
        const val REQUEST_GOOGLE_TASKS = 10014
        const val REQUEST_TASKS_ORG = 10016

        fun PreferenceScreen.removeAt(index: Int, count: Int = 1) {
            repeat(count) {
                removePreference(getPreference(index))
            }
        }

        fun PreferenceScreen.indexOf(pref: Preference): Int =
                0.until(preferenceCount).first { pref == getPreference(it) }

        fun PreferenceScreen.insertAt(index: Int): IconPreference {
            index.until(preferenceCount).forEach {
                getPreference(it).apply { order += 1 }
            }
            return IconPreference(context).apply {
                layoutResource = R.layout.preference_icon
                order = index
                iconVisible = true
                addPreference(this)
            }
        }
    }
}
