package ani.dantotsu.media.comments

import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.view.View
import android.widget.PopupMenu
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.R
import ani.dantotsu.connections.comments.Comment
import ani.dantotsu.connections.comments.CommentsAPI
import ani.dantotsu.copyToClipboard
import ani.dantotsu.databinding.ItemCommentsBinding
import ani.dantotsu.getAppString
import ani.dantotsu.loadImage
import ani.dantotsu.others.ImageViewDialog
import ani.dantotsu.profile.ProfileActivity
import ani.dantotsu.setAnimation
import ani.dantotsu.snackString
import ani.dantotsu.util.ColorEditor.Companion.adjustColorForContrast
import ani.dantotsu.util.ColorEditor.Companion.getContrastRatio
import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.Section
import com.xwray.groupie.viewbinding.BindableItem
import io.noties.markwon.Markwon
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.TimeZone
import kotlin.math.abs
import kotlin.math.sqrt

class CommentItem(
    val comment: Comment,
    private val markwon: Markwon,
    val parentSection: Section,
    private val commentsFragment: CommentsFragment,
    private val backgroundColor: Int,
    val commentDepth: Int
) :
    BindableItem<ItemCommentsBinding>() {
    lateinit var binding: ItemCommentsBinding
    val adapter = GroupieAdapter()
    private var subCommentIds: MutableList<Int> = mutableListOf()
    val repliesSection = Section()
    private var isEditing = false
    var isReplying = false
    private var repliesVisible = false
    var MAX_DEPTH = 3

    init {
        adapter.add(repliesSection)
    }

    override fun bind(viewBinding: ItemCommentsBinding, position: Int) {
        binding = viewBinding
        setAnimation(binding.root.context, binding.root)
        viewBinding.commentRepliesList.layoutManager =
            LinearLayoutManager(commentsFragment.activity)
        viewBinding.commentRepliesList.adapter = adapter
        val isUserComment = CommentsAPI.userId == comment.userId
        val levelColor = getAvatarColor(comment.totalVotes, backgroundColor)
        markwon.setMarkdown(viewBinding.commentText, comment.content)
        viewBinding.commentEdit.visibility = if (isUserComment) View.VISIBLE else View.GONE
        if (comment.tag == null) {
            viewBinding.commentUserTagLayout.visibility = View.GONE
        } else {
            viewBinding.commentUserTagLayout.visibility = View.VISIBLE
            viewBinding.commentUserTag.text = comment.tag.toString()
        }
        replying(isReplying) //sets default text
        editing(isEditing)
        if ((comment.replyCount ?: 0) > 0) {
            viewBinding.commentTotalReplies.visibility = View.VISIBLE
            viewBinding.commentRepliesDivider.visibility = View.VISIBLE
            viewBinding.commentTotalReplies.context.run {
                viewBinding.commentTotalReplies.text = if (repliesVisible)
                    getString(R.string.hide_replies)
                else
                    if (comment.replyCount == 1)
                        getString(R.string.view_reply)
                    else
                        getString(R.string.view_replies_count, comment.replyCount)
            }
        } else {
            viewBinding.commentTotalReplies.visibility = View.GONE
            viewBinding.commentRepliesDivider.visibility = View.GONE
        }
        viewBinding.commentReply.visibility = View.VISIBLE
        viewBinding.commentTotalReplies.setOnClickListener {
            if (repliesVisible) {
                repliesSection.clear()
                removeSubCommentIds()
                viewBinding.commentTotalReplies.context.run {
                    viewBinding.commentTotalReplies.text = if (comment.replyCount == 1)
                        getString(R.string.view_reply)
                    else
                        getString(R.string.view_replies_count, comment.replyCount)
                }
                repliesVisible = false
            } else {
                viewBinding.commentTotalReplies.setText(R.string.hide_replies)
                repliesSection.clear()
                commentsFragment.viewReplyCallback(this)
                repliesVisible = true
            }
        }

        viewBinding.commentUserName.setOnClickListener {
            ContextCompat.startActivity(
                commentsFragment.activity,
                Intent(commentsFragment.activity, ProfileActivity::class.java)
                    .putExtra("userId", comment.userId.toInt())
                    .putExtra("userLVL", "[${levelColor.second}]"),
                null
            )
        }
        viewBinding.commentUserAvatar.setOnClickListener {
            ContextCompat.startActivity(
                commentsFragment.activity,
                Intent(commentsFragment.activity, ProfileActivity::class.java)
                    .putExtra("userId", comment.userId.toInt())
                    .putExtra("userLVL", "[${levelColor.second}]"),
                null
            )
        }
        viewBinding.commentText.setOnLongClickListener {
            copyToClipboard(comment.content)
            true
        }

        viewBinding.commentEdit.setOnClickListener {
            editing(!isEditing)
            commentsFragment.editCallback(this)
        }
        viewBinding.commentReply.setOnClickListener {
            replying(!isReplying)
            commentsFragment.replyTo(this, comment.username)
            commentsFragment.replyCallback(this)
        }
        viewBinding.modBadge.visibility = if (comment.isMod == true) View.VISIBLE else View.GONE
        viewBinding.adminBadge.visibility = if (comment.isAdmin == true) View.VISIBLE else View.GONE
        viewBinding.commentInfo.setOnClickListener {
            val popup = PopupMenu(commentsFragment.requireContext(), viewBinding.commentInfo)
            popup.menuInflater.inflate(R.menu.profile_details_menu, popup.menu)
            popup.menu.findItem(R.id.commentDelete)?.isVisible =
                isUserComment || CommentsAPI.isAdmin || CommentsAPI.isMod
            popup.menu.findItem(R.id.commentBanUser)?.isVisible =
                (CommentsAPI.isAdmin || CommentsAPI.isMod) && !isUserComment
            popup.menu.findItem(R.id.commentReport)?.isVisible = !isUserComment
            popup.setOnMenuItemClickListener { item ->
                when (item.itemId) {
                    R.id.commentReport -> {
                        dialogBuilder(
                            getAppString(R.string.report_comment),
                            getAppString(R.string.report_comment_confirm)
                        ) {
                            CoroutineScope(Dispatchers.Main + SupervisorJob()).launch {
                                val success = CommentsAPI.reportComment(
                                    comment.commentId,
                                    comment.username,
                                    commentsFragment.mediaName,
                                    comment.userId
                                )
                                if (success) {
                                    snackString(R.string.comment_reported)
                                }
                            }
                        }
                        true
                    }

                    R.id.commentDelete -> {
                        dialogBuilder(
                            getAppString(R.string.delete_comment),
                            getAppString(R.string.delete_comment_confirm)
                        ) {
                            CoroutineScope(Dispatchers.Main + SupervisorJob()).launch {
                                val success = CommentsAPI.deleteComment(comment.commentId)
                                if (success) {
                                    snackString(R.string.comment_deleted)
                                    parentSection.remove(this@CommentItem)
                                }
                            }
                        }
                        true
                    }

                    R.id.commentBanUser -> {
                        dialogBuilder(
                            getAppString(R.string.ban_user),
                            getAppString(R.string.ban_user_confirm)
                        ) {
                            CoroutineScope(Dispatchers.Main + SupervisorJob()).launch {
                                val success = CommentsAPI.banUser(comment.userId)
                                if (success) {
                                    snackString(R.string.user_banned)
                                }
                            }
                        }
                        true
                    }

                    else -> {
                        false
                    }
                }
            }
            popup.show()
        }
        //fill the icon if the user has liked the comment
        setVoteButtons(viewBinding)
        viewBinding.commentUpVote.setOnClickListener {
            val voteType = if (comment.userVoteType == 1) 0 else 1
            val previousVoteType = comment.userVoteType
            val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
            scope.launch {
                val success = CommentsAPI.vote(comment.commentId, voteType)
                if (success) {
                    comment.userVoteType = voteType

                    if (previousVoteType == -1) {
                        comment.downvotes -= 1
                    }
                    comment.upvotes += if (voteType == 1) 1 else -1

                    notifyChanged()
                }
            }
        }

        viewBinding.commentDownVote.setOnClickListener {
            val voteType = if (comment.userVoteType == -1) 0 else -1
            val previousVoteType = comment.userVoteType
            val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
            scope.launch {
                val success = CommentsAPI.vote(comment.commentId, voteType)
                if (success) {
                    comment.userVoteType = voteType

                    if (previousVoteType == 1) {
                        comment.upvotes -= 1
                    }
                    comment.downvotes += if (voteType == -1) 1 else -1
                    notifyChanged()
                }
            }
        }
        viewBinding.commentTotalVotes.text = (comment.upvotes - comment.downvotes).toString()
        viewBinding.commentUserAvatar.setOnLongClickListener {
            ImageViewDialog.newInstance(
                commentsFragment.activity,
                "${comment.username}'s [Cover]",
                comment.profilePictureUrl
            )
        }
        comment.profilePictureUrl?.let { viewBinding.commentUserAvatar.loadImage(it) }
        viewBinding.commentUserName.text = comment.username
        val userColor = "[${levelColor.second}]"
        viewBinding.commentUserLevel.text = userColor
        viewBinding.commentUserLevel.setTextColor(levelColor.first)
        viewBinding.commentUserTime.text = formatTimestamp(comment.timestamp)
    }

    override fun getLayout(): Int {
        return R.layout.item_comments
    }

    fun containsGif(): Boolean {
        return comment.content.contains(".gif")
    }

    override fun initializeViewBinding(view: View): ItemCommentsBinding {
        return ItemCommentsBinding.bind(view)
    }

    fun replying(isReplying: Boolean) {
        binding.commentReply.text =
            if (isReplying) commentsFragment.activity.getString(R.string.cancel) else "Reply"
        this.isReplying = isReplying
    }

    fun editing(isEditing: Boolean) {
        binding.commentEdit.text =
            if (isEditing) commentsFragment.activity.getString(R.string.cancel) else commentsFragment.activity.getString(
                R.string.edit
            )
        this.isEditing = isEditing
    }

    fun registerSubComment(id: Int) {
        subCommentIds.add(id)
    }

    private fun removeSubCommentIds() {
        subCommentIds.forEach { id ->
            @Suppress("UNCHECKED_CAST")
            val parentComments = parentSection.groups as? List<CommentItem> ?: emptyList()
            val commentToRemove = parentComments.find { it.comment.commentId == id }
            commentToRemove?.let {
                it.removeSubCommentIds()
                parentSection.remove(it)
            }
        }
        subCommentIds.clear()
    }

    private fun setVoteButtons(viewBinding: ItemCommentsBinding) {
        when (comment.userVoteType) {
            1 -> {
                viewBinding.commentUpVote.setImageResource(R.drawable.ic_round_upvote_active_24)
                viewBinding.commentUpVote.alpha = 1f
                viewBinding.commentDownVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
            }

            -1 -> {
                viewBinding.commentUpVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
                viewBinding.commentDownVote.setImageResource(R.drawable.ic_round_upvote_active_24)
                viewBinding.commentDownVote.alpha = 1f
            }

            else -> {
                viewBinding.commentUpVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
                viewBinding.commentDownVote.setImageResource(R.drawable.ic_round_upvote_inactive_24)
            }
        }
    }

    @SuppressLint("SimpleDateFormat")
    private fun formatTimestamp(timestamp: String): String {
        return try {
            val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ROOT)
            dateFormat.timeZone = TimeZone.getTimeZone("UTC")
            val parsedDate = dateFormat.parse(timestamp)
            val currentDate = Date()

            val diff = currentDate.time - (parsedDate?.time ?: 0)

            val days = diff / (24 * 60 * 60 * 1000)
            val hours = diff / (60 * 60 * 1000) % 24
            val minutes = diff / (60 * 1000) % 60

            return when {
                days > 0 -> "${days}d"
                hours > 0 -> "${hours}h"
                minutes > 0 -> "${minutes}m"
                else -> "now"
            }
        } catch (e: Exception) {
            "now"
        }
    }

    companion object {
        @SuppressLint("SimpleDateFormat")
        fun timestampToMillis(timestamp: String): Long {
            val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ROOT)
            dateFormat.timeZone = TimeZone.getTimeZone("UTC")
            val parsedDate = dateFormat.parse(timestamp)
            return parsedDate?.time ?: 0
        }
    }

    private fun getAvatarColor(voteCount: Int, backgroundColor: Int): Pair<Int, Int> {
        val level = if (voteCount < 0) 0 else sqrt(abs(voteCount.toDouble()) / 0.8).toInt()
        val colorString =
            if (level > usernameColors.size - 1) usernameColors[usernameColors.size - 1] else usernameColors[level]
        var color = Color.parseColor(colorString)
        val ratio = getContrastRatio(color, backgroundColor)
        if (ratio < 4.5) {
            color = adjustColorForContrast(color, backgroundColor)
        }

        return Pair(color, level)
    }

    /**
     * Builds the dialog for yes/no confirmation
     * no doesn't do anything, yes calls the callback
     * @param title the title of the dialog
     * @param message the message of the dialog
     * @param callback the callback to call when the user clicks yes
     */
    private fun dialogBuilder(title: String, message: String, callback: () -> Unit) {
        val alertDialog =
            android.app.AlertDialog.Builder(commentsFragment.activity, R.style.MyPopup)
                .setTitle(title)
                .setMessage(message)
                .setPositiveButton("Yes") { dialog, _ ->
                    callback()
                    dialog.dismiss()
                }
                .setNegativeButton("No") { dialog, _ ->
                    dialog.dismiss()
                }
        val dialog = alertDialog.show()
        dialog?.window?.setDimAmount(0.8f)
    }

    private val usernameColors: Array<String> = arrayOf(
        "#9932cc",
        "#a020f0",
        "#8b008b",
        "#7b68ee",
        "#da70d6",
        "#dda0dd",
        "#ffe4b5",
        "#f0e68c",
        "#ffb6c1",
        "#fa8072",
        "#b03060",
        "#ff1493",
        "#ff00ff",
        "#ff69b4",
        "#dc143c",
        "#8b0000",
        "#ff0000",
        "#a0522d",
        "#f4a460",
        "#b8860b",
        "#ffa500",
        "#d2691e",
        "#ff6347",
        "#808000",
        "#ffd700",
        "#ffff54",
        "#8fbc8f",
        "#3cb371",
        "#008000",
        "#00fa9a",
        "#98fb98",
        "#00ff00",
        "#adff2f",
        "#32cd32",
        "#556b2f",
        "#9acd32",
        "#7fffd4",
        "#2f4f4f",
        "#5f9ea0",
        "#87ceeb",
        "#00bfff",
        "#00ffff",
        "#1e90ff",
        "#4682b4",
        "#0000ff",
        "#0000cd",
        "#00008b",
        "#191970",
        "#ffffff",
    )

}