package com.radiola.service

import android.content.Context
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import androidx.media3.extractor.DefaultExtractorsFactory
import com.radiola.domain.model.Recording
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Контроллер воспроизведения записей эфира (отдельный ExoPlayer, независимый от радио).
 */
@Singleton
class RecordingPlaybackController @Inject constructor(
    @ApplicationContext private val context: Context,
    private val playerController: PlayerController
) {
    private val scope = CoroutineScope(Dispatchers.Main)

    private val _current = MutableStateFlow<Recording?>(null)
    val current: StateFlow<Recording?> = _current

    private val _isPlaying = MutableStateFlow(false)
    val isPlaying: StateFlow<Boolean> = _isPlaying

    private val _positionMs = MutableStateFlow(0L)
    val positionMs: StateFlow<Long> = _positionMs

    private val _durationMs = MutableStateFlow(0L)
    val durationMs: StateFlow<Long> = _durationMs

    // Записи эфира — сырые ADTS-AAC/MP3 без контейнера и индексов перемотки.
    // Включаем CBR-seeking, иначе ExoPlayer считает поток неперематываемым
    // (seekTo не работал, запись всегда стартовала с начала).
    private val extractorsFactory = DefaultExtractorsFactory()
        .setConstantBitrateSeekingEnabled(true)
        .setConstantBitrateSeekingAlwaysEnabled(true)

    private val exoPlayer: ExoPlayer = ExoPlayer.Builder(context)
        .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory))
        .build().apply {
        addListener(object : Player.Listener {
            override fun onIsPlayingChanged(playing: Boolean) {
                _isPlaying.value = playing
            }

            override fun onPlaybackStateChanged(playbackState: Int) {
                when (playbackState) {
                    Player.STATE_READY -> {
                        val dur = duration
                        _durationMs.value = if (dur == C.TIME_UNSET) 0L else dur
                    }
                    Player.STATE_ENDED -> {
                        _isPlaying.value = false
                        _positionMs.value = _durationMs.value
                    }
                    else -> Unit
                }
            }
        })
    }

    private var positionPollingJob: Job? = null

    init {
        // Цикл обновления позиции каждые 500мс
        positionPollingJob = scope.launch {
            while (isActive) {
                if (exoPlayer.isPlaying) {
                    _positionMs.value = exoPlayer.currentPosition
                    val dur = exoPlayer.duration
                    if (dur != C.TIME_UNSET) {
                        _durationMs.value = dur
                    }
                }
                delay(500)
            }
        }
    }

    /**
     * Начать воспроизведение записи. Сначала останавливает радио.
     */
    fun play(recording: Recording) {
        // Останавливаем радиоплеер
        playerController.pause()
        playerController.stop()

        _current.value = recording
        _positionMs.value = 0L
        _durationMs.value = recording.duration ?: 0L

        val mediaItem = MediaItem.fromUri(android.net.Uri.fromFile(File(recording.filePath)))
        exoPlayer.setMediaItem(mediaItem)
        exoPlayer.prepare()
        exoPlayer.play()
    }

    /** Переключить паузу/воспроизведение. */
    fun togglePlayPause() {
        if (exoPlayer.isPlaying) {
            exoPlayer.pause()
        } else {
            exoPlayer.play()
        }
    }

    /** Перейти к позиции в мс. */
    fun seekTo(ms: Long) {
        val target = ms.coerceIn(0L, _durationMs.value.coerceAtLeast(1L))
        exoPlayer.seekTo(target)
        _positionMs.value = target
    }

    /** Перемотать на deltaMs (может быть отрицательным). */
    fun seekBy(deltaMs: Long) {
        seekTo(_positionMs.value + deltaMs)
    }

    /** Остановить воспроизведение и сбросить текущую запись. */
    fun stop() {
        exoPlayer.stop()
        _current.value = null
        _isPlaying.value = false
        _positionMs.value = 0L
        _durationMs.value = 0L
    }
}
