package com.radiola.ui.player

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.radiola.domain.model.DeeplinkService
import com.radiola.domain.model.Station
import com.radiola.domain.model.StreamQuality
import com.radiola.domain.model.Track
import com.radiola.domain.repository.SettingsRepository
import com.radiola.domain.repository.StationRepository
import com.radiola.domain.repository.NowPlayingRepository
import com.radiola.domain.repository.RecordingRepository
import com.radiola.domain.usecase.GetNowPlayingUseCase
import com.radiola.domain.usecase.GetStationsUseCase
import com.radiola.domain.usecase.SearchTrackInServiceUseCase
import com.radiola.domain.repository.TrackHistoryRepository
import com.radiola.domain.usecase.ToggleFavoriteUseCase
import com.radiola.domain.usecase.auth.PushHistoryUseCase
import com.radiola.service.PlayerController
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class PlayerViewModel @Inject constructor(
    private val playerController: PlayerController,
    private val stationRepository: StationRepository,
    private val nowPlayingRepository: NowPlayingRepository,
    private val getStationsUseCase: GetStationsUseCase,
    private val getNowPlayingUseCase: GetNowPlayingUseCase,
    private val searchTrackInServiceUseCase: SearchTrackInServiceUseCase,
    private val toggleFavoriteUseCase: ToggleFavoriteUseCase,
    private val trackHistoryRepository: TrackHistoryRepository,
    private val settingsRepository: SettingsRepository,
    private val recordingRepository: RecordingRepository,
    private val pushHistoryUseCase: PushHistoryUseCase,
    private val loveStreamResolver: com.radiola.data.remote.LoveStreamResolver,
    private val recordingPlaybackController: com.radiola.service.RecordingPlaybackController
) : ViewModel() {

    val isPlaying: StateFlow<Boolean> = playerController.isPlaying
    val currentStationPrefix: StateFlow<String?> = playerController.currentStationPrefix
    val spectrum: StateFlow<FloatArray> = playerController.spectrum

    val visualizerStyle: StateFlow<String> = settingsRepository.getVisualizerStyle()
        .stateIn(viewModelScope, kotlinx.coroutines.flow.SharingStarted.WhileSubscribed(5000), "bars_center")

    private val _currentStation = MutableStateFlow<Station?>(null)
    val currentStation: StateFlow<Station?> = _currentStation.asStateFlow()

    private val _currentTrack = MutableStateFlow<Track?>(null)
    val currentTrack: StateFlow<Track?> = _currentTrack.asStateFlow()

    private val _enabledServices = MutableStateFlow<List<DeeplinkService>>(emptyList())
    val enabledServices: StateFlow<List<DeeplinkService>> = _enabledServices.asStateFlow()

    private val _stations = MutableStateFlow<List<Station>>(emptyList())
    val stations: StateFlow<List<Station>> = _stations.asStateFlow()

    private val _playlist = MutableStateFlow<List<Station>>(emptyList())
    val playlist: StateFlow<List<Station>> = _playlist.asStateFlow()

    // Выбранное качество текущей станции (битрейт). null — у станции нет вариантов.
    private val _currentQuality = MutableStateFlow<StreamQuality?>(null)
    val currentQuality: StateFlow<StreamQuality?> = _currentQuality.asStateFlow()

    // Предпочитаемый битрейт пользователя (0 = авто/по умолчанию станции).
    private var preferredBitrate: Int = 0

    val isRecording: StateFlow<Boolean> = recordingRepository.isRecording

    // Таймер сна: оставшееся время в мс (null = выключен).
    val sleepRemainingMs: StateFlow<Long?> = playerController.sleepRemainingMs

    fun startSleepTimer(minutes: Int, sound: com.radiola.service.SleepSound? = null) =
        playerController.startSleepTimer(minutes * 60_000L, sound)
    fun cancelSleepTimer() = playerController.cancelSleepTimer()

    // Спектр (FFT) считаем только пока открыт плеер — экономия батареи в фоне.
    fun setSpectrumActive(active: Boolean) = playerController.setSpectrumActive(active)

    private var nowPlayingJob: Job? = null

    init {
        playerController.onSkipToNext = { playNext() }
        playerController.onSkipToPrevious = { playPrevious() }
        viewModelScope.launch {
            getStationsUseCase().collect { _stations.value = it }
        }
        viewModelScope.launch {
            settingsRepository.getEnabledDeeplinkServices().collect { ids ->
                _enabledServices.value = DeeplinkService.entries.filter { it.serviceId in ids }
            }
        }
        viewModelScope.launch {
            settingsRepository.getPreferredBitrate().collect { preferredBitrate = it }
        }
        viewModelScope.launch {
            _currentTrack
                .filterNotNull()
                .distinctUntilChanged()
                .collect { track ->
                    trackHistoryRepository.addTrack(track)
                }
        }
    }

    fun play(station: Station, playlist: List<Station>? = null) {
        // Глушим плеер записи, если он играл — иначе два ExoPlayer'а конфликтуют
        // (радио не стартует, запись зависает без управления).
        recordingPlaybackController.stop()
        _currentStation.value = station
        _currentTrack.value = null
        _playlist.value = playlist ?: _stations.value
        // Выбираем стартовое качество: предпочтение пользователя → совпадение с
        // потоком по умолчанию → высшее. Если вариантов нет — играем как есть.
        val quality = pickInitialQuality(station)
        _currentQuality.value = quality
        val streamUrl = quality?.url ?: station.streamUrl
        // Love Radio: подставляем сессионный UID (иначе поток отдаёт заглушку).
        // Для остальных resolve вернёт URL как есть.
        viewModelScope.launch {
            val url = loveStreamResolver.resolve(streamUrl)
            playerController.play(url, station.prefix, station.name, station.id)
        }
        viewModelScope.launch { pushHistoryUseCase(station.id) }
        nowPlayingJob?.cancel()
        nowPlayingJob = viewModelScope.launch {
            // Поллинг now-playing — ТОЛЬКО пока играем. collectLatest отменяет
            // внутренний цикл при паузе (иначе на паузе радио зря дёргали сеть
            // каждые 5с → батарея + лишняя нагрузка на бэкенд).
            launch {
                playerController.isPlaying.collectLatest { playing ->
                    if (!playing) return@collectLatest
                    while (true) {
                        nowPlayingRepository.refreshNowPlaying()
                        delay(5_000)
                    }
                }
            }
            // Collect now playing for this station (API has priority: covers + accurate metadata)
            launch {
                getNowPlayingUseCase(station.id)
                    .distinctUntilChanged()
                    .collect { track ->
                        if (track != null) {
                            _currentTrack.value = track
                            // Нет обложки — обогащаем приоритетно (играет прямо сейчас).
                            if (track.coverUrl.isNullOrBlank()) {
                                nowPlayingRepository.enrichCoverNow(track)
                            }
                            playerController.updateMetadata(
                                track.song,
                                track.artist,
                                track.coverUrl ?: "",
                                station.name
                            )
                        }
                    }
            }
            // Fallback: Icy metadata from stream for stations not in Record API
            launch {
                playerController.icyTitle
                    .filterNotNull()
                    .distinctUntilChanged()
                    .collect { icyTitle ->
                        // Only use Icy if no API track is currently active
                        if (_currentTrack.value == null) {
                            val track = parseIcyTitle(icyTitle)
                            if (track != null) {
                                _currentTrack.value = track
                                playerController.updateMetadata(
                                    track.song,
                                    track.artist,
                                    "",
                                    station.name
                                )
                            }
                        }
                    }
            }
        }
    }

    /** Стартовое качество станции с учётом предпочтения пользователя. */
    private fun pickInitialQuality(station: Station): StreamQuality? {
        val list = station.qualities
        if (list.size < 2) return null
        return list.firstOrNull { it.bitrate == preferredBitrate }
            ?: list.firstOrNull { it.url == station.streamUrl }
            ?: list.first()
    }

    /** Переключить качество текущей станции на лету (без сброса now-playing). */
    fun selectQuality(quality: StreamQuality) {
        val station = _currentStation.value ?: return
        if (_currentQuality.value?.bitrate == quality.bitrate) return
        _currentQuality.value = quality
        preferredBitrate = quality.bitrate
        viewModelScope.launch { settingsRepository.setPreferredBitrate(quality.bitrate) }
        viewModelScope.launch {
            val url = loveStreamResolver.resolve(quality.url)
            playerController.changeStream(url)
        }
    }

    private fun parseIcyTitle(title: String?): Track? {
        if (title.isNullOrBlank()) return null
        val separators = listOf(" - ", " — ", " – ")
        for (sep in separators) {
            val parts = title.split(sep, limit = 2)
            if (parts.size == 2) {
                return Track(
                    artist = parts[0].trim(),
                    song = parts[1].trim(),
                    coverUrl = null,
                    stationName = _currentStation.value?.name ?: ""
                )
            }
        }
        // No separator found: treat entire string as song title
        return Track(
            artist = "",
            song = title.trim(),
            coverUrl = null,
            stationName = _currentStation.value?.name ?: ""
        )
    }

    fun pause() {
        playerController.pause()
    }

    fun resume() {
        playerController.play()
    }

    fun togglePlayPause() {
        if (isPlaying.value) pause() else resume()
    }

    fun playNext() {
        val current = _currentStation.value ?: return
        val list = _playlist.value
        if (list.isEmpty()) return
        val index = list.indexOfFirst { it.id == current.id }
        val next = list.getOrNull((index + 1).mod(list.size))
        next?.let { play(it, list) }
    }

    fun playPrevious() {
        val current = _currentStation.value ?: return
        val list = _playlist.value
        if (list.isEmpty()) return
        val index = list.indexOfFirst { it.id == current.id }
        val prev = list.getOrNull((index - 1).mod(list.size))
        prev?.let { play(it, list) }
    }

    fun getDeeplinkUrl(track: Track, service: DeeplinkService): String {
        return searchTrackInServiceUseCase(track, service)
    }

    fun toggleFavorite(station: Station) {
        viewModelScope.launch {
            toggleFavoriteUseCase(station)
        }
    }

    fun toggleRecording() {
        viewModelScope.launch {
            if (recordingRepository.isRecording.value) {
                recordingRepository.stopRecording()
            } else {
                val station = _currentStation.value ?: return@launch
                recordingRepository.startRecording(station, _currentTrack.value)
            }
        }
    }
}
