package com.kaixed.kchat.ui.activity

import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.LinearLayout
import androidx.activity.OnBackPressedCallback
import androidx.activity.enableEdgeToEdge
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.kaixed.kchat.R
import com.kaixed.kchat.data.objectbox.ObjectBox.get
import com.kaixed.kchat.data.objectbox.entity.Messages
import com.kaixed.kchat.data.objectbox.entity.Messages_
import com.kaixed.kchat.databinding.ActivityChatBinding
import com.kaixed.kchat.model.FunctionItem
import com.kaixed.kchat.service.WebSocketService
import com.kaixed.kchat.service.WebSocketService.LocalBinder
import com.kaixed.kchat.ui.adapter.ChatAdapter
import com.kaixed.kchat.ui.adapter.EmojiAdapter
import com.kaixed.kchat.ui.adapter.FunctionPanelAdapter
import com.kaixed.kchat.ui.base.BaseActivity
import com.kaixed.kchat.ui.i.OnItemClickListener
import com.kaixed.kchat.utils.Constants.CURRENT_CONTACT_ID
import com.kaixed.kchat.utils.Constants.KEYBOARD_HEIGHT_RATIO
import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION
import com.kaixed.kchat.utils.ConstantsUtil.getCurrentContactId
import com.kaixed.kchat.utils.ConstantsUtil.getKeyboardHeight
import com.kaixed.kchat.utils.ConstantsUtil.getUsername
import com.kaixed.kchat.utils.ImageSpanUtil.insertEmoji
import com.tencent.mmkv.MMKV
import io.objectbox.Box
import io.objectbox.query.QueryBuilder
import org.json.JSONObject
import java.util.LinkedList

class ChatActivity : BaseActivity<ActivityChatBinding>(), OnItemClickListener {

    private var chatAdapter: ChatAdapter? = null

    private var webSocketService: WebSocketService? = null

    private val messagesList = LinkedList<Messages>()

    private lateinit var messagesBox: Box<Messages>

    private var contactId: String = ""

    private var contactNickname: String? = null

    private val username: String by lazy { getUsername() }

    private var tempIndex: Long = 0

    private val context: Context = this

    private var strings: MutableList<String>? = mutableListOf()

    private var imm: InputMethodManager? = null

    private var softKeyboardHeight = 0

    private var keyboardShown = false

    private var loading = false

    private var hasHistory = false

    private var bound = false

    private val mmkv by lazy { MMKV.mmkvWithID(MMKV_USER_SESSION) }

    companion object {
        private const val TAG = "ChatActivity"
        private const val LIMIT: Long = 20L
        private const val UNBLOCK_DELAY_TIME = 200L
    }

    override fun inflateBinding(): ActivityChatBinding {
        return ActivityChatBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()

        handleIntent(intent)

        initData()

        firstLoadData()

        initView()

        setListener()

        bindWebSocketService()

        setPanelChange()

        getKeyBoardVisibility()
    }

    private val connection: ServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            val binder = service as LocalBinder
            webSocketService = binder.getService()
            bound = true
            observeLiveData()
        }

        override fun onServiceDisconnected(arg0: ComponentName) {
            bound = false
        }
    }

    private fun getKeyBoardVisibility() {
        val rootView = binding.root

        rootView.viewTreeObserver.addOnGlobalLayoutListener {
            val r = Rect()
            rootView.getWindowVisibleDisplayFrame(r)
            val screenHeight = rootView.rootView.height
            val keypadHeight = screenHeight - r.bottom
            keyboardShown = keypadHeight > screenHeight * KEYBOARD_HEIGHT_RATIO
        }
    }

    private fun setBackPressListener() {
        onBackPressedDispatcher.addCallback(
            this,
            object : OnBackPressedCallback(true) {
                override fun handleOnBackPressed() {
                    if (binding.clBottomPanel.isShown) {
                        hidePanel(false)
                    } else {
                        isEnabled = false
                        onBackPressedDispatcher.onBackPressed()
                    }
                }
            },
        )
    }

    @SuppressLint("ClickableViewAccessibility")
    private fun setPanelChange() {
        binding.etInput.setOnTouchListener { _, event ->
            if (event.action == MotionEvent.ACTION_UP && binding.clBottomPanel.isShown) {
                lockContentViewHeight()
                hidePanel(true)
                unlockContentViewHeight()
            }
            false
        }

        binding.ivFunctionPanel.setOnClickListener { v ->
            if (binding.clBottomPanel.isShown) {
                if (binding.rvEmoji.isShown) {
                    handlePanelSwitch(false)
                } else {
                    hideBottomPanel()
                }
            } else {
                switchPanel(false)
            }
        }

        binding.ivEmoji.setOnClickListener { v ->
            if (binding.clBottomPanel.isShown) {
                if (binding.gvFunctionPanel.isShown) {
                    handlePanelSwitch(true)
                } else {
                    hideBottomPanel()
                }
            } else {
                switchPanel(true)
            }
        }
    }

    private fun switchPanel(isSwitchToEmoji: Boolean) {
        if (keyboardShown) {
            lockContentViewHeight()
            handlePanelSwitch(isSwitchToEmoji)
            unlockContentViewHeight()
        } else {
            handlePanelSwitch(isSwitchToEmoji)
        }
    }

    private fun hideBottomPanel() {
        lockContentViewHeight()
        hidePanel(true)
        unlockContentViewHeight()
    }

    private fun lockContentViewHeight() {
        val layoutParams = binding.recycleChatList.layoutParams as LinearLayout.LayoutParams
        layoutParams.height = binding.recycleChatList.height
        layoutParams.weight = 0f
        binding.recycleChatList.layoutParams = layoutParams
    }

    private fun unlockContentViewHeight() {
        binding.recycleChatList.postDelayed({
            val layoutParams = binding.recycleChatList.layoutParams as LinearLayout.LayoutParams
            layoutParams.weight = 1f
            binding.recycleChatList.layoutParams = layoutParams
        }, UNBLOCK_DELAY_TIME)
    }

    private fun setSoftKeyboardVisibility(isShowKeyboard: Boolean) {
        imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
        if (isShowKeyboard) {
            binding.etInput.requestFocus()
            imm!!.showSoftInput(binding.etInput, 0)
        } else {
            imm!!.hideSoftInputFromWindow(binding.etInput.windowToken, 0)
        }
    }

    private fun handlePanelSwitch(isEmojiPanel: Boolean) {
        setSoftKeyboardVisibility(false)
        binding.clBottomPanel.visibility = View.VISIBLE

        binding.rvEmoji.visibility = if (isEmojiPanel) View.VISIBLE else View.INVISIBLE
        binding.gvFunctionPanel.visibility = if (isEmojiPanel) View.INVISIBLE else View.VISIBLE
    }

    private fun hidePanel(showSoftKeyboard: Boolean) {
        if (binding.clBottomPanel.isShown) {
            binding.clBottomPanel.visibility = View.GONE
            if (showSoftKeyboard) {
                setSoftKeyboardVisibility(true)
            }
        }
    }

    private fun setListener() {
        setBackPressListener()
        binding.tvContactName.setOnLongClickListener {
            false
        }

        binding.gvFunctionPanel.selector = ColorDrawable(Color.TRANSPARENT)

        binding.tvSend.setOnClickListener {
            val message = binding.etInput.text.toString().trim()
            sendMessage(contactId, message, "0")
        }

        binding.recycleChatList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                if (recyclerView.canScrollVertically(-1) && hasHistory && !loading) {
                    loadMoreMessages()
                }
//                val layoutManager = checkNotNull(recyclerView.layoutManager as LinearLayoutManager?)
//                val firstVisiblePosition = layoutManager.findLastVisibleItemPosition()
//                if (tempIndex + 1 == messagesList[firstVisiblePosition].msgLocalId && hasHistory && !loading) {
//                    loadMoreMessages()
//                }
            }
        })
        binding.ivBack.setOnClickListener { finish() }

        binding.tvContactName.setOnClickListener {
            startActivity(
                Intent(
                    context,
                    ContactsDetailActivity::class.java
                )
            )
        }

        binding.ivMore.setOnClickListener {
            startActivity(
                Intent(
                    context,
                    ChatDetailActivity::class.java
                )
            )
        }

        binding.etInput.addTextChangedListener(
            afterTextChanged = { _ ->
                val isInputEmpty = binding.etInput.text.toString().isEmpty()
                binding.ivFunctionPanel.visibility = if (!isInputEmpty) View.GONE else View.VISIBLE
                binding.tvSend.visibility = if (isInputEmpty) View.GONE else View.VISIBLE
            }
        )

        binding.etInput.setOnEditorActionListener { _, actionId, event ->
            if (actionId == EditorInfo.IME_ACTION_DONE ||
                (event?.keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_DOWN)
            ) {
                val message = binding.etInput.text.toString().trim()
                sendMessage(contactId, message, "0")
                return@setOnEditorActionListener true
            }
            false
        }
    }

    private fun setupFunctionPanel() {
        strings?.add("[委屈]")
        val mEmojiAdapter = EmojiAdapter(strings)
        mEmojiAdapter.setOnItemClickListener(this)
        binding.rvEmoji.adapter = mEmojiAdapter

        val gridLayoutManager = GridLayoutManager(this, 7)
        binding.rvEmoji.layoutManager = gridLayoutManager

        val map: MutableMap<String, Int> = mutableMapOf()

        map["相册"] = R.drawable.ic_filled_picture
        map["拍摄"] = R.drawable.ic_filled_camera
        map["位置"] = R.drawable.ic_filled_location
        map["名片"] = R.drawable.ic_filled_personal
        map["转账"] = R.drawable.ic_filled_transfer
        map["文件"] = R.drawable.ic_folder_filled
        map["语音输入"] = R.drawable.ic_filled_voiceinput
        map["我的收藏"] = R.drawable.ic_filled_favorites

        val functionItems = mutableListOf<FunctionItem>()
        for (i in map.keys) {
            functionItems.add(
                FunctionItem(
                    i,
                    map[i]!!
                )
            )
        }

        val functionPanelAdapter = FunctionPanelAdapter(functionItems, this)
        binding.gvFunctionPanel.adapter = functionPanelAdapter
    }

    override fun onItemClick(position: Int) {
        runOnUiThread {
            val editable = binding.etInput.text
            // 获取当前光标位置
            val index = binding.etInput.selectionStart
            // 获取将要插入的表情符号
            val emoji = strings!![position]
            // 使用 ImageSpanUtil 插入表情符号
            insertEmoji(
                context,
                editable,
                binding.etInput.textSize.toInt(),
                emoji,
                index
            )
            // 设置新的光标位置
            binding.etInput.setSelection(index + emoji.length)
        }
    }

    private fun initView() {
        setupFunctionPanel()
        setRecycleView()
        contactNickname?.let {
            binding.tvContactName.text = contactNickname
        }
        setPanel()
    }

    private fun setPanel() {
        val layoutParams = binding.clBottomPanel.layoutParams
        layoutParams.height = softKeyboardHeight
        binding.clBottomPanel.layoutParams = layoutParams
    }

    private fun initData() {
        messagesBox = get().boxFor(Messages::class.java)
        softKeyboardHeight = getKeyboardHeight()
        mmkv.putString(CURRENT_CONTACT_ID, contactId)
    }

    private fun handleIntent(intent: Intent?) {
        intent?.let {
            contactId = intent.getStringExtra("contactId").toString()
            contactNickname = intent.getStringExtra("contactNickname")
            binding.tvContactName.text = contactNickname
        }
    }

    private fun setRecycleView() {
        val layoutManager = LinearLayoutManager(this)
        layoutManager.reverseLayout = true
        binding.recycleChatList.layoutManager = layoutManager
        chatAdapter = ChatAdapter(this, messagesList)
        binding.recycleChatList.adapter = chatAdapter
    }

    /**
     * 初次进入进行加载历史数据
     */
    private fun firstLoadData() {
        val messages = queryData(0, LIMIT + 1)
        if (messages.isNotEmpty()) {
            val size = messages.size
            hasHistory = size > LIMIT
            if (hasHistory) {
                val messages1 = messages.subList(0, LIMIT.toInt())
                messagesList.addAll(messages1)
                tempIndex = messages[size - 1].msgLocalId
            } else {
                messagesList.addAll(messages)
            }
        }
    }

    private fun loadMoreMessages() {
        if (loading) return

        loading = true

        val newMessages: List<Messages> = queryDataById(tempIndex, LIMIT + 1)

        val size = newMessages.size
        tempIndex = newMessages[size - 1].msgLocalId
        hasHistory = size > LIMIT

        if (newMessages.isNotEmpty()) {
            val messages1 = if (hasHistory) {
                newMessages.subList(0, LIMIT.toInt()).toMutableList()
            } else {
                newMessages.subList(0, newMessages.size).toMutableList()
            }

            val messagesSize = messagesList.size

            messagesList.addAll(messagesSize, messages1)
            binding.recycleChatList.post {
                chatAdapter!!.notifyItemRangeInserted(messagesSize, newMessages.size)
            }
        }

        loading = false
    }

    private fun observeLiveData() {
        if (webSocketService == null) {
            return
        }
        webSocketService!!.messageLivedata.observe(
            this
        ) { messages: Messages ->
            Log.d("haha11", messages.toString())

            if ("ack" != messages.type && username != messages.senderId) {
                val a = getCurrentContactId()
                if (messages.takerId == contactId) {
                    messagesList.addFirst(messages)
                    chatAdapter!!.notifyItemInserted(0)
                    binding.recycleChatList.smoothScrollToPosition(0)
                }
            }
        }
    }

    private fun sendMessage(talkerId: String, message: String, type: String) {
        val messages = Messages(
            content = message,
            timestamp = System.currentTimeMillis(),
            senderId = username,
            takerId = talkerId,
            type = type,
        )

        addData(messages)

        val id: Long = messagesBox.put(messages)

        val jsonObject = JSONObject()
        val jsonObject2 = JSONObject()
        jsonObject2.put("receiverId", contactId)
        jsonObject2.put("content", message)
        jsonObject2.put("msgLocalId", id)
        jsonObject.put("type", "single")
        jsonObject.put("body", jsonObject2)
        webSocketService!!.sendMessage(jsonObject.toString(), id)
        webSocketService!!.sendMessage(messages)

        binding.etInput.setText("")
    }

    private fun addData(messages: Messages) {
        messagesList.addFirst(messages)
        chatAdapter!!.notifyItemInserted(0)
        binding.recycleChatList.smoothScrollToPosition(0)
    }

    private fun queryData(offset: Long, limit: Long): List<Messages> {
        val query = messagesBox
            .query(Messages_.takerId.equal(contactId))
            .order(Messages_.timestamp, QueryBuilder.DESCENDING)
            .build()
        return query.find(offset, limit)
    }

    private fun queryDataById(msgLocalId: Long, limit: Long): List<Messages> {
        val query = messagesBox
            .query(Messages_.takerId.equal(contactId))
            .lessOrEqual(Messages_.msgLocalId, msgLocalId)
            .order(Messages_.timestamp, QueryBuilder.DESCENDING)
            .build()
        val offset = 0
        return query.find(offset.toLong(), limit)
    }

    private fun bindWebSocketService() {
        bindService(
            Intent(
                context,
                WebSocketService::class.java
            ),
            connection,
            BIND_AUTO_CREATE
        )
    }

    override fun onDestroy() {
        super.onDestroy()
        mmkv.putString(CURRENT_CONTACT_ID, "")
        if (bound) {
            unbindService(connection)
            bound = false
        }
    }
}
