package org.thoughtcrime.securesms.components.settings.app.notifications.profiles

import android.os.Bundle
import android.text.Editable
import android.text.TextUtils
import android.view.View
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.dd.CircularProgressButton
import com.google.android.material.textfield.TextInputLayout
import io.reactivex.rxjava3.kotlin.subscribeBy
import org.signal.core.util.BreakIteratorCompat
import org.signal.core.util.EditTextUtil
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.emoji.EmojiUtil
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
import org.thoughtcrime.securesms.components.settings.app.notifications.profiles.EditNotificationProfileViewModel.SaveNotificationProfileResult
import org.thoughtcrime.securesms.components.settings.app.notifications.profiles.models.NotificationProfileNamePreset
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.megaphone.Megaphones
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment
import org.thoughtcrime.securesms.util.BottomSheetUtil
import org.thoughtcrime.securesms.util.CircularProgressButtonUtil
import org.thoughtcrime.securesms.util.LifecycleDisposable
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.navigation.safeNavigate
import org.thoughtcrime.securesms.util.text.AfterTextChanged

/**
 * Dual use Edit/Create notification profile fragment. Use to create in the create profile flow,
 * and then to edit from profile details. Responsible for naming and emoji.
 */
class EditNotificationProfileFragment : DSLSettingsFragment(layoutId = R.layout.fragment_edit_notification_profile), ReactWithAnyEmojiBottomSheetDialogFragment.Callback {

  private val viewModel: EditNotificationProfileViewModel by viewModels(factoryProducer = this::createFactory)
  private val lifecycleDisposable = LifecycleDisposable()

  private var emojiView: ImageView? = null
  private var nameView: EditText? = null

  private fun createFactory(): ViewModelProvider.Factory {
    val profileId = EditNotificationProfileFragmentArgs.fromBundle(requireArguments()).profileId
    return EditNotificationProfileViewModel.Factory(profileId)
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ApplicationDependencies.getMegaphoneRepository().markFinished(Megaphones.Event.NOTIFICATION_PROFILES)
  }

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

    val toolbar: Toolbar = view.findViewById(R.id.toolbar)
    toolbar.setNavigationOnClickListener {
      ViewUtil.hideKeyboard(requireContext(), requireView())
      requireActivity().onBackPressed()
    }

    val title: TextView = view.findViewById(R.id.edit_notification_profile_title)
    val countView: TextView = view.findViewById(R.id.edit_notification_profile_count)
    val saveButton: CircularProgressButton = view.findViewById(R.id.edit_notification_profile_save)
    val emojiView: ImageView = view.findViewById(R.id.edit_notification_profile_emoji)
    val nameView: EditText = view.findViewById(R.id.edit_notification_profile_name)
    val nameTextWrapper: TextInputLayout = view.findViewById(R.id.edit_notification_profile_name_wrapper)

    EditTextUtil.addGraphemeClusterLimitFilter(nameView, NOTIFICATION_PROFILE_NAME_MAX_GLYPHS)
    nameView.addTextChangedListener(
      AfterTextChanged { editable: Editable ->
        presentCount(countView, editable.toString())
        nameTextWrapper.error = null
      }
    )

    emojiView.setOnClickListener {
      ReactWithAnyEmojiBottomSheetDialogFragment.createForAboutSelection()
        .show(childFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
    }

    view.findViewById<View>(R.id.edit_notification_profile_clear).setOnClickListener {
      nameView.setText("")
      onEmojiSelectedInternal("")
    }

    lifecycleDisposable.bindTo(viewLifecycleOwner.lifecycle)

    saveButton.setOnClickListener {
      if (TextUtils.isEmpty(nameView.text)) {
        nameTextWrapper.error = getString(R.string.EditNotificationProfileFragment__profile_must_have_a_name)
        return@setOnClickListener
      }

      lifecycleDisposable += viewModel.save(nameView.text.toString())
        .doOnSubscribe { CircularProgressButtonUtil.setSpinning(saveButton) }
        .doAfterTerminate { CircularProgressButtonUtil.cancelSpinning(saveButton) }
        .subscribeBy(
          onSuccess = { saveResult ->
            when (saveResult) {
              is SaveNotificationProfileResult.Success -> {
                ViewUtil.hideKeyboard(requireContext(), nameView)
                if (saveResult.createMode) {
                  findNavController().safeNavigate(EditNotificationProfileFragmentDirections.actionEditNotificationProfileFragmentToAddAllowedMembersFragment(saveResult.profile.id))
                } else {
                  findNavController().navigateUp()
                }
              }
              SaveNotificationProfileResult.DuplicateNameFailure -> {
                nameTextWrapper.error = getString(R.string.EditNotificationProfileFragment__a_profile_with_this_name_already_exists)
              }
            }
          }
        )
    }

    lifecycleDisposable += viewModel.getInitialState()
      .subscribeBy(
        onSuccess = { initial ->
          if (initial.createMode) {
            saveButton.text = getString(R.string.EditNotificationProfileFragment__create)
            title.setText(R.string.EditNotificationProfileFragment__name_your_profile)
          } else {
            saveButton.text = getString(R.string.EditNotificationProfileFragment__save)
            title.setText(R.string.EditNotificationProfileFragment__edit_this_profile)
          }
          nameView.setText(initial.name)
          onEmojiSelectedInternal(initial.emoji)

          ViewUtil.focusAndMoveCursorToEndAndOpenKeyboard(nameView)
        }
      )

    this.nameView = nameView
    this.emojiView = emojiView
  }

  override fun bindAdapter(adapter: DSLSettingsAdapter) {
    NotificationProfileNamePreset.register(adapter)

    val onClick = { preset: NotificationProfileNamePreset.Model ->
      nameView?.apply {
        setText(preset.bodyResource)
        setSelection(length(), length())
      }
      onEmojiSelectedInternal(preset.emoji)
    }

    adapter.submitList(
      listOf(
        NotificationProfileNamePreset.Model("\uD83D\uDCAA", R.string.EditNotificationProfileFragment__work, onClick),
        NotificationProfileNamePreset.Model("\uD83D\uDE34", R.string.EditNotificationProfileFragment__sleep, onClick),
        NotificationProfileNamePreset.Model("\uD83D\uDE97", R.string.EditNotificationProfileFragment__driving, onClick),
        NotificationProfileNamePreset.Model("\uD83D\uDE0A", R.string.EditNotificationProfileFragment__downtime, onClick),
        NotificationProfileNamePreset.Model("\uD83D\uDCA1", R.string.EditNotificationProfileFragment__focus, onClick),
      )
    )
  }

  override fun onReactWithAnyEmojiSelected(emoji: String) {
    onEmojiSelectedInternal(emoji)
  }

  override fun onReactWithAnyEmojiDialogDismissed() = Unit

  private fun presentCount(countView: TextView, profileName: String) {
    val breakIterator = BreakIteratorCompat.getInstance()
    breakIterator.setText(profileName)

    val glyphCount = breakIterator.countBreaks()
    if (glyphCount >= NOTIFICATION_PROFILE_NAME_LIMIT_DISPLAY_THRESHOLD) {
      countView.visibility = View.VISIBLE
      countView.text = resources.getString(R.string.EditNotificationProfileFragment__count, glyphCount, NOTIFICATION_PROFILE_NAME_MAX_GLYPHS)
    } else {
      countView.visibility = View.GONE
    }
  }

  private fun onEmojiSelectedInternal(emoji: String) {
    val drawable = EmojiUtil.convertToDrawable(requireContext(), emoji)
    if (drawable != null) {
      emojiView?.setImageDrawable(drawable)
      viewModel.onEmojiSelected(emoji)
    } else {
      emojiView?.setImageResource(R.drawable.ic_add_emoji)
      viewModel.onEmojiSelected("")
    }
  }

  companion object {
    private const val NOTIFICATION_PROFILE_NAME_MAX_GLYPHS = 32
    private const val NOTIFICATION_PROFILE_NAME_LIMIT_DISPLAY_THRESHOLD = 22
  }
}
