/*
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package com.runicgateway.app.ui.shard

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.runicgateway.app.core.net.ShardStreamEvent
import com.runicgateway.app.core.result.ApiResult
import com.runicgateway.app.data.api.dto.GovernorDto
import com.runicgateway.app.data.api.dto.GovernorTermDto
import com.runicgateway.app.data.repository.ShardRepository
import com.runicgateway.app.ui.UiState
import com.runicgateway.app.ui.toShardUiState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

/**
 * The town-governor board (PLAN.md §6.2): loaded from `/public/shard/governors`,
 * kept live by `city.update` frames (keyed by city — cities are fixed, so there is
 * no remove). Empty on shards without City Loyalty. A city's term ledger is fetched
 * on demand via [loadHistory] for the look-back panel.
 */
@HiltViewModel
class GovernorsViewModel @Inject constructor(
    private val repository: ShardRepository,
) : ViewModel() {

    private val board = LiveBoard<GovernorDto> { it.city }

    private val _state = MutableStateFlow<UiState<List<GovernorDto>>>(UiState.Loading)
    val state: StateFlow<UiState<List<GovernorDto>>> = _state.asStateFlow()

    private val _connected = MutableStateFlow(false)
    val connected: StateFlow<Boolean> = _connected.asStateFlow()

    /** Per-city expanded term history, loaded lazily; absent = not yet requested. */
    private val _history = MutableStateFlow<Map<String, List<GovernorTermDto>>>(emptyMap())
    val history: StateFlow<Map<String, List<GovernorTermDto>>> = _history.asStateFlow()

    init {
        load()
        collectLive()
    }

    fun load() {
        _state.value = UiState.Loading
        viewModelScope.launch {
            when (val result = repository.governors()) {
                is ApiResult.Ok -> {
                    board.seed(result.data)
                    publish()
                }
                else -> _state.value = result.toShardUiState()
            }
        }
    }

    /** Fetch (once) the term ledger for [city] for its expandable history panel. */
    fun loadHistory(city: String) {
        if (_history.value.containsKey(city)) return
        viewModelScope.launch {
            val terms = (repository.governorHistory(city) as? ApiResult.Ok)?.data ?: emptyList()
            _history.value = _history.value + (city to terms)
        }
    }

    private fun collectLive() {
        viewModelScope.launch {
            repository.liveEvents().collect { event ->
                when (event) {
                    is ShardStreamEvent.Open -> _connected.value = true
                    is ShardStreamEvent.Closed -> _connected.value = false
                    is ShardStreamEvent.Frame -> applyFrame(event)
                }
            }
        }
    }

    private fun applyFrame(frame: ShardStreamEvent.Frame) {
        if (frame.kind != "city.update") return
        val gov = repository.governorFrame(frame.data) ?: return
        if (gov.city.isBlank()) return
        board.upsert(gov)
        if (_state.value is UiState.Success) publish()
    }

    private fun publish() {
        val rows = board.values().sortedBy { it.city.lowercase() }
        _state.value = UiState.Success(rows)
    }
}
