package com.github.libretube.ui.adapters

import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.Menu
import android.view.ViewGroup
import android.widget.PopupMenu
import androidx.core.os.bundleOf
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.ListAdapter
import com.github.libretube.R
import com.github.libretube.api.SponsorBlockLabelHelper
import com.github.libretube.helpers.DownloadHelper
import com.github.libretube.api.obj.StreamItem
import com.github.libretube.constants.IntentData
import com.github.libretube.databinding.AllCaughtUpRowBinding
import com.github.libretube.databinding.TrendingRowBinding
import com.github.libretube.extensions.dpToPx
import com.github.libretube.extensions.toID
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.parcelable.PlayerData
import com.github.libretube.ui.adapters.callbacks.DiffUtilItemCallback
import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.dialogs.AddToPlaylistDialog
import com.github.libretube.ui.extensions.setFormattedDuration
import com.github.libretube.ui.extensions.setWatchProgressLength
import com.github.libretube.ui.sheets.VideoOptionsBottomSheet
import com.github.libretube.ui.viewholders.VideoCardsViewHolder
import com.github.libretube.util.DeArrowUtil
import com.github.libretube.util.TextUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class VideoCardsAdapter(
    private val columnWidthDp: Float? = null,
    private val onVideoClick: ((StreamItem) -> Unit)? = null
) : ListAdapter<StreamItem, VideoCardsViewHolder>(DiffUtilItemCallback()) {

    override fun getItemViewType(position: Int): Int {
        return if (currentList[position].type == CAUGHT_UP_STREAM_TYPE) CAUGHT_UP_TYPE else NORMAL_TYPE
    }

    fun removeItemById(videoId: String) {
        val index = currentList.indexOfFirst {
            it.url?.toID() == videoId
        }.takeIf { it > 0 } ?: return
        val updatedList = currentList.toMutableList().also {
            it.removeAt(index)
        }

        submitList(updatedList)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VideoCardsViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        return when {
            viewType == CAUGHT_UP_TYPE -> VideoCardsViewHolder(
                AllCaughtUpRowBinding.inflate(layoutInflater, parent, false)
            )

            else -> VideoCardsViewHolder(
                TrendingRowBinding.inflate(layoutInflater, parent, false)
            )
        }
    }

    @SuppressLint("SetTextI18n")
    override fun onBindViewHolder(holder: VideoCardsViewHolder, position: Int) {
        val video = getItem(holder.bindingAdapterPosition)
        val videoId = video.url.orEmpty().toID()

        val context = (holder.trendingRowBinding ?: holder.allCaughtUpBinding)!!.root.context
        val activity = (context as BaseActivity)
        val fragmentManager = activity.supportFragmentManager

        holder.trendingRowBinding?.apply {
            // set a fixed width for better visuals
            if (columnWidthDp != null) {
                root.updateLayoutParams {
                    width = columnWidthDp.dpToPx()
                }
            }
            watchProgress.setWatchProgressLength(videoId, video.duration ?: 0L)

            textViewTitle.text = video.title
            textViewChannel.text = TextUtils.formatViewsString(
                root.context,
                video.views ?: -1,
                video.uploaded,
                video.uploaderName
            )

            video.duration?.let {
                thumbnailDuration.setFormattedDuration(
                    it,
                    video.isShort,
                    video.uploaded
                )
            }
            ImageHelper.loadImage(video.thumbnail, thumbnail)

            if (video.uploaderAvatar != null) {
                channelImageContainer.isVisible = true
                ImageHelper.loadImage(video.uploaderAvatar, channelImage, true)
                channelImage.setOnClickListener {
                    NavigationHelper.navigateChannel(root.context, video.uploaderUrl)
                }
            } else {
                channelImageContainer.isGone = true
                textViewChannel.setOnClickListener {
                    NavigationHelper.navigateChannel(root.context, video.uploaderUrl)
                }
            }
            root.setOnClickListener {
                try {
                    (activity as? com.github.libretube.ui.activities.AbstractPlayerHostActivity)
                        ?.runOnPlayerFragment {
                            try {
                                this.binding.playerMotionLayout.transitionToStart()
                                // update mini player title and artwork immediately
                                this.binding.player.binding.exoTitle.text = video.title
                                val art = this.binding.player.backgroundBinding.exoArtwork
                                com.github.libretube.helpers.ImageHelper.loadImage(video.thumbnail, art)
                            } catch (e: Exception) {
                                // ignore UI update failures
                            }
                            true
                        }
                } catch (e: Exception) {
                }

                onVideoClick?.invoke(video) ?: NavigationHelper.navigateVideo(root.context, PlayerData(videoId))
            }

            addToPlaylistButton?.setOnClickListener {
                AddToPlaylistDialog().apply {
                    arguments = bundleOf(IntentData.videoInfo to video)
                }.show(fragmentManager, AddToPlaylistDialog::class.java.name)
            }

            videoOptionsMenu.setOnClickListener {
                PopupMenu(root.context, it).apply {
                    menu.add(Menu.NONE, MENU_ID_ADD_TO_PLAYLIST, Menu.NONE, root.context.getString(R.string.addToPlaylist))
                    menu.add(Menu.NONE, MENU_ID_DOWNLOAD, Menu.NONE, root.context.getString(R.string.download))
                    setOnMenuItemClickListener { menuItem ->
                        when (menuItem.itemId) {
                            MENU_ID_ADD_TO_PLAYLIST -> {
                                AddToPlaylistDialog().apply {
                                    arguments = bundleOf(IntentData.videoInfo to video)
                                }.show(fragmentManager, AddToPlaylistDialog::class.java.name)
                                true
                            }
                            MENU_ID_DOWNLOAD -> {
                                DownloadHelper.startDownloadDialog(
                                    root.context,
                                    fragmentManager,
                                    videoId
                                )
                                true
                            }
                            else -> false
                        }
                    }
                }.show()
            }

            root.setOnLongClickListener {
                fragmentManager.setFragmentResultListener(
                    VideoOptionsBottomSheet.VIDEO_OPTIONS_SHEET_REQUEST_KEY,
                    activity
                ) { _, _ ->
                    notifyItemChanged(position)
                }
                val sheet = VideoOptionsBottomSheet()
                sheet.arguments = bundleOf(IntentData.streamItem to video)
                sheet.show(fragmentManager, VideoCardsAdapter::class.java.name)
                true
            }

            // always hide the icon, to avoid issues where the icon is recycled and shown until the web requests succeeds
            sponsorBadgeCard.isVisible = false
            CoroutineScope(Dispatchers.IO).launch {
                if (PlayerHelper.sponsorBlockEnabled) {
                    val sponsor = SponsorBlockLabelHelper.getVideoLabels(videoId)
                    withContext(Dispatchers.Main) {
                        val category = sponsor?.segments?.firstOrNull()?.category
                        sponsorBadgeCard.isVisible = category != null
                        SponsorBlockLabelHelper.categoryIcon(category)?.let {
                            sponsorBadgeIcon.setImageDrawable(
                                context.getDrawable(it)
                            )
                        }
                        sponsorBadgeIcon.tooltipText =
                            SponsorBlockLabelHelper.categoryLabel(category)
                                ?.let { context.getString(it) }
                    }
                }

                DeArrowUtil.deArrowVideoId(videoId)?.let { (title, thumbnail) ->
                    withContext(Dispatchers.Main) {
                        if (title != null) this@apply.textViewTitle.text = title
                        if (thumbnail != null) ImageHelper.loadImage(thumbnail, this@apply.thumbnail)
                    }
                }
            }
        }
    }

    companion object {
        private const val NORMAL_TYPE = 0
        private const val CAUGHT_UP_TYPE = 1
        private const val MENU_ID_ADD_TO_PLAYLIST = 1
        private const val MENU_ID_DOWNLOAD = 2

        const val CAUGHT_UP_STREAM_TYPE = "caught"
    }
}
