package org.mosad.teapod.ui.activity.main.fragments

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.*
import org.mosad.teapod.BuildConfig
import org.mosad.teapod.R
import org.mosad.teapod.databinding.FragmentAccountBinding
import org.mosad.teapod.parser.crunchyroll.Benefits
import org.mosad.teapod.parser.crunchyroll.Crunchyroll
import org.mosad.teapod.parser.crunchyroll.Profile
import org.mosad.teapod.parser.crunchyroll.supportedAudioLocals
import org.mosad.teapod.parser.crunchyroll.supportedSubtitleLocals
import org.mosad.teapod.preferences.EncryptedPreferences
import org.mosad.teapod.preferences.Preferences
import org.mosad.teapod.ui.activity.main.MainActivity
import org.mosad.teapod.ui.components.LoginModalBottomSheet
import org.mosad.teapod.util.DataTypes.Theme
import org.mosad.teapod.util.showFragment
import org.mosad.teapod.util.toDisplayString
import java.util.*

class AccountFragment : Fragment() {

    private lateinit var binding: FragmentAccountBinding
    private var profile: Deferred<Profile> = lifecycleScope.async {
        Crunchyroll.profile()
    }
    private var benefits: Deferred<Benefits> = lifecycleScope.async {
        Crunchyroll.benefits()
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        binding = FragmentAccountBinding.inflate(inflater, container, false)
        return binding.root
    }

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

        binding.textAccountLogin.text = EncryptedPreferences.login

        // load account status and tier (async) info before anything else
        lifecycleScope.launch {
            benefits.await().apply {
                this.items.firstOrNull { it.benefit == "cr_premium" }?.let {
                    binding.textAccountSubscription.text = getString(R.string.account_premium)
                }

                this.items.firstOrNull { it.benefit == "cr_fan_pack" }?.let {
                    binding.textAccountSubscriptionDesc.text =
                        getString(R.string.account_tier, getString(R.string.account_tier_mega_fan))
                }
            }
        }

        // add preferred subtitles
        lifecycleScope.launch {
            binding.textSettingsAudioLanguageDesc.text = Locale.forLanguageTag(
                profile.await().preferredContentAudioLanguage
            ).displayLanguage
            binding.textSettingsSubtitleLanguageDesc.text = Locale.forLanguageTag(
                profile.await().preferredContentSubtitleLanguage
            ).displayLanguage
        }
        binding.switchAutoplay.isChecked = Preferences.autoplay
        binding.textThemeSelected.text = when (Preferences.theme) {
            Theme.SYSTEM -> getString(R.string.theme_system)
            Theme.LIGHT -> getString(R.string.theme_light)
            Theme.DARK -> getString(R.string.theme_dark)
        }

        binding.linearDevSettings.isVisible = Preferences.devSettings
        binding.switchUpdatePlayhead.isChecked = Preferences.updatePlayhead

        binding.textInfoAboutDesc.text = getString(R.string.info_about_desc, BuildConfig.VERSION_NAME, getString(R.string.build_time))

        initActions()
    }

    private fun initActions() {
        binding.linearAccountLogin.setOnClickListener {
            showLoginDialog()
        }

        binding.linearSettingsAudioLanguage.setOnClickListener {
            showAudioLanguageSelection()
        }

        binding.linearSettingsSubtitleLanguage.setOnClickListener {
            showSubtitleLanguageSelection()
        }

        binding.switchAutoplay.setOnClickListener {
            Preferences.saveAutoplay(requireContext(), binding.switchAutoplay.isChecked)
        }

        binding.linearTheme.setOnClickListener {
            showThemeDialog()
        }

        binding.linearInfo.setOnClickListener {
            activity?.showFragment(AboutFragment())
        }

        binding.switchUpdatePlayhead.setOnClickListener {
            Preferences.saveUpdatePlayhead(requireContext(), binding.switchUpdatePlayhead.isChecked)
        }

        binding.linearExportData.setOnClickListener {
            // unused
        }

        binding.linearImportData.setOnClickListener {
            // unused
        }
    }

    private fun showLoginDialog() {
        val loginModal = LoginModalBottomSheet().apply {
            login = EncryptedPreferences.login
            password = ""
            positiveAction = {
                EncryptedPreferences.saveCredentials(login, password, requireContext())

                // TODO only dismiss if login was successful
                this.dismiss()
            }
            negativeAction = {
                this.dismiss()
            }
        }
        activity?.let { loginModal.show(it.supportFragmentManager, LoginModalBottomSheet.TAG) }
    }

    private fun showAudioLanguageSelection() {
        // we should be able to use the index of supportedLocals for language selection, items is GUI only
        val items = supportedAudioLocals.map {
            it.toDisplayString(getString(R.string.settings_content_language_none))
        }.toTypedArray()

        var initialSelection: Int
        // profile should be completed here, therefore blocking
        runBlocking {
            initialSelection = supportedAudioLocals.indexOf(Locale.forLanguageTag(
                profile.await().preferredContentAudioLanguage))
            if (initialSelection < 0) initialSelection = supportedAudioLocals.lastIndex
        }

        MaterialAlertDialogBuilder(requireContext())
            .setTitle(R.string.settings_audio_language)
            .setSingleChoiceItems(items, initialSelection){ dialog, which ->
                updateAudioLanguage(supportedAudioLocals[which])
                dialog.dismiss()
            }
            .show()
    }

    private fun showSubtitleLanguageSelection() {
        // we should be able to use the index of supportedLocals for language selection, items is GUI only
        val items = supportedSubtitleLocals.map {
            it.toDisplayString(getString(R.string.settings_content_language_none))
        }.toTypedArray()

        var initialSelection: Int
        // profile should be completed here, therefore blocking
        runBlocking {
            initialSelection = supportedSubtitleLocals.indexOf(Locale.forLanguageTag(
                profile.await().preferredContentSubtitleLanguage))
            if (initialSelection < 0) initialSelection = supportedSubtitleLocals.lastIndex
        }

        MaterialAlertDialogBuilder(requireContext())
            .setTitle(R.string.settings_audio_language)
            .setSingleChoiceItems(items, initialSelection){ dialog, which ->
                updateSubtitleLanguage(supportedSubtitleLocals[which])
                dialog.dismiss()
            }
            .show()
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    private fun updateAudioLanguage(preferredLocale: Locale) {
        lifecycleScope.launch {
            Crunchyroll.setPreferredAudioLanguage(preferredLocale.toLanguageTag())

        }.invokeOnCompletion {
            // update the local preferred audio language
            Preferences.savePreferredAudioLocal(requireContext(), preferredLocale)

            // update profile since the language selection might have changed
            profile = lifecycleScope.async { Crunchyroll.profile() }
            profile.invokeOnCompletion {
                // update language once loading profile is completed
                binding.textSettingsAudioLanguageDesc.text = Locale.forLanguageTag(
                    profile.getCompleted().preferredContentAudioLanguage
                ).displayLanguage
            }
        }
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    private fun updateSubtitleLanguage(preferredLocal: Locale) {
        lifecycleScope.launch {
            Crunchyroll.setPreferredSubtitleLanguage(preferredLocal.toLanguageTag())

        }.invokeOnCompletion {
            // update the local preferred subtitle language
            Preferences.savePreferredSubtitleLocal(requireContext(), preferredLocal)

            // update profile since the language selection might have changed
            profile = lifecycleScope.async { Crunchyroll.profile() }
            profile.invokeOnCompletion {
                // update language once loading profile is completed
                binding.textSettingsAudioLanguageDesc.text = Locale.forLanguageTag(
                    profile.getCompleted().preferredContentSubtitleLanguage
                ).displayLanguage
            }
        }
    }

    private fun showThemeDialog() {
        val items = arrayOf(
            resources.getString(R.string.theme_system),
            resources.getString(R.string.theme_light),
            resources.getString(R.string.theme_dark)
        )

        MaterialAlertDialogBuilder(requireContext())
            .setTitle(R.string.theme)
            .setSingleChoiceItems(items, Preferences.theme.ordinal){ _, which ->
                when(which) {
                    0 -> Preferences.saveTheme(requireContext(), Theme.SYSTEM)
                    1 -> Preferences.saveTheme(requireContext(), Theme.LIGHT)
                    2 -> Preferences.saveTheme(requireContext(), Theme.DARK)
                    else -> Preferences.saveTheme(requireContext(), Theme.SYSTEM)
                }

                (activity as MainActivity).restart()
            }
            .show()
    }

}