package com.radiola.service

import android.content.Context
import android.media.AudioDeviceCallback
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.net.Uri
import androidx.media3.common.AudioAttributes
import androidx.media3.common.C
import androidx.media3.common.ForwardingPlayer
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import androidx.media3.common.Metadata
import androidx.media3.common.Player
import android.util.Log
import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.DefaultDataSource
import androidx.media3.datasource.DefaultHttpDataSource
import androidx.media3.exoplayer.DefaultRenderersFactory
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.audio.AudioSink
import androidx.media3.exoplayer.audio.DefaultAudioSink
import androidx.media3.exoplayer.audio.TeeAudioProcessor
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import androidx.media3.extractor.metadata.icy.IcyInfo
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
import javax.inject.Singleton

@UnstableApi
@Singleton
class PlayerController @Inject constructor(
    @ApplicationContext context: Context
) {
    // Анализатор спектра реального звука — для «живого» эквалайзера.
    private val spectrumAnalyzer = AudioSpectrumAnalyzer()
    val spectrum: StateFlow<FloatArray> = spectrumAnalyzer.spectrum

    // RenderersFactory, который вставляет наш tee-процессор в аудио-конвейер
    // (читает декодированный PCM, не меняя звук).
    private val renderersFactory = object : DefaultRenderersFactory(context) {
        override fun buildAudioSink(
            context: Context,
            enableFloatOutput: Boolean,
            enableAudioTrackPlaybackParams: Boolean,
        ): AudioSink {
            return DefaultAudioSink.Builder(context)
                .setEnableFloatOutput(enableFloatOutput)
                .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams)
                .setAudioProcessors(arrayOf(TeeAudioProcessor(spectrumAnalyzer)))
                .build()
        }
    }
    private val _isPlaying = MutableStateFlow(false)
    val isPlaying: StateFlow<Boolean> = _isPlaying

    private val _currentStationPrefix = MutableStateFlow<String?>(null)
    val currentStationPrefix: StateFlow<String?> = _currentStationPrefix

    // Id играющей станции — для подсветки активной карточки в списке.
    private val _currentStationId = MutableStateFlow<Int?>(null)
    val currentStationId: StateFlow<Int?> = _currentStationId

    private val _icyTitle = MutableStateFlow<String?>(null)
    val icyTitle: StateFlow<String?> = _icyTitle.asStateFlow()

    private val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager

    var onSkipToNext: (() -> Unit)? = null
    var onSkipToPrevious: (() -> Unit)? = null

    private val audioDeviceCallback = object : AudioDeviceCallback() {
        override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>?) {
            val addedPlayback = addedDevices?.any { it.isPlaybackDevice() } == true
            if (addedPlayback && _currentStationPrefix.value != null && !exoPlayer.isPlaying) {
                Log.d("PlayerController", "Playback device connected → resume")
                exoPlayer.play()
            }
        }

        override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>?) {
            val removedPlayback = removedDevices?.any { it.isPlaybackDevice() } == true
            if (removedPlayback && exoPlayer.isPlaying) {
                Log.d("PlayerController", "Playback device removed → pause")
                exoPlayer.pause()
            }
        }
    }

    private fun AudioDeviceInfo.isPlaybackDevice(): Boolean {
        return isSink && (
            type == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP ||
            type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES ||
            type == AudioDeviceInfo.TYPE_WIRED_HEADSET ||
            type == AudioDeviceInfo.TYPE_USB_HEADSET ||
            type == AudioDeviceInfo.TYPE_USB_DEVICE ||
            type == AudioDeviceInfo.TYPE_BLE_HEADSET ||
            type == AudioDeviceInfo.TYPE_BLE_SPEAKER
        )
    }

    // HTTP-источник с разрешёнными кросс-протокольными редиректами (http→https):
    // многие станции отдают 301 c http на https, без этого ExoPlayer их не играет.
    private val mediaSourceFactory = DefaultMediaSourceFactory(
        DefaultDataSource.Factory(
            context,
            DefaultHttpDataSource.Factory()
                .setAllowCrossProtocolRedirects(true)
        )
    )

    private val exoPlayer: ExoPlayer = ExoPlayer.Builder(context)
        .setRenderersFactory(renderersFactory)
        .setMediaSourceFactory(mediaSourceFactory)
        .setAudioAttributes(
            AudioAttributes.Builder()
                .setUsage(C.USAGE_MEDIA)
                .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
                .build(),
            true
        )
        .setHandleAudioBecomingNoisy(true)
        .build()
        .apply {
            addListener(object : Player.Listener {
                override fun onIsPlayingChanged(playing: Boolean) {
                    _isPlaying.value = playing
                }

                override fun onMediaMetadataChanged(mediaMetadata: MediaMetadata) {
                    val title = mediaMetadata.title?.toString()
                    if (!title.isNullOrBlank()) {
                        Log.d("PlayerController", "MediaMetadata title: $title")
                        _icyTitle.value = title
                    }
                }

                override fun onMetadata(metadata: Metadata) {
                    Log.d("PlayerController", "onMetadata called, length=${metadata.length()}")
                    for (i in 0 until metadata.length()) {
                        val entry = metadata.get(i)
                        Log.d("PlayerController", "Metadata entry[$i]: ${entry::class.java.simpleName}")
                        when (entry) {
                            is IcyInfo -> {
                                Log.d("PlayerController", "IcyInfo title='${entry.title}', url='${entry.url}', raw='${entry.rawMetadata}'")
                                entry.title?.let {
                                    if (it.isNotBlank()) {
                                        _icyTitle.value = it
                                    }
                                }
                            }
                        }
                    }
                }
            })
        }

    val player: Player = object : ForwardingPlayer(exoPlayer) {
        override fun getAvailableCommands(): Player.Commands {
            return super.getAvailableCommands()
                .buildUpon()
                .add(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
                .add(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
                .build()
        }

        override fun seekToNextMediaItem() {
            onSkipToNext?.invoke() ?: super.seekToNextMediaItem()
        }

        override fun seekToPreviousMediaItem() {
            onSkipToPrevious?.invoke() ?: super.seekToPreviousMediaItem()
        }
    }

    init {
        audioManager.registerAudioDeviceCallback(audioDeviceCallback, null)
    }

    fun play(url: String, stationPrefix: String, stationName: String, stationId: Int? = null) {
        Log.d("PlayerController", "play() called with url=$url prefix=$stationPrefix")
        _currentStationId.value = stationId
        _icyTitle.value = null
        val mediaItem = MediaItem.Builder()
            .setUri(url)
            .setMediaMetadata(
                MediaMetadata.Builder()
                    .setTitle(stationName)
                    .setArtist("")
                    .build()
            )
            .build()
        exoPlayer.setMediaItem(mediaItem)
        exoPlayer.prepare()
        exoPlayer.play()
        _currentStationPrefix.value = stationPrefix
    }

    /** Сменить URL потока (переключение качества) без потери текущих метаданных/обложки. */
    fun changeStream(url: String) {
        Log.d("PlayerController", "changeStream() url=$url")
        val keepMetadata = exoPlayer.currentMediaItem?.mediaMetadata
        _icyTitle.value = null
        val builder = MediaItem.Builder().setUri(url)
        if (keepMetadata != null) builder.setMediaMetadata(keepMetadata)
        exoPlayer.setMediaItem(builder.build())
        exoPlayer.prepare()
        exoPlayer.play()
    }

    fun updateMetadata(song: String, artist: String, coverUrl: String, stationName: String) {
        val currentMediaItem = exoPlayer.currentMediaItem ?: return
        val artworkUri = coverUrl.takeIf { it.isNotBlank() }?.let { Uri.parse(it) }
        val updatedMediaItem = currentMediaItem.buildUpon()
            .setMediaMetadata(
                MediaMetadata.Builder()
                    .setTitle(song)
                    .setArtist("$artist  •  $stationName")
                    .setAlbumTitle(stationName)
                    .setArtworkUri(artworkUri)
                    .build()
            )
            .build()
        exoPlayer.replaceMediaItem(0, updatedMediaItem)
    }

    fun pause() {
        exoPlayer.pause()
    }

    fun play() {
        exoPlayer.play()
    }

    fun stop() {
        exoPlayer.stop()
        _currentStationPrefix.value = null
        _currentStationId.value = null
    }

    fun release() {
        audioManager.unregisterAudioDeviceCallback(audioDeviceCallback)
        exoPlayer.release()
    }
}
