package xyz.cottongin.radio247.ui.screens.nowplaying

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import xyz.cottongin.radio247.RadioApplication
import xyz.cottongin.radio247.audio.IcyMetadata
import xyz.cottongin.radio247.service.PlaybackState
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

class NowPlayingViewModel(application: Application) : AndroidViewModel(application) {
    private val app = application as RadioApplication
    val controller = app.controller
    val playbackState = controller.state
    val estimatedLatencyMs = controller.estimatedLatencyMs
    val stayConnected = app.preferences.stayConnected.stateIn(
        viewModelScope,
        SharingStarted.Lazily,
        false
    )
    val bufferMs = app.preferences.bufferMs.stateIn(
        viewModelScope,
        SharingStarted.Lazily,
        0
    )

    private val _displayArtworkUrl = MutableStateFlow<String?>(null)
    val displayArtworkUrl: StateFlow<String?> = _displayArtworkUrl.asStateFlow()

    private val _displayMetadata = MutableStateFlow<IcyMetadata?>(null)
    val displayMetadata: StateFlow<IcyMetadata?> = _displayMetadata.asStateFlow()

    val sessionElapsed: StateFlow<Long>
    val connectionElapsed: StateFlow<Long>

    private var artworkResolveJob: Job? = null
    private var displayStationId: Long? = null

    init {
        val ticker = flow {
            while (true) {
                emit(System.currentTimeMillis())
                delay(1000)
            }
        }

        sessionElapsed = combine(ticker, playbackState) { now, state ->
            when (state) {
                is PlaybackState.Connecting -> now - state.sessionStartedAt
                is PlaybackState.Playing -> now - state.sessionStartedAt
                is PlaybackState.Paused -> now - state.sessionStartedAt
                is PlaybackState.Reconnecting -> now - state.sessionStartedAt
                else -> 0L
            }
        }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0L)

        connectionElapsed = combine(ticker, playbackState) { now, state ->
            when (state) {
                is PlaybackState.Playing -> now - state.connectionStartedAt
                else -> 0L
            }
        }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0L)

        viewModelScope.launch {
            playbackState.collect { state ->
                val (station, metadata) = when (state) {
                    is PlaybackState.Connecting -> state.station to null
                    is PlaybackState.Playing -> state.station to state.metadata
                    is PlaybackState.Paused -> state.station to state.metadata
                    is PlaybackState.Reconnecting -> state.station to state.metadata
                    PlaybackState.Idle -> null to null
                }
                when (state) {
                    is PlaybackState.Connecting -> {
                        artworkResolveJob?.cancel()
                        if (station?.id != displayStationId) {
                            displayStationId = station?.id
                            _displayMetadata.value = null
                            _displayArtworkUrl.value = station?.defaultArtworkUrl
                        }
                    }
                    is PlaybackState.Playing,
                    is PlaybackState.Paused,
                    is PlaybackState.Reconnecting -> {
                        val stationChanged = station?.id != displayStationId
                        val trackChanged = stationChanged || isTrackChange(metadata, _displayMetadata.value)

                        if (stationChanged) {
                            displayStationId = station?.id
                        }

                        if (trackChanged && metadata != null) {
                            artworkResolveJob?.cancel()
                            val pendingMetadata = metadata
                            artworkResolveJob = viewModelScope.launch {
                                val url = station?.let { s ->
                                    app.albumArtResolver.resolve(
                                        artist = pendingMetadata.artist,
                                        title = pendingMetadata.title,
                                        icyStreamUrl = pendingMetadata.streamUrl,
                                        stationArtworkUrl = s.defaultArtworkUrl
                                    )
                                }
                                _displayArtworkUrl.value = url
                                _displayMetadata.value = pendingMetadata
                            }
                        } else if (stationChanged) {
                            artworkResolveJob?.cancel()
                            _displayMetadata.value = null
                            _displayArtworkUrl.value = station?.defaultArtworkUrl
                        }
                    }
                    PlaybackState.Idle -> {
                        artworkResolveJob?.cancel()
                        displayStationId = null
                        _displayMetadata.value = null
                        _displayArtworkUrl.value = null
                    }
                }
            }
        }
    }

    private fun isTrackChange(new: IcyMetadata?, old: IcyMetadata?): Boolean {
        if (new == null && old == null) return false
        if (new == null || old == null) return true
        return new.artist != old.artist || new.title != old.title
    }

    fun stop() {
        controller.stop()
    }

    fun pause() {
        controller.pause()
    }

    fun resume() {
        val state = playbackState.value
        if (state is PlaybackState.Paused) {
            controller.play(state.station)
        }
    }

    fun skipAhead() {
        controller.seekToLive()
    }

    fun toggleStayConnected() {
        viewModelScope.launch {
            val current = stayConnected.value
            app.preferences.setStayConnected(!current)
        }
    }

    fun setBufferMs(ms: Int) {
        viewModelScope.launch {
            app.preferences.setBufferMs(ms)
        }
    }
}
