package com.adealink.weparty.exoplayer

import android.content.Context
import android.os.Build
import androidx.annotation.OptIn
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.Player.STATE_BUFFERING
import androidx.media3.common.util.UnstableApi
import androidx.media3.database.StandaloneDatabaseProvider
import androidx.media3.datasource.DefaultDataSource
import androidx.media3.datasource.DefaultHttpDataSource
import androidx.media3.datasource.cache.CacheDataSource
import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor
import androidx.media3.datasource.cache.SimpleCache
import androidx.media3.exoplayer.DefaultLoadControl
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.source.MediaSource
import androidx.media3.exoplayer.source.ProgressiveMediaSource
import com.adealink.frame.log.Log
import com.adealink.frame.util.AppUtil
import com.adealink.weparty.storage.file.FilePath
import java.io.File
import java.lang.ref.WeakReference

@OptIn(UnstableApi::class)
class ExoPlayerHelper : DefaultLifecycleObserver {

    private var contextRef: WeakReference<Context?> = WeakReference(null)
    private val context: Context?
        get() {
            return contextRef.get()
        }
    private var uri: String? = null
    private var scaleMode: Int? = null
    private var volume: Float? = null
    private var repeatMode = Player.REPEAT_MODE_OFF
    private var playWhenReady = true
    private var player: ExoPlayer? = null

    //Player初始化回调
    private var onPlayerInit: ((player: Player) -> Unit)? = null

    //缓冲导致的播放暂停
    private var onPauseWhenBuffering: ((isPlaying: Boolean) -> Unit)? = null

    private var doOnReadyCallback: (() -> Unit)? = null
    private var onPlayingChangedCallback: ((isPlaying: Boolean) -> Unit)? = null
    private var onErrorCallback: ((error: PlaybackException) -> Unit)? = null

    private var isRelease = false

    fun observe(lifecycle: Lifecycle) {
        lifecycle.addObserver(this)
    }

    fun unObserve(lifecycle: Lifecycle) {
        lifecycle.removeObserver(this)
    }

    fun getPlayer(): ExoPlayer? {
        return player
    }

    class Builder(val context: Context, val onPlayerInit: ((player: Player) -> Unit)) {

        private var uri: String? = null
        private var scaleMode: Int? = null
        private var volume: Float? = null
        private var repeatMode = Player.REPEAT_MODE_OFF
        private var playWhenReady = true

        private var onPauseWhenBuffering: ((isPlaying: Boolean) -> Unit)? = null
        private var onPlayingChangedCallback: ((isPlaying: Boolean) -> Unit)? = null
        private var doOnReadyCallback: (() -> Unit)? = null
        private var onErrorCallback: ((error: PlaybackException) -> Unit)? = null

        fun uri(uri: String): Builder {
            this.uri = uri
            return this
        }

        fun scaleMode(mode: Int): Builder {
            this.scaleMode = mode
            return this
        }

        fun volume(volume: Float): Builder {
            this.volume = volume
            return this
        }

        fun repeatMode(mode: Int): Builder {
            this.repeatMode = mode
            return this
        }

        fun playWhenReady(playWhenReady: Boolean): Builder {
            this.playWhenReady = playWhenReady
            return this
        }

        fun playListener(
            doOnReadyCallback: (() -> Unit)? = null,
            onPlayingChangedCallback: ((isPlaying: Boolean) -> Unit)? = null,
            onErrorCallback: ((error: PlaybackException) -> Unit)? = null
        ): Builder {
            this.doOnReadyCallback = doOnReadyCallback
            this.onPlayingChangedCallback = onPlayingChangedCallback
            this.onErrorCallback = onErrorCallback
            return this
        }

        fun onPauseWhenBuffering(listener: ((isPlaying: Boolean) -> Unit)?): Builder {
            this.onPauseWhenBuffering = listener
            return this
        }

        fun build(): ExoPlayerHelper {
            val helper = ExoPlayerHelper().apply {
                this.contextRef = WeakReference(this@Builder.context)
                this.onPlayerInit = this@Builder.onPlayerInit

                this.uri = this@Builder.uri
                this.scaleMode = this@Builder.scaleMode
                this.volume = this@Builder.volume
                this.repeatMode = this@Builder.repeatMode
                this.playWhenReady = this@Builder.playWhenReady
                this.onPauseWhenBuffering = this@Builder.onPauseWhenBuffering
                this.onPlayingChangedCallback = this@Builder.onPlayingChangedCallback
                this.doOnReadyCallback = this@Builder.doOnReadyCallback
                this.onErrorCallback = this@Builder.onErrorCallback
            }
            return helper
        }
    }

    @OptIn(UnstableApi::class)
    private fun initializePlayer(mediaSource: MediaSource, onPlayerInit: (ExoPlayer) -> Unit) {
        Log.d(TAG, "($this)initializePlayer")
        val ctx = context ?: return

        val exoPlayer = ExoPlayer.Builder(ctx)
            .setLoadControl(LOAD_CONTROL)
            .build()
        onPlayerInit.invoke(exoPlayer)
        exoPlayer.also { player ->
            player.setMediaSource(mediaSource)
            scaleMode?.let { mode ->
                player.videoScalingMode = mode
            }
            volume?.let { v ->
                player.volume = v
            }

            player.playWhenReady = playWhenReady
            player.repeatMode = repeatMode
            player.addListener(object : Player.Listener {
                override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
                    super.onPlayWhenReadyChanged(playWhenReady, reason)
                    Log.d(
                        TAG,
                        "($this)onPlayWhenReadyChanged, playWhenReady:$playWhenReady, reason:$reason"
                    )
                }

                override fun onIsPlayingChanged(isPlaying: Boolean) {
                    super.onIsPlayingChanged(isPlaying)
                    Log.d(TAG, "($this)onIsPlayingChanged, isPlaying:$isPlaying")
                    when (player.playbackState) {
                        STATE_BUFFERING -> {
                            //缓冲中
                            onPauseWhenBuffering?.invoke(isPlaying)
                        }

                        else -> {
                            //缓冲完毕
                            onPauseWhenBuffering?.invoke(true)
                        }
                    }
                    onPlayingChangedCallback?.invoke(isPlaying)
                }

//                override fun onEvents(player: Player, events: Player.Events) {
//                    super.onEvents(player, events)
//                    if (!AppBase.isRelease) {
//                        val eventStr = StringBuilder()
//                        for (i in 0 until events.size()) {
//                            eventStr.append(
//                                events.get(i)
//                            )
//                            if (i < events.size() - 1) {
//                                eventStr.append(",")
//                            }
//                        }
//                        Log.d(TAG, "onIsPlayingChanged, events:${eventStr}")
//                    }
//                }

                override fun onPlaybackStateChanged(playbackState: Int) {
                    Log.d(TAG, "($this)onPlaybackStateChanged, playbackState: $playbackState")
                    when (playbackState) {
                        STATE_BUFFERING -> {
                            //缓冲中
                            onPauseWhenBuffering?.invoke(player.isPlaying)
                        }

                        else -> {
                            //缓冲完毕
                            onPauseWhenBuffering?.invoke(true)
                        }
                    }

                    if (Player.STATE_READY == playbackState) {
                        doOnReadyCallback?.invoke()
                        doOnReadyCallback = null
                    }
                }

                override fun onPlayerError(error: PlaybackException) {
                    super.onPlayerError(error)
                    Log.d(TAG, "($this)onPlayerError, error: $error")
                    onErrorCallback?.invoke(error)
                }

                override fun onPlayerErrorChanged(error: PlaybackException?) {
                    super.onPlayerErrorChanged(error)
                    Log.d(TAG, "($this)onPlayerErrorChanged, error:$error")
                }

            })
            player.prepare()
        }
    }

    fun initPlayer(playWhenReady: Boolean = this@ExoPlayerHelper.playWhenReady) {
        Log.d(TAG, "($this)initPlayer, playWhenReady:$playWhenReady")
        releaseSharedPlayer()

        this@ExoPlayerHelper.playWhenReady = playWhenReady
        val mediaSource = buildMediaSource() ?: return
        initializePlayer(mediaSource) {
            player = it
            sharedExoPlayer = it
            onPlayerInit?.invoke(it)
        }
    }

    fun releasePlayer() {
        Log.d(TAG, "($this)releasePlayer")
        isRelease = true
        player?.release()
        player = null
    }

    fun isRelease(): Boolean {
        return isRelease
    }

    //----ExoPlayer的生命周期
    override fun onStart(owner: LifecycleOwner) {
        super.onStart(owner)
        if (Build.VERSION.SDK_INT >= 24) {
            initPlayer()
        }
    }

    override fun onResume(owner: LifecycleOwner) {
        super.onResume(owner)
        if (Build.VERSION.SDK_INT < 24) {
            initPlayer()
        }
    }

    @OptIn(UnstableApi::class)
    private fun buildMediaSource(): MediaSource? {
        Log.d(TAG, "($this)buildMediaSource, uri:$uri")
        val ctx = context ?: return null
        val uriString = uri ?: return null
        //1. 构建缓存文件
        //val cacheFile = File(getCachePath(uriString))
        //2. 构建缓存实例
        //val cache = SimpleCache(cacheFile, LeastRecentlyUsedCacheEvictor(MAX_CACHE_BYTE), StandaloneDatabaseProvider(ctx))
        val mediaItem = MediaItem.fromUri(uriString)
//        if (uriString.startsWith("http")) {
//            //本地资源
//            return ProgressiveMediaSource.Factory(DefaultDataSourceFactory(ctx))
//                .createMediaSource(mediaItem)
//        }
        //3. 构建 DataSourceFactory
        val cacheDataSourceFactory = CacheDataSource.Factory()
            .setCache(VIDEO_CACHE)
            .setCacheReadDataSourceFactory(
                DefaultDataSource.Factory(
                    ctx,
                    DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true)
                )
            )
            .setUpstreamDataSourceFactory(
                DefaultDataSource.Factory(
                    ctx,
                    DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true)
                )
            )
            .setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
        //4. 构建 MediaSource
//                    val mediaSource = if (uriString.endsWith("m3u8")) {
//                        //HlsMediaSource.Factory(cacheDataSourceFactory).createMediaSource(mediaItem!!) //m3u8
//                    } else {
//                        ProgressiveMediaSource.Factory(cacheDataSourceFactory).createMediaSource(mediaItem)
//                    }
        return ProgressiveMediaSource.Factory(cacheDataSourceFactory)
            .createMediaSource(mediaItem)
    }

    override fun onPause(owner: LifecycleOwner) {
        super.onPause(owner)
        if (Build.VERSION.SDK_INT < 24) {
            releasePlayer()
        }
    }

    override fun onStop(owner: LifecycleOwner) {
        super.onStop(owner)
        if (Build.VERSION.SDK_INT >= 24) {
            releasePlayer()
        }
    }

    companion object {
        private const val TAG = "ExoPlayerHelper"

        private const val MAX_CACHE_BYTE = 50 * 1024 * 1024L
        private const val MAX_BUFFER_BYTE = 10 * 1024 * 1024

        private val VIDEO_CACHE = SimpleCache(
            File(FilePath.videoCachePath ?: AppUtil.appContext.cacheDir.absolutePath),
            LeastRecentlyUsedCacheEvictor(MAX_CACHE_BYTE),
            StandaloneDatabaseProvider(AppUtil.appContext)
        )

        private val LOAD_CONTROL = DefaultLoadControl.Builder()
            .setBufferDurationsMs(1500, 3000, 1000, 1000)
            .setTargetBufferBytes(MAX_BUFFER_BYTE)
            .build()

        @JvmStatic
        private var sharedExoPlayer: Player? = null

        @JvmStatic
        fun releaseSharedPlayer() {
            sharedExoPlayer?.let {
                Log.d(TAG, "releaseSharedPlayer")
                it.release()
            }
        }
    }

}