package chat.vojo.proxy.ui

import android.app.Application
import androidx.compose.ui.graphics.ImageBitmap
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import chat.vojo.proxy.core.AppSettings
import chat.vojo.proxy.core.ProxyConfig
import chat.vojo.proxy.data.AppInfo
import chat.vojo.proxy.data.AppRepository
import chat.vojo.proxy.data.ConfigStore
import chat.vojo.proxy.data.ProxyState
import chat.vojo.proxy.service.ProxyVpnService
import chat.vojo.proxy.service.VpnState
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

class MainViewModel(app: Application) : AndroidViewModel(app) {

    private val store = ConfigStore(app)
    private val appRepo = AppRepository(app)

    val state: StateFlow<ProxyState> =
        store.state.stateIn(viewModelScope, SharingStarted.Eagerly, ProxyState())

    val status: StateFlow<VpnState.Status> = VpnState.status
    val stats: StateFlow<VpnState.Stats> = VpnState.stats
    val error: StateFlow<String?> = VpnState.error
    val connectedSince: StateFlow<Long> = VpnState.connectedSince
    val events: StateFlow<List<VpnState.TunnelEvent>> = VpnState.events
    val applied: StateFlow<VpnState.Applied?> = VpnState.applied

    private val _apps = MutableStateFlow<List<AppInfo>>(emptyList())
    val apps: StateFlow<List<AppInfo>> = _apps.asStateFlow()

    private val _appsLoading = MutableStateFlow(false)
    val appsLoading: StateFlow<Boolean> = _appsLoading.asStateFlow()

    /** Refreshes on every screen entry so newly installed apps show up. */
    fun loadApps() {
        if (_appsLoading.value) return
        _appsLoading.value = true
        viewModelScope.launch {
            _apps.value = appRepo.installedApps()
            _appsLoading.value = false
        }
    }

    suspend fun iconFor(packageName: String): ImageBitmap? = appRepo.icon(packageName)

    fun cachedIcon(packageName: String): ImageBitmap? = appRepo.cachedIcon(packageName)

    fun setActive(id: String) = viewModelScope.launch {
        val changing = id != state.value.activeConfig?.id
        store.setActive(id)
        // Switching servers while the tunnel runs must actually move the traffic.
        if (changing && VpnState.status.value in RUNNING_STATES) {
            ProxyVpnService.restart(getApplication())
        }
    }

    fun reconnect() = ProxyVpnService.restart(getApplication())

    fun saveConfig(config: ProxyConfig) = viewModelScope.launch { store.upsertConfig(config) }
    fun deleteConfig(id: String) = viewModelScope.launch { store.deleteConfig(id) }

    fun toggleApp(packageName: String, enabled: Boolean) = viewModelScope.launch {
        store.updateSettings { s ->
            val apps = if (enabled) s.allowedApps + packageName else s.allowedApps - packageName
            s.copy(allowedApps = apps)
        }
        // Whitelist changes only take effect at establish() time — re-apply the
        // tunnel live so toggling an app on/off routes immediately, mirroring the
        // auto-reconnect on an active-server switch.
        reconnectIfRunning()
    }

    fun clearApps() = viewModelScope.launch {
        store.updateSettings { it.copy(allowedApps = emptySet()) }
        reconnectIfRunning()
    }

    private fun reconnectIfRunning() {
        if (VpnState.status.value in RUNNING_STATES) ProxyVpnService.restart(getApplication())
    }

    fun updateSettings(transform: (AppSettings) -> AppSettings) = viewModelScope.launch {
        store.updateSettings(transform)
    }

    private companion object {
        val RUNNING_STATES = setOf(VpnState.Status.CONNECTED, VpnState.Status.CONNECTING)
    }
}
