package org.thoughtcrime.securesms.components

import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.View.OnLongClickListener
import android.view.ViewGroup
import androidx.appcompat.widget.AppCompatImageButton
import com.google.android.material.snackbar.Snackbar
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.menu.ActionItem
import org.thoughtcrime.securesms.components.menu.SignalContextMenu
import org.thoughtcrime.securesms.conversation.MessageSendType
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.ViewUtil
import java.lang.AssertionError
import java.util.concurrent.CopyOnWriteArrayList

/**
 * The send button you see in a conversation.
 * Also encapsulates the long-press menu that allows users to switch [MessageSendType]s.
 */
class SendButton(context: Context, attributeSet: AttributeSet?) : AppCompatImageButton(context, attributeSet), OnLongClickListener {

  companion object {
    private val TAG = Log.tag(SendButton::class.java)
  }

  private val listeners: MutableList<SendTypeChangedListener> = CopyOnWriteArrayList()

  private var availableSendTypes: List<MessageSendType> = MessageSendType.getAllAvailable(context, false)
  private var activeMessageSendType: MessageSendType? = null
  private var defaultTransportType: MessageSendType.TransportType = MessageSendType.TransportType.SIGNAL
  private var defaultSubscriptionId: Int? = null

  lateinit var snackbarContainer: View
  private var popupContainer: ViewGroup? = null

  init {
    setOnLongClickListener(this)
    ViewUtil.mirrorIfRtl(this, getContext())
  }

  /**
   * @return True if the [selectedSendType] was chosen manually by the user, otherwise false.
   */
  val isManualSelection: Boolean
    get() = activeMessageSendType != null

  /**
   * The actively-selected send type.
   */
  val selectedSendType: MessageSendType
    get() {
      activeMessageSendType?.let {
        return it
      }

      if (defaultTransportType === MessageSendType.TransportType.SMS) {
        for (type in availableSendTypes) {
          if (type.usesSmsTransport && (defaultSubscriptionId == null || type.simSubscriptionId == defaultSubscriptionId)) {
            return type
          }
        }
      }

      for (type in availableSendTypes) {
        if (type.transportType === defaultTransportType) {
          return type
        }
      }

      Log.w(TAG, "No options of default type! Resetting. DefaultTransportType: $defaultTransportType, AllAvailable: ${availableSendTypes.map { it.transportType }}")

      val signalType: MessageSendType? = availableSendTypes.firstOrNull { it.usesSignalTransport }
      if (signalType != null) {
        Log.w(TAG, "No options of default type, but Signal type is available. Switching. DefaultTransportType: $defaultTransportType, AllAvailable: ${availableSendTypes.map { it.transportType }}")
        defaultTransportType = MessageSendType.TransportType.SIGNAL
        onSelectionChanged(signalType, false)
        return signalType
      } else if (availableSendTypes.isEmpty()) {
        Log.w(TAG, "No send types available at all! Enabling the Signal transport.")
        defaultTransportType = MessageSendType.TransportType.SIGNAL
        availableSendTypes = listOf(MessageSendType.SignalMessageSendType)
        onSelectionChanged(MessageSendType.SignalMessageSendType, false)
        return MessageSendType.SignalMessageSendType
      } else {
        throw AssertionError("No options of default type! DefaultTransportType: $defaultTransportType, AllAvailable: ${availableSendTypes.map { it.transportType }}")
      }
    }

  fun addOnSelectionChangedListener(listener: SendTypeChangedListener) {
    listeners.add(listener)
  }

  fun triggerSelectedChangedEvent() {
    onSelectionChanged(newType = selectedSendType, isManualSelection = false)
  }

  fun resetAvailableTransports(isMediaMessage: Boolean) {
    availableSendTypes = MessageSendType.getAllAvailable(context, isMediaMessage)
    activeMessageSendType = null
    defaultTransportType = MessageSendType.TransportType.SIGNAL
    defaultSubscriptionId = null
    onSelectionChanged(newType = selectedSendType, isManualSelection = false)
  }

  fun disableTransportType(type: MessageSendType.TransportType) {
    availableSendTypes = availableSendTypes.filterNot { it.transportType == type }
  }

  fun setDefaultTransport(type: MessageSendType.TransportType) {
    if (defaultTransportType == type) {
      return
    }
    defaultTransportType = type
    onSelectionChanged(newType = selectedSendType, isManualSelection = false)
  }

  fun setSendType(sendType: MessageSendType?) {
    if (activeMessageSendType == sendType) {
      return
    }
    activeMessageSendType = sendType
    onSelectionChanged(newType = selectedSendType, isManualSelection = true)
  }

  fun setDefaultSubscriptionId(subscriptionId: Int?) {
    if (defaultSubscriptionId == subscriptionId) {
      return
    }
    defaultSubscriptionId = subscriptionId
    onSelectionChanged(newType = selectedSendType, isManualSelection = false)
  }

  /**
   * Must be called with a view that is acceptable for determining the bounds of the popup selector.
   */
  fun setPopupContainer(container: ViewGroup) {
    popupContainer = container
  }

  private fun onSelectionChanged(newType: MessageSendType, isManualSelection: Boolean) {
    setImageResource(newType.buttonDrawableRes)
    contentDescription = context.getString(newType.titleRes)

    for (listener in listeners) {
      listener.onSendTypeChanged(newType, isManualSelection)
    }
  }

  override fun onLongClick(v: View): Boolean {
    if (!isEnabled) {
      return false
    }

    if (availableSendTypes.size == 1) {
      return if (!SignalStore.misc().smsExportPhase.allowSmsFeatures()) {
        Snackbar.make(snackbarContainer, R.string.InputPanel__sms_messaging_is_no_longer_supported_in_signal, Snackbar.LENGTH_SHORT).show()
        true
      } else {
        false
      }
    }

    val currentlySelected: MessageSendType = selectedSendType

    val items = availableSendTypes
      .filterNot { it == currentlySelected }
      .map { option ->
        ActionItem(
          iconRes = option.menuDrawableRes,
          title = option.getTitle(context),
          action = { setSendType(option) }
        )
      }

    SignalContextMenu.Builder((parent as View), popupContainer!!)
      .preferredVerticalPosition(SignalContextMenu.VerticalPosition.ABOVE)
      .offsetY(ViewUtil.dpToPx(8))
      .show(items)

    return true
  }

  interface SendTypeChangedListener {
    fun onSendTypeChanged(newType: MessageSendType, manuallySelected: Boolean)
  }
}
