package de.justjanne.quasseldroid.service

import android.content.ComponentName
import android.content.Context
import android.content.ServiceConnection
import android.os.IBinder
import android.util.Log
import de.justjanne.libquassel.client.session.ClientSession
import de.justjanne.libquassel.protocol.util.StateHolder
import de.justjanne.libquassel.protocol.util.flatMap
import de.justjanne.quasseldroid.BuildConfig
import de.justjanne.quasseldroid.service.QuasselService.Companion.quasselService
import de.justjanne.quasseldroid.util.lifecycle.DefaultContextualLifecycleObserver
import de.justjanne.quasseldroid.util.lifecycle.LifecycleStatus
import kotlinx.coroutines.flow.MutableStateFlow

class QuasselBackend : DefaultContextualLifecycleObserver(), ServiceConnection,
  StateHolder<ClientSessionWrapper?> {
  private var connectionData: ConnectionData? = null

  override fun flow() = state.flatMap()
  override fun state() = state.value?.state()
  private val state = MutableStateFlow<QuasselBinder?>(null)

  override fun onCreate(owner: Context) {
    super.onCreate(owner)
    connectionData?.let { (address, username, password) ->
      Log.d("QuasselBackend", "Starting Quassel Service")
      owner.startService(owner.quasselService(address, Pair(username, password)))
    }
  }

  override fun onStart(owner: Context) {
    super.onStart(owner)
    connectionData?.let { (address, username, password) ->
      Log.d("QuasselBackend", "Binding Quassel Service")
      owner.bindService(owner.quasselService(address, Pair(username, password)), this, 0)
    }
  }

  override fun onStop(owner: Context) {
    super.onStop(owner)
    Log.d("QuasselBackend", "Unbinding Quassel Service")
    owner.unbindService(this)
  }

  fun login(context: Context, connectionData: ConnectionData): Boolean {
    this.connectionData = connectionData
    when (status) {
      LifecycleStatus.CREATED -> {
        val (address, username, password) = connectionData
        Log.d("QuasselBackend", "Starting Quassel Service")
        context.startService(
          context.quasselService(address, Pair(username, password))
        )
        return true
      }
      LifecycleStatus.STARTED, LifecycleStatus.RESUMED -> {
        val (address, username, password) = connectionData
        Log.d("QuasselBackend", "Binding Quassel Service")
        context.startService(
          context.quasselService(address, Pair(username, password))
        )
        context.bindService(
          context.quasselService(address, Pair(username, password)),
          this,
          0
        )
        return true
      }
      else -> {
        Log.w("QuasselBackend", "Trying to log in but status is $status")
        return false
      }
    }
  }

  fun disconnect(context: Context) {
    context.stopService(context.quasselService())
  }

  override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
    if (name == quasselService && service is QuasselBinder) {
      Log.d("QuasselBackend", "Quassel Service bound")
      state.value = service
    } else {
      Log.w("QuasselBackend", "Unknown Service bound: $name")
    }
  }

  override fun onServiceDisconnected(name: ComponentName?) {
    Log.d("QuasselBackend", "Service unbound: $name")
    state.value = null
  }

  companion object {
    private val quasselService = ComponentName(
      BuildConfig.APPLICATION_ID,
      QuasselService::class.java.canonicalName!!
    )
  }
}