package chat.vojo.proxy.service

import chat.vojo.proxy.core.AppSettings
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import java.util.concurrent.atomic.AtomicLong

/** Process-wide observable VPN state shared between the service and the UI. */
object VpnState {

    enum class Status { DISCONNECTED, CONNECTING, CONNECTED, STOPPING, ERROR }

    data class Stats(
        val txBytes: Long = 0,
        val rxBytes: Long = 0,
    )

    /** Config + settings snapshot the running tunnel was built from. */
    data class Applied(val configId: String, val settings: AppSettings)

    /** One row of the connection journal rendered by the timeline rail. */
    data class TunnelEvent(
        /** Process-unique, monotonic — a stable LazyColumn key even when two events share a millisecond. */
        val id: Long,
        val at: Long,
        val label: String,
        val detail: String? = null,
        val tone: Tone = Tone.NEUTRAL,
    ) {
        enum class Tone { NEUTRAL, OK, WARN, ERROR }
    }

    private const val MAX_EVENTS = 30
    private val eventSeq = AtomicLong(0)

    private val _status = MutableStateFlow(Status.DISCONNECTED)
    val status: StateFlow<Status> = _status.asStateFlow()

    private val _stats = MutableStateFlow(Stats())
    val stats: StateFlow<Stats> = _stats.asStateFlow()

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

    /** Epoch millis when the tunnel reached CONNECTED, or 0. */
    private val _connectedSince = MutableStateFlow(0L)
    val connectedSince: StateFlow<Long> = _connectedSince.asStateFlow()

    private val _applied = MutableStateFlow<Applied?>(null)
    val applied: StateFlow<Applied?> = _applied.asStateFlow()

    private val _events = MutableStateFlow<List<TunnelEvent>>(emptyList())
    val events: StateFlow<List<TunnelEvent>> = _events.asStateFlow()

    internal fun setConnecting() {
        _error.value = null
        _status.value = Status.CONNECTING
    }

    internal fun setConnected(applied: Applied) {
        _status.value = Status.CONNECTED
        _applied.value = applied
        if (_connectedSince.value == 0L) _connectedSince.value = System.currentTimeMillis()
    }

    internal fun setStopping() {
        _status.value = Status.STOPPING
    }

    /** Final state after shutdown; a non-null [error] survives until the next connect. */
    internal fun setStopped(error: String? = null) {
        if (error != null) {
            _error.value = error
            _status.value = Status.ERROR
        } else {
            _status.value = Status.DISCONNECTED
        }
        _connectedSince.value = 0L
        _stats.value = Stats()
        _applied.value = null
    }

    internal fun setStats(stats: Stats) {
        _stats.value = stats
    }

    internal fun event(label: String, detail: String? = null, tone: TunnelEvent.Tone = TunnelEvent.Tone.NEUTRAL) {
        val ev = TunnelEvent(eventSeq.incrementAndGet(), System.currentTimeMillis(), label, detail, tone)
        _events.value = (_events.value + ev).takeLast(MAX_EVENTS)
    }
}
