package com.yaros.RadioUrl

import android.app.ActivityManager
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.media.AudioManager
import android.net.Uri
import android.os.BatteryManager
import android.os.Binder
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.media3.common.AudioAttributes
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import androidx.media3.common.Metadata
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.DataSource
import androidx.media3.datasource.DefaultDataSource
import androidx.media3.datasource.okhttp.OkHttpDataSource
import androidx.media3.exoplayer.DefaultLoadControl
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.SeekParameters
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import androidx.media3.session.DefaultMediaNotificationProvider
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSessionService
import androidx.media3.session.SessionCommand
import androidx.media3.session.SessionResult
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import com.yaros.RadioUrl.helpers.AudioHelper
import com.yaros.RadioUrl.helpers.NetworkHelper
import com.yaros.RadioUrl.helpers.NetworkMonitor
import com.yaros.RadioUrl.helpers.PreferencesHelper
import com.yaros.RadioUrl.helpers.VolumeSettingsHelper
import timber.log.Timber

/**
 * ВАЖНЫЕ АРХИТЕКТУРНЫЕ ИЗМЕНЕНИЯ ПО СРАВНЕНИЮ С ПРЕДЫДУЩЕЙ ВЕРСИЕЙ:
 *
 * 1. Убран PlayerNotificationManager и вся связанная с ним рефлексия для привязки
 *    MediaSession token к уведомлению. MediaSessionService имеет встроенный
 *    MediaNotification.Provider, который сам создаёт уведомление, сам вызывает
 *    startForeground() и корректно регистрирует сессию в системе (шторка,
 *    блокировка экрана, Android Auto, Wear OS). Смешивание старого
 *    PlayerNotificationManager с MediaSessionService — известная причина того,
 *    что система (особенно на новых версиях Android) не распознаёт активное
 *    воспроизведение.
 *
 * 2. Аудиофокус больше не обрабатывается вручную. setAudioAttributes(..., true)
 *    включает handleAudioFocus у ExoPlayer — теперь ExoPlayer сам запрашивает
 *    и отдаёт фокус ПЕРЕД любым player.play(), независимо от того, откуда
 *    пришла команда: из нашего кода, из системного уведомления, с Bluetooth-
 *    гарнитуры, из Android Auto или от Ассистента. Раньше ручной запрос фокуса
 *    происходил только внутри playRadio() / кастомной SessionCommand("play"),
 *    а стандартные системные команды play/pause идут через MediaSession
 *    напрямую на плеер, минуя onCustomCommand — поэтому фокус для них никогда
 *    не запрашивался.
 */
@UnstableApi
class PlayerService : MediaSessionService(), Player.Listener {
    private var exoPlayer: ExoPlayer? = null
    private lateinit var mediaSession: MediaSession
    private lateinit var notificationManager: NotificationManager
    private lateinit var networkMonitor: NetworkMonitor

    inner class LocalBinder : Binder() {
        val service: PlayerService
            get() = this@PlayerService
    }

    override fun onCreate() {
        super.onCreate()
        initComponents()
        setupPlayer()
        setupMediaSession()
        setupMediaNotificationProvider()
        setupNetworkMonitor()
        // Намеренно НЕ вызываем startForeground() вручную здесь.
        // MediaSessionService сам переведёт сервис в foreground, как только
        // начнётся подготовка/воспроизведение медиа (см. playRadio()).
    }

    private fun initComponents() {
        notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        networkMonitor = NetworkMonitor(this)
    }

    private fun createDataSourceFactory(): DataSource.Factory {
        return try {
            OkHttpDataSource.Factory(NetworkHelper.client)
                .setUserAgent("SoundWaveRadio/1.0")
        } catch (e: Exception) {
            Timber.tag("PlayerService").e("Error creating data source factory: ${e.message}")
            OkHttpDataSource.Factory(NetworkHelper.client)
                .setUserAgent("SoundWaveRadio/1.0")
        }
    }

    private fun setupPlayer() {
        try {
            val dataSourceFactory = DefaultDataSource.Factory(
                this,
                createDataSourceFactory()
            )
            exoPlayer = ExoPlayer.Builder(this)
                .setHandleAudioBecomingNoisy(true) // Автоматически останавливает при отключении наушников
                .setWakeMode(C.WAKE_MODE_NETWORK)
                .setLoadControl(
                    DefaultLoadControl.Builder()
                        .setBufferDurationsMs(
                            MIN_BUFFER_MS,
                            MAX_BUFFER_MS,
                            PLAYBACK_BUFFER_MS,
                            REBUFFER_MS
                        )
                        .setPrioritizeTimeOverSizeThresholds(true)
                        .build()
                )
                .setMediaSourceFactory(
                    DefaultMediaSourceFactory(dataSourceFactory)
                )
                .build()
                .apply {
                    addListener(this@PlayerService)
                    setAudioAttributes(
                        AudioAttributes.Builder()
                            .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
                            .setUsage(C.USAGE_MEDIA)
                            .build(),
                        true // handleAudioFocus = true — фокусом управляет сам ExoPlayer
                    )
                    playWhenReady = true
                    setSeekParameters(SeekParameters.CLOSEST_SYNC)
                    trackSelectionParameters = trackSelectionParameters
                        .buildUpon()
                        .setForceHighestSupportedBitrate(true)
                        .build()
                }
            Timber.tag("PlayerService").i("ExoPlayer initialized successfully")
        } catch (e: Exception) {
            Timber.tag("PlayerService").e("Error setting up player: ${e.message}")
            throw e
        }
    }

    private fun setupMediaSession() {
        mediaSession = MediaSession.Builder(this, exoPlayer!!)
            .setSessionActivity(getSessionActivity())
            .setCallback(MediaSessionCallback())
            .build()
    }

    /**
     * Настраивает встроенный провайдер уведомлений MediaSessionService.
     * Он сам создаст notification channel, сам построит MediaStyle-уведомление,
     * сам подгрузит artwork по MediaMetadata.artworkUri и сам вызовет
     * startForeground()/stopForeground() в нужные моменты жизненного цикла.
     */
    private fun setupMediaNotificationProvider() {
        setMediaNotificationProvider(
            DefaultMediaNotificationProvider.Builder(this)
                .setChannelId(NOTIFICATION_CHANNEL_ID)
                .setChannelName(R.string.notification_info)
                .setNotificationId(NOTIFICATION_ID)
                .build()
        )
    }

    private fun setupNetworkMonitor() {
        networkMonitor.addListener(object : NetworkMonitor.Listener {
            override fun onNetworkAvailable() {
                Handler(Looper.getMainLooper()).post {
                    exoPlayer?.prepare()
                    exoPlayer?.play()
                }
            }

            override fun onNetworkLost() {
                Handler(Looper.getMainLooper()).post {
                    exoPlayer?.pause()
                }
            }
        })
        networkMonitor.start()
    }

    private fun getSessionActivity(): PendingIntent {
        val intent = Intent(this, MainActivity::class.java).apply {
            addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
        }

        val pendingIntentFlags = when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
            }
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            }
            else -> {
                PendingIntent.FLAG_UPDATE_CURRENT
            }
        }

        return PendingIntent.getActivity(
            this,
            0,
            intent,
            pendingIntentFlags
        )
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
        return if (::mediaSession.isInitialized) mediaSession else null
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)

        intent?.let {
            when (it.action) {
                Keys.ACTION_PLAY_STREAM -> {
                    val streamUri = it.getStringExtra(Keys.EXTRA_STREAM_URI)
                    val stationName = it.getStringExtra(Keys.EXTRA_STATION_NAME)
                    val stationImage = it.getStringExtra(Keys.EXTRA_STATION_IMAGE)
                    val stationUuid = it.getStringExtra(Keys.EXTRA_STATION_UUID)

                    Timber.tag("PlayerService").i("Received ACTION_PLAY_STREAM: name=$stationName, uri=$streamUri, uuid=$stationUuid")

                    if (!streamUri.isNullOrBlank()) {
                        val artUri = if (!stationImage.isNullOrBlank()) {
                            Uri.parse(stationImage)
                        } else {
                            null
                        }

                        playRadio(
                            url = streamUri,
                            title = stationName ?: "Radio Station",
                            artist = getString(R.string.app_name),
                            artUri = artUri,
                            stationUuid = stationUuid
                        )
                    } else {
                        Timber.tag("PlayerService").e("Stream URI is null or blank")
                    }
                }
            }
        }

        return START_STICKY
    }

    override fun onTaskRemoved(rootIntent: Intent?) {
        super.onTaskRemoved(rootIntent)
        Timber.tag("PlayerService").i("Task removed, isPlaying: ${exoPlayer?.isPlaying}")

        if (exoPlayer?.isPlaying != true) {
            Timber.tag("PlayerService").i("Stopping service - not playing")
            stopServiceGracefully()
        } else {
            Timber.tag("PlayerService").i("Keeping service alive - still playing")
        }
    }

    override fun onDestroy() {
        releaseResources()
        super.onDestroy()
    }

    private fun releaseResources() {
        try {
            retryHandler.removeCallbacksAndMessages(null)
            retryAttempts = 0
            networkMonitor.stop()

            if (::mediaSession.isInitialized) {
                mediaSession.release()
            }
            exoPlayer?.release()
            exoPlayer = null
        } catch (e: Exception) {
            Timber.tag("PlayerService").e("Error releasing resources: ${e.message}")
        }
    }

    private inner class MediaSessionCallback : MediaSession.Callback {
        override fun onConnect(
            session: MediaSession,
            controller: MediaSession.ControllerInfo
        ): MediaSession.ConnectionResult {
            val baseResult = super.onConnect(session, controller)
            val customCommands = listOf(
                SessionCommand(Keys.CMD_START_SLEEP_TIMER, Bundle.EMPTY),
                SessionCommand(Keys.CMD_CANCEL_SLEEP_TIMER, Bundle.EMPTY),
                SessionCommand(Keys.CMD_REQUEST_SLEEP_TIMER_REMAINING, Bundle.EMPTY),
                SessionCommand(Keys.CMD_REQUEST_METADATA_HISTORY, Bundle.EMPTY)
            )
            val newSessionCommands = baseResult.availableSessionCommands
                .buildUpon()
                .apply { customCommands.forEach { add(it) } }
                .build()
            return MediaSession.ConnectionResult.accept(
                newSessionCommands,
                baseResult.availablePlayerCommands
            )
        }

        override fun onCustomCommand(
            session: MediaSession,
            controller: MediaSession.ControllerInfo,
            customCommand: SessionCommand,
            args: Bundle
        ): ListenableFuture<SessionResult> {
            when (customCommand.customAction) {
                "play" -> {
                    if (isAppInForeground()) {
                        // Аудиофокус запросит сам ExoPlayer при вызове play()
                        exoPlayer?.play()
                        Timber.tag("PlayerService").i("Play command received")
                    } else {
                        Timber.tag("PlayerService").w("Невозможно воспроизвести из фонового режима")
                    }
                }
                "pause" -> exoPlayer?.pause()
                "stop" -> stopServiceGracefully()
                Keys.CMD_REQUEST_METADATA_HISTORY -> {
                    val metadataHistory = PreferencesHelper.loadMetadataHistory()
                    val resultBundle = Bundle().apply {
                        putStringArrayList(Keys.EXTRA_METADATA_HISTORY, ArrayList(metadataHistory))
                    }
                    return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS, resultBundle))
                }
            }
            return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
        }
    }

    override fun onIsPlayingChanged(isPlaying: Boolean) {
        super.onIsPlayingChanged(isPlaying)
        Timber.tag("PlayerService").i("onIsPlayingChanged: $isPlaying")
    }

    override fun onPlaybackStateChanged(playbackState: Int) {
        try {
            when (playbackState) {
                Player.STATE_READY -> {
                    Timber.tag("PlayerService").i("Playback state: READY")
                    retryAttempts = 0 // Reset retry counter on successful playback
                    handlePlaybackReady()
                }
                Player.STATE_ENDED -> {
                    Timber.tag("PlayerService").i("Playback state: ENDED")
                    handlePlaybackEnd()
                }
                Player.STATE_IDLE -> {
                    Timber.tag("PlayerService").i("Playback state: IDLE")
                    if (exoPlayer?.playWhenReady == false) {
                        stopServiceGracefully()
                    }
                }
                Player.STATE_BUFFERING -> {
                    Timber.tag("PlayerService").i("Playback state: BUFFERING")
                    handleBufferingState()
                }
            }
        } catch (e: Exception) {
            Timber.tag("PlayerService").e("Error in onPlaybackStateChanged: ${e.message}")
        }
    }

    override fun onPlayerError(error: PlaybackException) {
        Timber.tag("PlayerService").e("Playback error: ${error.errorCodeName} - ${error.message}")
        when (error.errorCode) {
            PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED -> {
                Timber.tag("PlayerService").e("Network connection failed")
                handlePlaybackError(error)
            }
            PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS -> {
                Timber.tag("PlayerService").e("Bad HTTP status: ${error.message}")
                handlePlaybackError(error)
            }
            PlaybackException.ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE -> {
                Timber.tag("PlayerService").e("Invalid HTTP content type")
                handlePlaybackError(error)
            }
            PlaybackException.ERROR_CODE_TIMEOUT -> {
                Timber.tag("PlayerService").e("Playback timeout")
                handlePlaybackError(error)
            }
            PlaybackException.ERROR_CODE_IO_UNSPECIFIED -> {
                Timber.tag("PlayerService").e("Unspecified IO error")
                handlePlaybackError(error)
            }
            PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED,
            PlaybackException.ERROR_CODE_PARSING_MANIFEST_MALFORMED -> {
                Timber.tag("PlayerService").e("Malformed stream format")
                stopServiceGracefully()
            }
            else -> {
                Timber.tag("PlayerService").e("Unknown playback error: ${error.errorCode}")
                handlePlaybackError(error)
            }
        }
    }

    override fun onMetadata(metadata: Metadata) {
        super.onMetadata(metadata)
        try {
            if (metadata.length() > 0) {
                val metadataString = AudioHelper.getMetadataString(metadata)
                if (metadataString.isNotEmpty()) {
                    Timber.tag("PlayerService").i("Metadata received: $metadataString")

                    val metadataHistory = PreferencesHelper.loadMetadataHistory()

                    if (metadataHistory.isEmpty() || metadataHistory.last() != metadataString) {
                        metadataHistory.add(metadataString)

                        while (metadataHistory.size > Keys.DEFAULT_SIZE_OF_METADATA_HISTORY) {
                            metadataHistory.removeAt(0)
                        }

                        PreferencesHelper.saveMetadataHistory(metadataHistory)
                        Timber.tag("PlayerService").i("Metadata saved to history")
                    }
                }
            }
        } catch (e: Exception) {
            Timber.tag("PlayerService").e("Error processing metadata: ${e.message}")
        }
    }

    private fun handlePlaybackReady() {
        optimizePowerUsage()
    }

    private fun handleBufferingState() {
        exoPlayer?.playWhenReady = true
    }

    fun playRadio(url: String, title: String, artist: String, artUri: Uri?, stationUuid: String? = null) {
        try {
            Timber.tag("PlayerService").i("=== playRadio called ===")
            Timber.tag("PlayerService").i("Title: $title, URL: $url, UUID: $stationUuid")

            if (url.isBlank()) {
                Timber.tag("PlayerService").e("Invalid URL: URL is blank")
                return
            }

            exoPlayer?.apply {
                val targetVolume = if (!stationUuid.isNullOrBlank()) {
                    val stationVolume = VolumeSettingsHelper.getVolume(this@PlayerService, stationUuid)
                    Timber.tag("PlayerService").i("Station has custom volume: $stationVolume (${(stationVolume * 100).toInt()}%)")
                    stationVolume
                } else {
                    Timber.tag("PlayerService").i("No UUID provided, using default volume: 1.0")
                    1.0f
                }

                volume = targetVolume
                Timber.tag("PlayerService").i("Volume set to: $targetVolume before playback")

                stop()
                clearMediaItems()

                val mediaItem = MediaItem.Builder()
                    .setUri(url)
                    .setMediaMetadata(
                        MediaMetadata.Builder()
                            .setTitle(title)
                            .setArtist(artist)
                            .setArtworkUri(artUri)
                            .build()
                    )
                    .build()

                setMediaItem(mediaItem)

                // prepare()+play() — ExoPlayer сам запросит аудиофокус перед стартом
                // благодаря handleAudioFocus=true из setupPlayer()
                prepare()
                play()
            } ?: run {
                Timber.tag("PlayerService").e("ExoPlayer is null, cannot play radio")
            }
        } catch (e: IllegalArgumentException) {
            Timber.tag("PlayerService").e("Invalid URL format: ${e.message}")
        } catch (e: IllegalStateException) {
            Timber.tag("PlayerService").e("Player in invalid state: ${e.message}")
        } catch (e: Exception) {
            Timber.tag("PlayerService").e("Error playing radio: ${e.message}")
            handlePlaybackError(PlaybackException("Error playing radio", e, PlaybackException.ERROR_CODE_UNSPECIFIED))
        }
    }

    private fun stopServiceGracefully() {
        Timber.tag("PlayerService").i("Stopping service gracefully")
        exoPlayer?.stop()
        stopSelf()
    }

    private fun optimizePowerUsage() {
        exoPlayer?.setWakeMode(
            if (isCharging(this)) C.WAKE_MODE_NETWORK
            else C.WAKE_MODE_LOCAL
        )
    }

    private fun handlePlaybackEnd() {
        stopServiceGracefully()
    }

    private fun isAppInForeground(): Boolean {
        val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val appProcesses = activityManager.runningAppProcesses ?: return false
        val packageName = packageName
        return appProcesses.any { process ->
            process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
                    process.processName == packageName
        }
    }

    fun isCharging(context: Context): Boolean {
        val intentFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
        val batteryStatus: Intent? = context.registerReceiver(null, intentFilter)

        val status = batteryStatus?.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
        return status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL
    }

    private var retryAttempts = 0
    private val maxRetryAttempts = 3
    private val retryHandler = Handler(Looper.getMainLooper())

    private fun handlePlaybackError(error: PlaybackException) {
        Timber.tag("PlayerService").e("Playback error: ${error.errorCodeName} - ${error.message}")
        when (error.errorCode) {
            PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
            PlaybackException.ERROR_CODE_TIMEOUT,
            PlaybackException.ERROR_CODE_IO_UNSPECIFIED -> {
                if (retryAttempts < maxRetryAttempts) {
                    retryPlayback()
                } else {
                    Timber.tag("PlayerService").e("Max retry attempts reached, stopping playback")
                    retryAttempts = 0
                    stopServiceGracefully()
                }
            }
            else -> {
                retryAttempts = 0
                stopServiceGracefully()
            }
        }
    }

    private fun retryPlayback() {
        retryAttempts++
        val delayMillis = (retryAttempts * 2000L) // Exponential backoff: 2s, 4s, 6s
        Timber.tag("PlayerService").i("Retrying playback (attempt $retryAttempts/$maxRetryAttempts) in ${delayMillis}ms")

        retryHandler.postDelayed({
            try {
                exoPlayer?.let { player ->
                    if (player.playbackState == Player.STATE_IDLE ||
                        player.playbackState == Player.STATE_ENDED) {
                        player.prepare()
                        player.play()
                    }
                }
            } catch (e: Exception) {
                Timber.tag("PlayerService").e("Error during retry: ${e.message}")
                retryAttempts = 0
                stopServiceGracefully()
            }
        }, delayMillis)
    }

    companion object {
        const val NOTIFICATION_ID = 2031
        const val NOTIFICATION_CHANNEL_ID = "playback_channel"
        private const val MIN_BUFFER_MS = 5000
        private const val MAX_BUFFER_MS = 10000
        private const val PLAYBACK_BUFFER_MS = 2000
        private const val REBUFFER_MS = 5000
    }
}