package com.fabledsword.minstrel.quarantine.ui

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.fabledsword.minstrel.api.ErrorCopy
import com.fabledsword.minstrel.connectivity.NetworkStatusController
import com.fabledsword.minstrel.connectivity.recoveries
import com.fabledsword.minstrel.events.EventsStream
import com.fabledsword.minstrel.models.QuarantineRef
import com.fabledsword.minstrel.shared.UiState
import com.fabledsword.minstrel.quarantine.data.QuarantineRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject

private const val SHARE_STOP_TIMEOUT_MS = 5_000L

@HiltViewModel
class HiddenTabViewModel @Inject constructor(
    private val repository: QuarantineRepository,
    private val eventsStream: EventsStream,
    networkStatus: NetworkStatusController,
) : ViewModel() {

    private val refreshError = MutableStateFlow<String?>(null)

    /**
     * Cache-first: emits the cached `cached_quarantine_mine` rows
     * instantly (paints from disk on cold open), SWR-refreshes
     * underneath, and shows Error only when there's no cache AND the
     * refresh failed. Optimistic flag/unflag write the cache, so the
     * list updates without waiting on the server.
     */
    val uiState: StateFlow<UiState<List<QuarantineRef>>> =
        combine(repository.observeMine(), refreshError) { rows, err ->
            when {
                rows.isNotEmpty() -> UiState.Success(rows)
                err != null -> UiState.Error(err)
                else -> UiState.Empty
            }
        }.stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(SHARE_STOP_TIMEOUT_MS),
            initialValue = UiState.Loading,
        )

    init {
        refresh()
        // Live cross-device updates: any quarantine.* event (hide /
        // unhide / resolve / delete from another device) re-fetches the
        // snapshot while the tab is open. Mirrors Flutter's
        // LiveEventsDispatcher invalidating myQuarantineProvider; here
        // the screen-scoped VM subscribes directly, per the Android
        // dispatcher's documented pattern for screen-scoped state.
        viewModelScope.launch {
            eventsStream.events
                .filter { it.kind.startsWith("quarantine.") }
                .collect { refresh() }
        }
        // Screen-level auto-recovery (issue #1245): re-pull when server
        // health returns, so a load that failed offline heals unprompted.
        viewModelScope.launch {
            networkStatus.recoveries().collect { refresh() }
        }
    }

    fun refresh(): Job = viewModelScope.launch {
        refreshError.value = null
        runCatching { repository.refresh() }
            .onFailure { refreshError.value = ErrorCopy.fromThrowable(it) }
    }

    /**
     * Optimistic unhide — the repository drops the cache row first (the
     * observeMine Flow re-emits so the list updates instantly), then
     * calls the server, enqueueing on failure so the intent persists.
     */
    fun unflag(trackId: String) {
        viewModelScope.launch {
            runCatching { repository.unflag(trackId) }
        }
    }
}
