package com.brentvatne.exoplayer

import android.annotation.SuppressLint
import android.app.Dialog
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.LinearLayout
import androidx.activity.OnBackPressedCallback
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.media3.ui.LegacyPlayerControlView
import com.brentvatne.common.api.ControlsConfig
import com.brentvatne.common.toolbox.DebugLog
import java.lang.ref.WeakReference

@SuppressLint("PrivateResource")
class FullScreenPlayerView(
    context: Context,
    private val exoPlayerView: ExoPlayerView,
    private val reactExoplayerView: ReactExoplayerView,
    private val playerControlView: LegacyPlayerControlView?,
    private val onBackPressedCallback: OnBackPressedCallback,
    private val controlsConfig: ControlsConfig
) : Dialog(context, android.R.style.Theme_Black_NoTitleBar) {

    private var parent: ViewGroup? = null
    private val containerView = FrameLayout(context)
    private val mKeepScreenOnHandler = Handler(Looper.getMainLooper())
    private val mKeepScreenOnUpdater = KeepScreenOnUpdater(this)

    // As this view is fullscreen we need to save initial state and restore it afterward
    // Following variables save UI state when open the view
    // restoreUIState, will reapply these values
    private var initialSystemBarsBehavior: Int? = null
    private var initialNavigationBarIsVisible: Boolean? = null
    private var initialNotificationBarIsVisible: Boolean? = null

    private class KeepScreenOnUpdater(fullScreenPlayerView: FullScreenPlayerView) : Runnable {
        private val mFullscreenPlayer = WeakReference(fullScreenPlayerView)

        override fun run() {
            try {
                val fullscreenVideoPlayer = mFullscreenPlayer.get()
                if (fullscreenVideoPlayer != null) {
                    val window = fullscreenVideoPlayer.window
                    if (window != null) {
                        val isPlaying = fullscreenVideoPlayer.exoPlayerView.isPlaying
                        if (isPlaying) {
                            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
                        } else {
                            window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
                        }
                    }
                    fullscreenVideoPlayer.mKeepScreenOnHandler.postDelayed(this, UPDATE_KEEP_SCREEN_ON_FLAG_MS)
                }
            } catch (ex: Exception) {
                DebugLog.e("ExoPlayer Exception", "Failed to flag FLAG_KEEP_SCREEN_ON on fullscreen.")
                DebugLog.e("ExoPlayer Exception", ex.toString())
            }
        }

        companion object {
            private const val UPDATE_KEEP_SCREEN_ON_FLAG_MS = 200L
        }
    }

    init {
        setContentView(containerView, generateDefaultLayoutParams())

        window?.let {
            val inset = WindowInsetsControllerCompat(it, it.decorView)
            initialSystemBarsBehavior = inset.systemBarsBehavior
            initialNavigationBarIsVisible = ViewCompat.getRootWindowInsets(it.decorView)
                ?.isVisible(WindowInsetsCompat.Type.navigationBars()) == true
            initialNotificationBarIsVisible = ViewCompat.getRootWindowInsets(it.decorView)
                ?.isVisible(WindowInsetsCompat.Type.statusBars()) == true
        }
    }

    override fun onStart() {
        super.onStart()
        parent = exoPlayerView.parent as ViewGroup?
        parent?.removeView(exoPlayerView)
        containerView.addView(exoPlayerView, generateDefaultLayoutParams())
        playerControlView?.let {
            updateFullscreenButton(playerControlView, true)
            parent?.removeView(it)
            containerView.addView(it, generateDefaultLayoutParams())
        }
        updateNavigationBarVisibility()
    }

    override fun onStop() {
        super.onStop()
        mKeepScreenOnHandler.removeCallbacks(mKeepScreenOnUpdater)
        containerView.removeView(exoPlayerView)
        parent?.addView(exoPlayerView, generateDefaultLayoutParams())
        playerControlView?.let {
            updateFullscreenButton(playerControlView, false)
            containerView.removeView(it)
            parent?.addView(it, generateDefaultLayoutParams())
        }
        parent?.requestLayout()
        parent = null
        onBackPressedCallback.handleOnBackPressed()
        restoreSystemUI()
    }

    // restore system UI state
    private fun restoreSystemUI() {
        window?.let {
            updateNavigationBarVisibility(
                it,
                initialNavigationBarIsVisible,
                initialNotificationBarIsVisible,
                initialSystemBarsBehavior
            )
        }
    }

    private fun getFullscreenIconResource(isFullscreen: Boolean): Int =
        if (isFullscreen) {
            androidx.media3.ui.R.drawable.exo_icon_fullscreen_exit
        } else {
            androidx.media3.ui.R.drawable.exo_icon_fullscreen_enter
        }

    private fun updateFullscreenButton(playerControlView: LegacyPlayerControlView, isFullscreen: Boolean) {
        val imageButton = playerControlView.findViewById<ImageButton?>(com.brentvatne.react.R.id.exo_fullscreen)
        imageButton?.let {
            val imgResource = getFullscreenIconResource(isFullscreen)
            val desc = if (isFullscreen) {
                context.getString(androidx.media3.ui.R.string.exo_controls_fullscreen_exit_description)
            } else {
                context.getString(androidx.media3.ui.R.string.exo_controls_fullscreen_enter_description)
            }
            imageButton.setImageResource(imgResource)
            imageButton.contentDescription = desc
        }
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        if (reactExoplayerView.preventsDisplaySleepDuringVideoPlayback) {
            mKeepScreenOnHandler.post(mKeepScreenOnUpdater)
        }
    }

    private fun generateDefaultLayoutParams(): FrameLayout.LayoutParams {
        val layoutParams = FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.MATCH_PARENT
        )
        layoutParams.setMargins(0, 0, 0, 0)
        return layoutParams
    }

    private fun updateBarVisibility(
        inset: WindowInsetsControllerCompat,
        type: Int,
        shouldHide: Boolean?,
        initialVisibility: Boolean?,
        systemBarsBehavior: Int? = null
    ) {
        shouldHide?.takeIf { it != initialVisibility }?.let {
            if (it) {
                inset.hide(type)
                systemBarsBehavior?.let { behavior -> inset.systemBarsBehavior = behavior }
            } else {
                inset.show(type)
            }
        }
    }

    // Move the UI to fullscreen.
    // if you change this code, remember to check that the UI is well restored in restoreUIState
    private fun updateNavigationBarVisibility(
        window: Window,
        hideNavigationBarOnFullScreenMode: Boolean?,
        hideNotificationBarOnFullScreenMode: Boolean?,
        systemBarsBehavior: Int?
    ) {
        // Configure the behavior of the hidden system bars.
        val inset = WindowInsetsControllerCompat(window, window.decorView)

        // Update navigation bar visibility and apply systemBarsBehavior if hiding
        updateBarVisibility(
            inset,
            WindowInsetsCompat.Type.navigationBars(),
            hideNavigationBarOnFullScreenMode,
            initialNavigationBarIsVisible,
            systemBarsBehavior
        )

        // Update notification bar visibility (no need for systemBarsBehavior here)
        updateBarVisibility(
            inset,
            WindowInsetsCompat.Type.statusBars(),
            hideNotificationBarOnFullScreenMode,
            initialNotificationBarIsVisible
        )
    }

    private fun updateNavigationBarVisibility() {
        window?.let {
            updateNavigationBarVisibility(
                it,
                controlsConfig.hideNavigationBarOnFullScreenMode,
                controlsConfig.hideNotificationBarOnFullScreenMode,
                WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
            )
        }
        if (controlsConfig.hideNotificationBarOnFullScreenMode) {
            val liveContainer = playerControlView?.findViewById<LinearLayout?>(com.brentvatne.react.R.id.exo_live_container)
            liveContainer?.let {
                val layoutParams = it.layoutParams as LinearLayout.LayoutParams
                layoutParams.topMargin = 40
                it.layoutParams = layoutParams
            }
        }
    }
}
