package org.libre.agosto.p2play

import android.annotation.SuppressLint
import android.app.DownloadManager
import android.content.Intent
import android.content.pm.ActivityInfo
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle
import android.os.Looper
import android.view.View
import android.view.WindowManager
import android.webkit.WebChromeClient
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.session.MediaController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import org.libre.agosto.p2play.adapters.CommentariesAdapter
import org.libre.agosto.p2play.ajax.Actions
import org.libre.agosto.p2play.ajax.Comments
import org.libre.agosto.p2play.ajax.Videos
import org.libre.agosto.p2play.databinding.ActivityReproductorBinding
import org.libre.agosto.p2play.helpers.setFullscreen
import org.libre.agosto.p2play.models.CommentaryModel
import org.libre.agosto.p2play.models.DownloadFiles
import org.libre.agosto.p2play.models.VideoModel
import org.libre.agosto.p2play.singletons.PlaybackSingleton

class ReproductorActivity : AppCompatActivity() {
    private val clientVideo: Videos = Videos()
    lateinit var video: VideoModel
    lateinit var videoPlayback: VideoModel
    private val actions: Actions = Actions()
    private val client: Comments = Comments()
    private val videos: Videos = Videos()

    // Commentaries adapter values
    private lateinit var recyclerView: RecyclerView
    private lateinit var viewAdapter: RecyclerView.Adapter<*>
    private lateinit var viewManager: RecyclerView.LayoutManager

    // Exoplayer
    private lateinit var player: ExoPlayer

    private lateinit var mediaControl: MediaController

    // Fullscreen info
    private var isFullscreen = false

    // Resume info
    private var isResume = false

    private lateinit var binding: ActivityReproductorBinding

    @SuppressLint("SetJavaScriptEnabled", "SetTextI18n")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityReproductorBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)

        val fullscreenButton  = binding.exoPlayer.findViewById<ImageView>(R.id.exo_fullscreen_custom)
        val fullscreenButton2 = binding.fullscreenPlayer.findViewById<ImageView>(R.id.exo_fullscreen_custom)

        val videoView = binding.videoView
        videoView.webChromeClient = WebClient()
        videoView.settings.javaScriptEnabled = true
        videoView.settings.allowContentAccess = true
        videoView.settings.javaScriptCanOpenWindowsAutomatically = true
        videoView.settings.allowFileAccess = true
        videoView.settings.allowFileAccessFromFileURLs = true
        videoView.settings.allowUniversalAccessFromFileURLs = true
        videoView.settings.domStorageEnabled = true

        try {
            val resume = this.intent.extras?.getSerializable("resume")
            if (resume == null) {
                video = this.intent.extras?.getSerializable("video") as VideoModel
                isResume = false
            } else {
                video = PlaybackSingleton.video!!
                isResume = true
            }

            binding.tittleVideoTxt.text = this.video.name
            binding.viewsTxt.text = "${this.video.views} ${getString(R.string.view_text)}"
            binding.userTxt.text = this.video.username
            binding.descriptionVideoTxt.text = this.video.description
            val haveDescription = this.video.description.endsWith("...")
            if (haveDescription) {
                binding.showMoreBtn.visibility = View.VISIBLE
            }
            binding.hostTxt.text = this.video.userHost

            // Check if user had profile image
            if (this.video.userImageUrl != "") {
                Picasso.get().load("https://" + ManagerSingleton.url + this.video.userImageUrl).into(binding.userImg)
            }
            // Load the video
            videoView.loadUrl("https://" + ManagerSingleton.url + this.video.embedUrl)
        } catch (err: Exception) {
            err.printStackTrace()
        }

        viewManager = LinearLayoutManager(this)
        this.setDataComments(arrayListOf())

        this.getComments()

        binding.subscribeBtn.setOnClickListener { subscribe() }
        binding.likeLayout.setOnClickListener { rate("like") }
        binding.dislikeLayout.setOnClickListener { rate("dislike") }
        binding.commentBox.commentaryBtn.setOnClickListener { makeComment() }
        binding.showMoreBtn.setOnClickListener { getDescription() }
        binding.shareLayout.setOnClickListener { shareIntent() }
        binding.reportLayout.setOnClickListener { reportIntent() }
        fullscreenButton.setOnClickListener { toggleFullscreen() }
        fullscreenButton2.setOnClickListener { toggleFullscreen() }
        binding.downloadLayout!!.setOnClickListener { downloadVideo() }


        binding.userImg.setOnClickListener {
            val intent = Intent(this, ChannelActivity::class.java)
            intent.putExtra("channel", video.getChannel())
            startActivity(intent)
        }

        AsyncTask.execute {
            videoPlayback = this.clientVideo.getVideo(this.video.uuid)
            // TODO: Make this configurable
            // val bufferSize = 1024 * 1024 // 1mb
            // val allocator = DefaultAllocator(true, bufferSize)
            // val loadControl = DefaultLoadControl.Builder()
            //    .setAllocator(allocator)
            //    .build()

            runOnUiThread {
                try {
                    if (PlaybackSingleton.player == null || !PlaybackSingleton.player!!.playWhenReady) {
                        PlaybackSingleton.player = ExoPlayer.Builder(this)
                            //.setSeekBackIncrementMs(10000)
                            //.setSeekForwardIncrementMs(10000)
                            //.setLoadControl(loadControl)
                        .build()
                    }

                    player = PlaybackSingleton.player!!
                    binding.exoPlayer.player = player

                    binding.exoPlayer
                    player.addListener(
                        object : Player.Listener {
                            override fun onPlaybackStateChanged(playbackState: Int) {
                                super.onPlaybackStateChanged(playbackState)
                                val controls = binding.exoPlayer.findViewById<LinearLayout>(R.id.exo_center_controls)
                                if (playbackState == Player.STATE_BUFFERING || playbackState == Player.STATE_IDLE) {
                                  controls.visibility = View.INVISIBLE
                               } else if (playbackState == Player.STATE_READY) {
                                  controls.visibility = View.VISIBLE
                               }
                           }
                       }
                    )

                    println("----- video --------")
                    println(videoPlayback.streamingData?.playlistUrl)

                    if (!isResume) {
                        val mediaItem = MediaItem.Builder()
                            .setUri(videoPlayback.streamingData?.playlistUrl!!)
                            .setMediaMetadata(
                                MediaMetadata.Builder()
                                    .setArtist(videoPlayback.username)
                                    .setTitle(videoPlayback.name)
                                    .setArtworkUri(Uri.parse("https://${ManagerSingleton.url}${videoPlayback.thumbUrl}"))
                                    .build(),
                            ).build()
                        PlaybackSingleton.setData(mediaItem, video)
                    }
                    // Start the playback.
                    // TODO: Setting for autoplay
                    // player.play()
                } catch (err: Exception) {
                    err.printStackTrace()
                }
            }
        }
    }

    private fun subscribe() {
        AsyncTask.execute {
            if (Looper.myLooper() == null) {
                Looper.prepare()
            }
            val res = this.actions.subscribe(ManagerSingleton.token.token, video.getChannel())
            if (res == 1) {
                runOnUiThread {
                    ManagerSingleton.toast(getString(R.string.subscribeMsg), this)
                    this.changeSubscribeBtn(true)
                }
            }
        }
    }

    private fun unSubscribe() {
        AsyncTask.execute {
            if (Looper.myLooper() == null) {
                Looper.prepare()
            }
            val res = this.actions.unSubscribe(ManagerSingleton.token.token, video.getChannel())
            if (res == 1) {
                runOnUiThread {
                    ManagerSingleton.toast(getString(R.string.unSubscribeMsg), this)
                    this.changeSubscribeBtn(false)
                }
            }
        }
    }

    private fun rate(rate: String) {
        AsyncTask.execute {
            if (Looper.myLooper() == null) {
                Looper.prepare()
            }
            val res = this.actions.rate(ManagerSingleton.token.token, this.video.id, rate)
            if (res == 1) {
                runOnUiThread {
                    ManagerSingleton.toast(getString(R.string.rateMsg), this)
                    if (rate == "like") {
                        binding.textViewLike.setTextColor(ContextCompat.getColor(this, R.color.colorLike))
                        binding.textViewDislike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
                    } else if (rate == "dislike") {
                        binding.textViewDislike.setTextColor(ContextCompat.getColor(this, R.color.colorDislike))
                        binding.textViewLike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
                    }
                }
            }
        }
    }

    private fun getRate() {
        AsyncTask.execute {
            if (Looper.myLooper() == null) {
                Looper.prepare()
            }
            val rate = this.actions.getRate(ManagerSingleton.token.token, this.video.id)
            runOnUiThread {
                when (rate) {
                    "like" -> {
                        binding.textViewLike.setTextColor(ContextCompat.getColor(this, R.color.colorLike))
                        binding.textViewDislike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
                    }
                    "dislike" -> {
                        binding.textViewDislike.setTextColor(ContextCompat.getColor(this, R.color.colorDislike))
                        binding.textViewLike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
                    }
                    else -> {
                        binding.textViewLike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
                        binding.textViewDislike.setTextColor(ContextCompat.getColor(this, R.color.primary_dark_material_light))
                    }
                }
            }
        }
    }

    private fun getSubscription() {
        val account = this.video.nameChannel + "@" + this.video.userHost
        AsyncTask.execute {
            if (Looper.myLooper() == null) {
                Looper.prepare()
            }
            val isSubscribed = this.actions.getSubscription(ManagerSingleton.token.token, account)
            runOnUiThread {
                this.changeSubscribeBtn(isSubscribed)
            }
        }
    }

    private fun changeSubscribeBtn(subscribed: Boolean) {
        if (subscribed) {
            binding.subscribeBtn.text = getText(R.string.unSubscribeBtn)
            binding.subscribeBtn.setOnClickListener { this.unSubscribe() }
        } else {
            binding.subscribeBtn.text = getText(R.string.subscribeBtn)
            binding.subscribeBtn.setOnClickListener { this.subscribe() }
        }
    }

    private fun setDataComments(data: ArrayList<CommentaryModel>) {
        // Set data for RecyclerView
        viewAdapter = CommentariesAdapter(data).setFragmentManager(supportFragmentManager)

        recyclerView = findViewById<RecyclerView>(R.id.listCommentaries).apply {
            // use this setting to improve performance if you know that changes
            // in content do not change the layout size of the RecyclerView
            setHasFixedSize(true)

            // use a linear layout manager
            layoutManager = viewManager

            // specify an viewAdapter (see also next example)
            adapter = viewAdapter
        }
    }

    private fun getComments() {
        AsyncTask.execute {
            val data = this.client.getCommentaries(this.video.id)
            runOnUiThread {
                this.setDataComments(data)
            }
        }
    }

    private fun makeComment() {
        if (binding.commentBox.commentaryText.text.toString() == "") {
            ManagerSingleton.toast(getString(R.string.emptyCommentaryMsg), this)
            return
        }
        val text = binding.commentBox.commentaryText.text.toString()
        AsyncTask.execute {
            val res = this.client.makeCommentary(ManagerSingleton.token.token, this.video.id, text)
            runOnUiThread {
                if (res) {
                    ManagerSingleton.toast(getString(R.string.makedCommentaryMsg), this)
                    binding.commentBox.commentaryText.text?.clear()
                    this.getComments()
                } else {
                    ManagerSingleton.toast(getString(R.string.errorCommentaryMsg), this)
                }
            }
        }
    }

    override fun onResume() {
        super.onResume()
        if (ManagerSingleton.user.status == 1) {
            this.getRate()
            this.getSubscription()
            binding.actionsLayout.visibility = View.VISIBLE
            binding.subscribeBtn.visibility = View.VISIBLE
            binding.commentBox.commentaryLayout.visibility = View.VISIBLE
            if (ManagerSingleton.user.avatar != "") {
                Picasso.get().load("https://" + ManagerSingleton.url + ManagerSingleton.user.avatar).into(binding.commentBox.userImgCom)
            }
        } else {
            binding.commentBox.commentaryLayout.visibility = View.GONE
        }
    }

    private fun getDescription() {
        AsyncTask.execute {
            val fullDescription = this.videos.fullDescription(this.video.id)
            runOnUiThread {
                binding.descriptionVideoTxt.text = fullDescription
                binding.showMoreBtn.visibility = View.GONE
            }
        }
    }

    private fun shareIntent() {
        val sendIntent: Intent = Intent().apply {
            action = Intent.ACTION_SEND
            putExtra(Intent.EXTRA_TEXT, "${video.name} ${video.getVideoUrl()}")
            type = "text/plain"
        }
        startActivity(Intent.createChooser(sendIntent, resources.getText(R.string.shareBtn)))
    }

    private fun reportIntent() {
        val builder = AlertDialog.Builder(this)
        // Get the layout inflater
        val dialog = layoutInflater.inflate(R.layout.report_dialog, null)

        val inputReason = dialog.findViewById<EditText>(R.id.reportText)

        // Inflate and set the layout for the dialog
        // Pass null as the parent view because its going in the dialog layout
        builder.setView(dialog)
            .setTitle(R.string.reportDialog)
            // Add action buttons
            .setPositiveButton(R.string.reportBtn) { _, _ ->
                val reason = inputReason.text.toString()
                reportVideo(reason)
            }
            .setNegativeButton("Cancel") { dialog, _ ->
                dialog.run { cancel() }
            }
        val alertDialog = builder.create()
        alertDialog.show()
    }

    private fun reportVideo(reason: String) {
        AsyncTask.execute {
            val res = actions.reportVideo(video.id, reason, ManagerSingleton.token.token)

            runOnUiThread {
                if (res) {
                    ManagerSingleton.toast(getText(R.string.reportDialogMsg).toString(), this)
                } else {
                    ManagerSingleton.toast(getText(R.string.errorMsg).toString(), this)
                }
            }
        }
    }

    private fun toggleFullscreen() {
        if (isFullscreen) {
            binding.nonFullScreen.visibility = View.VISIBLE
            binding.fullScreenExo.visibility = View.GONE
            binding.exoPlayer.player = player
            binding.fullscreenPlayer.player = null
            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED

            val attrs = window.attributes
            attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv()
            attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON.inv()
            window.attributes = attrs
            window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE

            isFullscreen = false
        } else {
            val matchParent = WindowManager.LayoutParams.MATCH_PARENT

            binding.nonFullScreen.visibility = View.GONE
            binding.fullScreenExo.visibility = View.VISIBLE

            binding.exoPlayer.player = null
            binding.fullscreenPlayer.player = player

            setFullscreen(window)

            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
            isFullscreen = true
        }
    }

    private fun downloadVideo() {
            val downloadFile = videoPlayback.streamingData?.downloadFiles?.first()
            if (downloadFile != null) {
                val manager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
                val request = DownloadManager.Request(Uri.parse(downloadFile.url))
                val fileName = "${videoPlayback.name}-${downloadFile.resolution}.${downloadFile.url.split('.').last()}"
                try {
                    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
                        .setTitle(fileName)
                        .setDescription(getString(R.string.downloadText))
                        .setDestinationInExternalPublicDir(
                            android.os.Environment.DIRECTORY_DOWNLOADS,
                            fileName
                        )
                    manager.enqueue(request)
                    ManagerSingleton.toast(getString(R.string.downloadStarted), this)
                } catch (_: Exception) {
                    ManagerSingleton.toast(getString(R.string.downloadFailed), this)
                }
            } else {
                ManagerSingleton.toast(getString(R.string.downloadFailed), this)
            }

    }

    override fun onDestroy() {
        if (!player.isPlaying) {
            PlaybackSingleton.release()
        }
        super.onDestroy()
    }

    internal inner class WebClient : WebChromeClient() {
        private var mCustomView: View? = null
        private var mCustomViewCallback: WebChromeClient.CustomViewCallback? = null
        private var mOriginalOrientation: Int = 0
        private var mOriginalSystemUiVisibility: Int = 0

        override fun getDefaultVideoPoster(): Bitmap? {
            AsyncTask.execute {
                this@ReproductorActivity.actions.watchVideo(this@ReproductorActivity.video.id, ManagerSingleton.token.token)
            }

            return if (mCustomView == null) {
                null
            } else {
                BitmapFactory.decodeResource(this@ReproductorActivity.resources, 2130837573)
            }
        }

        override fun onHideCustomView() {
            try {
                this@ReproductorActivity.binding.nonFullScreen.visibility = View.VISIBLE
                this@ReproductorActivity.binding.fullScreen.visibility = View.GONE
                this@ReproductorActivity.binding.fullScreen.removeView(this.mCustomView)
                this.mCustomView = null

                this.mCustomViewCallback!!.onCustomViewHidden()
                this.mCustomViewCallback = null

                this@ReproductorActivity.requestedOrientation = this.mOriginalOrientation
                // this@ReproductorActivity.window.decorView.systemUiVisibility = this.mOriginalSystemUiVisibility

                val attrs = this@ReproductorActivity.window.attributes
                attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv()
                attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON.inv()
                window.attributes = attrs
                this@ReproductorActivity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
            } catch (err: Exception) {
                err.printStackTrace()
            }
        }

        override fun onShowCustomView(paramView: View, paramCustomViewCallback: WebChromeClient.CustomViewCallback) {
            if (this.mCustomView != null) {
                this.onHideCustomView()
                return
            }
            try {
                this.mCustomView = paramView
                this.mOriginalSystemUiVisibility = this@ReproductorActivity.window.decorView.systemUiVisibility
                this.mOriginalOrientation = this@ReproductorActivity.requestedOrientation
                this.mCustomViewCallback = paramCustomViewCallback
                val matchParent = WindowManager.LayoutParams.MATCH_PARENT

                this@ReproductorActivity.binding.nonFullScreen.visibility = View.GONE
                this@ReproductorActivity.binding.fullScreen.visibility = View.VISIBLE

                this@ReproductorActivity.binding.fullScreen.addView(paramView, FrameLayout.LayoutParams(matchParent, matchParent))

                setFullscreen(this@ReproductorActivity.window)

                this@ReproductorActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
            } catch (err: Exception) {
                err.printStackTrace()
            }
        }
    }
}
