package com.omixlab.lckcontrol.ui.plans

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.omixlab.lckcontrol.data.repository.AccountRepository
import com.omixlab.lckcontrol.data.repository.StreamPlanRepository
import com.omixlab.lckcontrol.shared.ConnectedClientInfo
import com.omixlab.lckcontrol.shared.ILckControlCallback
import com.omixlab.lckcontrol.shared.ILckControlService
import com.omixlab.lckcontrol.shared.LinkedAccount
import com.omixlab.lckcontrol.shared.StreamDestination
import com.omixlab.lckcontrol.shared.StreamPlan
import com.omixlab.lckcontrol.data.local.AppPreferences
import com.omixlab.lckcontrol.util.GameInfoProvider
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject

data class DestinationInput(
    val linkedAccountId: String = "",
    val linkedAccountLabel: String = "",
    val title: String = "",
    val description: String = "",
    val privacyStatus: String = "public",
    val gameId: String = "",
    val tags: String = "",
    val isCustom: Boolean = false,
    val rtmpUrl: String = "",
    val streamKey: String = "",
)

@HiltViewModel
class CreatePlanViewModel @Inject constructor(
    @ApplicationContext private val context: Context,
    savedStateHandle: SavedStateHandle,
    private val accountRepository: AccountRepository,
    private val streamPlanRepository: StreamPlanRepository,
    private val appPreferences: AppPreferences,
    val gameInfoProvider: GameInfoProvider,
) : ViewModel() {

    val editingPlanId: String? = savedStateHandle.get<String>("planId")
    val isEditMode: Boolean = editingPlanId != null

    val linkedAccounts: StateFlow<List<LinkedAccount>> = accountRepository.observeAccounts()
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())

    private val _planName = MutableStateFlow("")
    val planName: StateFlow<String> = _planName.asStateFlow()

    private val _executionMode = MutableStateFlow(appPreferences.getDefaultExecutionMode())
    val executionMode: StateFlow<String> = _executionMode.asStateFlow()

    private val _gameId = MutableStateFlow("")
    val gameId: StateFlow<String> = _gameId.asStateFlow()

    private val _connectedClients = MutableStateFlow<List<ConnectedClientInfo>>(emptyList())
    val connectedClients: StateFlow<List<ConnectedClientInfo>> = _connectedClients.asStateFlow()

    private val _destinations = MutableStateFlow<List<DestinationInput>>(emptyList())
    val destinations: StateFlow<List<DestinationInput>> = _destinations.asStateFlow()

    private val _isCreating = MutableStateFlow(false)
    val isCreating: StateFlow<Boolean> = _isCreating.asStateFlow()

    private val _error = MutableStateFlow<String?>(null)
    val error: StateFlow<String?> = _error.asStateFlow()

    private var service: ILckControlService? = null

    private val callback = object : ILckControlCallback.Stub() {
        override fun onStreamPlansChanged(plans: List<StreamPlan>) {}
        override fun onStreamPlanUpdated(plan: StreamPlan) {}
        override fun onClientRegistered(clientId: String) { refreshClients() }
        override fun onClientUnregistered(clientId: String) { refreshClients() }
        override fun onAuthStateChanged(authenticated: Boolean) {}
    }

    private val serviceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
            service = ILckControlService.Stub.asInterface(binder)
            service?.registerCallback(callback)
            refreshClients()
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            service = null
            _connectedClients.value = emptyList()
        }
    }

    init {
        bindToService()
        if (isEditMode) {
            loadExistingPlan()
        }
    }

    private fun bindToService() {
        val intent = Intent().apply {
            component = ComponentName(
                context.packageName,
                "com.omixlab.lckcontrol.service.LckControlService",
            )
        }
        context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
    }

    private fun refreshClients() {
        val clients = service?.connectedClients ?: emptyList()
        _connectedClients.value = clients
        // Auto-fill gameId from first connected client when empty
        if (_gameId.value.isBlank() && clients.isNotEmpty()) {
            _gameId.value = clients.first().packageName
        }
    }

    private fun loadExistingPlan() {
        viewModelScope.launch {
            try {
                val plan = streamPlanRepository.getPlan(editingPlanId!!) ?: return@launch
                _planName.value = plan.name
                _executionMode.value = plan.executionMode
                _gameId.value = plan.gameId

                // Wait for linked accounts to load for label resolution
                val accounts = accountRepository.getAccounts()
                _destinations.value = plan.destinations.map { dest ->
                    val account = accounts.find { it.id == dest.linkedAccountId }
                    val label = if (account != null) "${account.serviceId} - ${account.displayName}" else dest.service
                    val isCustomRtmp = account?.serviceId == "CUSTOM_RTMP"
                    DestinationInput(
                        linkedAccountId = dest.linkedAccountId,
                        linkedAccountLabel = label,
                        title = dest.title,
                        description = dest.description,
                        privacyStatus = dest.privacyStatus,
                        gameId = dest.gameId,
                        tags = dest.tags.joinToString(","),
                        isCustom = isCustomRtmp || dest.service == "CUSTOM",
                        rtmpUrl = if (isCustomRtmp) account?.rtmpUrl ?: dest.rtmpUrl else dest.rtmpUrl,
                        streamKey = if (isCustomRtmp) account?.streamKey ?: dest.streamKey else dest.streamKey,
                    )
                }
            } catch (e: Exception) {
                _error.value = e.message ?: "Failed to load plan"
            }
        }
    }

    fun setPlanName(name: String) {
        _planName.value = name
    }

    fun setExecutionMode(mode: String) {
        _executionMode.value = mode
    }

    fun setGameId(gameId: String) {
        _gameId.value = gameId
    }

    fun addDestination() {
        _destinations.value = _destinations.value + DestinationInput()
    }

    fun updateDestination(index: Int, destination: DestinationInput) {
        _destinations.value = _destinations.value.toMutableList().apply {
            set(index, destination)
        }
    }

    fun removeDestination(index: Int) {
        _destinations.value = _destinations.value.toMutableList().apply {
            removeAt(index)
        }
    }

    fun savePlan(onSaved: (String) -> Unit) {
        val name = _planName.value.trim()
        val dests = _destinations.value

        if (name.isBlank()) {
            _error.value = "Plan name is required"
            return
        }
        if (dests.isEmpty()) {
            _error.value = "Add at least one destination"
            return
        }
        for (dest in dests) {
            if (dest.title.isBlank()) {
                _error.value = "All destinations need a title"
                return
            }
            if (dest.isCustom) {
                if (dest.rtmpUrl.isBlank() || dest.streamKey.isBlank()) {
                    _error.value = "Custom destinations need RTMP URL and stream key"
                    return
                }
            } else if (dest.linkedAccountId.isBlank()) {
                _error.value = "All destinations need an account (or use Custom RTMP)"
                return
            }
        }

        viewModelScope.launch {
            _isCreating.value = true
            _error.value = null
            try {
                val accounts = linkedAccounts.value
                val streamDests = dests.map { input ->
                    if (input.isCustom) {
                        StreamDestination(
                            service = "CUSTOM",
                            title = input.title,
                            rtmpUrl = input.rtmpUrl,
                            streamKey = input.streamKey,
                        )
                    } else {
                        val account = accounts.find { it.id == input.linkedAccountId }
                        StreamDestination(
                            service = account?.serviceId ?: "",
                            linkedAccountId = input.linkedAccountId,
                            title = input.title,
                            description = input.description,
                            privacyStatus = input.privacyStatus,
                            gameId = input.gameId,
                            tags = input.tags.split(",").map { it.trim() }.filter { it.isNotBlank() },
                        )
                    }
                }
                val plan = if (isEditMode) {
                    streamPlanRepository.updatePlan(editingPlanId!!, name, streamDests, _executionMode.value, _gameId.value)
                } else {
                    streamPlanRepository.createPlan(name, streamDests, _executionMode.value, _gameId.value)
                }
                onSaved(plan.planId)
            } catch (e: Exception) {
                _error.value = e.message ?: "Failed to ${if (isEditMode) "update" else "create"} plan"
            } finally {
                _isCreating.value = false
            }
        }
    }

    fun clearError() {
        _error.value = null
    }

    override fun onCleared() {
        service?.unregisterCallback(callback)
        try {
            context.unbindService(serviceConnection)
        } catch (_: IllegalArgumentException) {}
        super.onCleared()
    }
}
