package com.example.myapplication

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.inputmethodservice.InputMethodService
import android.media.AudioManager
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
import android.widget.HorizontalScrollView
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.core.app.NotificationCompat
import com.example.myapplication.data.WordDictionary
import com.example.myapplication.data.LanguageModelLoader
import com.example.myapplication.theme.ThemeManager
import com.example.myapplication.keyboard.KeyboardEnvironment
import com.example.myapplication.keyboard.MainKeyboard
import com.example.myapplication.keyboard.NumberKeyboard
import com.example.myapplication.keyboard.SymbolKeyboard
import com.example.myapplication.keyboard.AiKeyboard
import android.text.InputType
import android.view.KeyEvent
import android.os.SystemClock
import com.example.myapplication.network.AuthEventBus
import com.example.myapplication.network.AuthEvent
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import android.content.Intent
import android.view.inputmethod.ExtractedTextRequest
import android.graphics.drawable.GradientDrawable
import kotlin.math.abs



class MyInputMethodService : InputMethodService(), KeyboardEnvironment {

    // ====== 键盘视图 & 键盘对象 ======
    private var currentKeyboardView: View? = null

    private var mainKeyboardView: View? = null
    private var numberKeyboardView: View? = null
    private var symbolKeyboardView: View? = null
    private var aiKeyboardView: View? = null

    private var mainKeyboard: MainKeyboard? = null
    private var numberKeyboard: NumberKeyboard? = null
    private var symbolKeyboard: SymbolKeyboard? = null
    private var aiKeyboard: AiKeyboard? = null

    // 单键预览（只给建议栏等用，按键的预览已经放到各 Keyboard 里了）
    private var keyPreviewPopup: PopupWindow? = null

    private var lastWordForLM: String? = null  // 上一次输入的词

    private val wordDictionary = WordDictionary(this)          // 词库
    private var currentInput = StringBuilder()                 // 当前输入前缀
    private var completionSuggestions = emptyList<String>()    // 自动完成建议

    @Volatile private var isSpecialToken: BooleanArray = BooleanArray(0)

    private val suggestionStats by lazy { SuggestionStats(applicationContext) }
    private val specialTokens = setOf("<unk>", "<s>", "</s>")

    @Volatile private var bigramModel: com.example.myapplication.data.BigramModel? = null
    @Volatile private var word2id: Map<String, Int> = emptyMap()
    @Volatile private var id2word: List<String> = emptyList()
    @Volatile private var bigramReady: Boolean = false

    companion object {
        private const val TAG = "MyIME"
        private const val NOTIFICATION_CHANNEL_ID = "input_method_channel"
        private const val NOTIFICATION_ID = 1
    }
    // ================= 表情 =================
    private var emojiKeyboardView: View? = null
    private var emojiKeyboard: com.example.myapplication.keyboard.EmojiKeyboard? = null
    // =================上滑清空==================
    private var swipeHintPopup: PopupWindow? = null
    private var swipeClearPopup: PopupWindow? = null
    private var swipeClearPopupShown = false
    // 备份：上次“清空”前的全文
    @Volatile private var lastClearedText: String? = null
    @Volatile private var lastClearedSelStart: Int = 0
    @Volatile private var lastClearedSelEnd: Int = 0

    // ===== KeyboardEnvironment 实现所需属性 =====

    override val ctx: Context
        get() = this

    // override val layoutInflater get() = super.getLayoutInflater()

    override val mainHandler: Handler = Handler(Looper.getMainLooper())

    override val audioManager: AudioManager by lazy {
        getSystemService(AUDIO_SERVICE) as AudioManager
    }


    // ===== 输入视图状态 =====

    private var isInputViewShownFlag = false

    // Shift 状态
    private var isShiftOn = false

    // 删除长按
    private var isDeleting = false
    private val repeatDelInitialDelay = 350L
    private val repeatDelInterval = 50L
    private val refreshAfterEditDelayMs = 16L   // 1 帧
    private val refreshAfterEditRunnable = Runnable { refreshSuggestionsAfterEdit() }


    private val repeatDelRunnable = object : Runnable {
        override fun run() {
            if (!isDeleting) return
            deleteOne()
            mainHandler.postDelayed(this, repeatDelInterval)
        }
    }

    // ===== 颜色缓存 =====
    override var currentTextColor: ColorStateList =
        ColorStateList.valueOf(Color.parseColor("#000000"))//联想字体颜色
        private set

    override var currentBorderColor: ColorStateList =
        ColorStateList.valueOf(Color.parseColor("#1A000000"))
        private set

    override var currentBackgroundColor: ColorStateList =
        ColorStateList.valueOf(Color.TRANSPARENT)
        private set

    //主题更新
    private val themeListener: () -> Unit = {
        applyThemeAfterThemeChanged()
    }
    private fun applyThemeAfterThemeChanged() {
        mainKeyboard?.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
        numberKeyboard?.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
        symbolKeyboard?.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
        aiKeyboard?.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
        emojiKeyboard?.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)

        currentKeyboardView?.apply {
            requestLayout()
            invalidate()
        }
    }
    // 键盘关闭
    override fun getInputConnection(): InputConnection? {
        return currentInputConnection
    }

    override fun hideKeyboard() {
        currentInputConnection?.finishComposingText()
        requestHideSelf(0)
    }

    // 副字符映射表（主键盘上滑）
    private val swipeAltMap: Map<Char, Char> = mapOf(
        'q' to '1',
        'w' to '2',
        'e' to '3',
        'r' to '4',
        't' to '5',
        'y' to '6',
        'u' to '7',
        'i' to '8',
        'o' to '9',
        'p' to '0',

        'a' to '!',
        's' to '@',
        'd' to '#',
        'f' to '$',
        'g' to '%',
        'h' to '^',
        'j' to '&',
        'k' to '*',
        'l' to '(',

        'z' to ')',
        'x' to '-',
        'c' to '+',
        'v' to '=',
        'b' to '?',
        'n' to '/',
        'm' to '.'
    )

    // ================= 生命周期 =================

    override fun onCreate() {
        super.onCreate()

        ThemeManager.ensureBuiltInThemesInstalled(this)
        ThemeManager.init(this)
    
        ThemeManager.addThemeChangeListener(themeListener)

        // 异步加载词典与 bigram 模型
        Thread {
            // 1) Trie 词典
            try {
                wordDictionary.loadIfNeeded()
            } catch (e: Throwable) {
                Log.w(TAG, "Trie load failed: ${e.message}", e)
            }

            // 2) Bigram 模型
            try {
                val m = LanguageModelLoader.load(this)

                require(m.biRowptr.size == m.vocab.size + 1) {
                    "biRowptr size ${m.biRowptr.size} != vocab.size+1 ${m.vocab.size + 1}"
                }
                require(m.biCols.size == m.biLogp.size) {
                    "biCols size ${m.biCols.size} != biLogp size ${m.biLogp.size}"
                }

                bigramModel = m

                val map = HashMap<String, Int>(m.vocab.size * 2)
                m.vocab.forEachIndexed { idx, w -> map[w.lowercase()] = idx }
                word2id = map
                id2word = m.vocab

                isSpecialToken = BooleanArray(id2word.size)
                for (i in id2word.indices) {
                    if (specialTokens.contains(id2word[i])) {
                        isSpecialToken[i] = true
                    }
                }

                bigramReady = true
            } catch (e: Throwable) {
                bigramReady = false
                Log.w(TAG, "Bigram load failed: ${e.message}", e)
            }
        }.start()

        createNotificationChannelIfNeeded()
        tryStartForegroundSafe()

        // 监听认证事件
        // CoroutineScope(Dispatchers.Main).launch {
        //     AuthEventBus.events.collectLatest { event ->
        //         if (event is AuthEvent.TokenExpired) {
        //             // 启动 MainActivity 并跳转到登录页面
        //             val intent = Intent(this@MyInputMethodService, MainActivity::class.java).apply {
        //                 flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        //                 putExtra("navigate_to", "loginFragment")
        //             }
        //             startActivity(intent)
        //         } else if (event is AuthEvent.GenericError) {
        //             // 显示错误提示
        //             android.widget.Toast.makeText(this@MyInputMethodService, "请求失败: ${event.message}", android.widget.Toast.LENGTH_SHORT).show()
        //         }
        //     }
        // }
    }
    
    // 输入法状态变化
    private fun createNotificationChannelIfNeeded() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                NOTIFICATION_CHANNEL_ID,
                "输入法服务",
                NotificationManager.IMPORTANCE_LOW
            ).apply { description = "保持输入法服务运行" }

            val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            manager.createNotificationChannel(channel)
        }
    }

    private fun tryStartForegroundSafe() {
        try {
            val notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
                .setContentTitle("输入法服务")
                .setContentText("输入法正在运行")
                .setSmallIcon(android.R.drawable.ic_dialog_info)
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .setOngoing(true)
                .build()

            startForeground(NOTIFICATION_ID, notification)
            Log.i(TAG, "startForeground success")
        } catch (e: Exception) {
            Log.w(TAG, "startForeground failed: ${e.message}")
        }
    }

    // 创建输入视图：此处只负责选择主键盘
    override fun onCreateInputView(): View {
        val keyboard = ensureMainKeyboard()
        currentKeyboardView = keyboard.rootView
        mainKeyboardView = keyboard.rootView
        return keyboard.rootView
    }

    override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
        super.onStartInputView(info, restarting)
        isInputViewShownFlag = true
    
        // 根据当前输入框类型自动切换键盘
        if (isNumberEditor(info)) {
            showNumberKeyboard()
        } else {
            showMainKeyboard()
        }
    }
    
     //判断当前输入框是否是数字相关输入框：
    private fun isNumberEditor(info: EditorInfo?): Boolean {
        if (info == null) return false

        val inputType = info.inputType
        val clazz = inputType and InputType.TYPE_MASK_CLASS
        val variation = inputType and InputType.TYPE_MASK_VARIATION

        // 纯数字、电话输入
        if (clazz == InputType.TYPE_CLASS_NUMBER || clazz == InputType.TYPE_CLASS_PHONE) {
            return true
        }

        // 数字密码：numberPassword
        if (clazz == InputType.TYPE_CLASS_TEXT &&
            variation == InputType.TYPE_TEXT_VARIATION_PASSWORD &&
            (inputType and InputType.TYPE_NUMBER_FLAG_DECIMAL != 0 ||
            inputType and InputType.TYPE_NUMBER_FLAG_SIGNED != 0)
        ) {
            return true
        }

        return false
    }

    override fun onFinishInput() {
        super.onFinishInput()
        clearEditorState()
    }

    override fun onFinishInputView(finishingInput: Boolean) {
        super.onFinishInputView(finishingInput)
        isInputViewShownFlag = false
        stopRepeatDelete()

         // 清理本次输入状态
        clearEditorState()

        mainHandler.postDelayed({
            if (!isInputViewShownFlag) {
                try {
                    hideWindow()
                } catch (e: Exception) {
                    Log.w(TAG, "hideWindow failed: ${e.message}")
                }
            }
        }, 50)
    }

    override fun onDestroy() {
        ThemeManager.removeThemeChangeListener(themeListener)
        stopRepeatDelete()
        super.onDestroy()
    }

    // ================= KeyboardEnvironment：键盘切换 =================

    private fun ensureMainKeyboard(): MainKeyboard {
        if (mainKeyboard == null) {
            mainKeyboard = MainKeyboard(
                env = this,
                swipeAltMap = swipeAltMap,
                onToggleShift = {
                    isShiftOn = !isShiftOn
                    isShiftOn
                }
            )
            mainKeyboardView = mainKeyboard!!.rootView
    
            // 初始状态：隐藏联想条，显示控制面板
            mainKeyboardView
                ?.findViewById<HorizontalScrollView>(R.id.completion_scroll)
                ?.visibility = View.GONE
    
            mainKeyboardView
                ?.findViewById<LinearLayout>(R.id.control_layout)
                ?.visibility = View.VISIBLE
    
            // 删除键长按连删
            val delId = resources.getIdentifier("key_del", "id", packageName)
            mainKeyboardView?.findViewById<View?>(delId)?.let { attachRepeatDeleteInternal(it) }
        }
        return mainKeyboard!!
    }

    // 上滑清空
    private fun clearAllAndBackup() {
        val ic = currentInputConnection ?: return
    
        val et = try {
            ic.getExtractedText(ExtractedTextRequest(), 0)
        } catch (_: Throwable) {
            null
        } ?: return
    
        val full = et.text?.toString().orEmpty()
        if (full.isEmpty()) {
            // 已经空了就不做
            clearEditorState()
            return
        }
    
        // 备份
        lastClearedText = full
        lastClearedSelStart = et.selectionStart.coerceIn(0, full.length)
        lastClearedSelEnd = et.selectionEnd.coerceIn(0, full.length)
    
        // 清空：全选 -> 用空串替换
        ic.beginBatchEdit()
        try {
            ic.setSelection(0, full.length)
            ic.commitText("", 1)
        } finally {
            ic.endBatchEdit()
        }
    
        clearEditorState()
        
        // 清空后立即更新所有键盘的按钮可见性
        mainHandler.post {
            mainKeyboard?.updateRevokeButtonVisibility()
            numberKeyboard?.updateRevokeButtonVisibility()
            symbolKeyboard?.updateRevokeButtonVisibility()
        }
    }

    // 回填上次清空的文本
    override fun revokeLastClearedText() {
        val ic = currentInputConnection ?: return
        val text = lastClearedText ?: return
        
        // 回填文本并恢复光标位置
        ic.beginBatchEdit()
        try {
            // 先清空当前内容
            val currentText = ic.getTextBeforeCursor(1000, 0)?.toString().orEmpty()
            if (currentText.isNotEmpty()) {
                ic.setSelection(0, currentText.length)
                ic.commitText("", 1)
            }
            
            // 回填备份的文本
            ic.commitText(text, 1)
            
            // 恢复光标位置
            val selStart = lastClearedSelStart.coerceIn(0, text.length)
            val selEnd = lastClearedSelEnd.coerceIn(0, text.length)
            ic.setSelection(selStart, selEnd)
            
            // 清空备份，避免重复回填
            lastClearedText = null
            lastClearedSelStart = 0
            lastClearedSelEnd = 0
        } finally {
            ic.endBatchEdit()
        }
    }

    // 检查是否有可回填的文本
    override fun hasClearedText(): Boolean {
        return lastClearedText != null
    }

    private fun showSwipeClearHint(anchor: View, text: String = "Clear") {
        mainHandler.post {
            if (swipeClearPopupShown) return@post
            swipeClearPopupShown = true
    
            // 先关旧的
            swipeClearPopup?.dismiss()
            swipeClearPopup = null
    
            val dp = resources.displayMetrics.density
    
            // ✅ 这里“对标按键预览气泡”：优先用你项目里可能已有的 preview 背景 drawable
            // 你如果确定资源名，就把 getIdentifier 换成 R.drawable.xxx
            val previewBgId = resources.getIdentifier("key_preview_bg", "drawable", packageName)
                .takeIf { it != 0 }
                ?: resources.getIdentifier("popup_preview_bg", "drawable", packageName)
                    .takeIf { it != 0 }
    
            val tv = TextView(this).apply {
                this.text = text
                textSize = 16f
                setTextColor(Color.BLACK)
                setPadding(20, 10, 20, 10)
                background = GradientDrawable().apply {
                    shape = GradientDrawable.RECTANGLE
                    cornerRadius = 16f
                    setColor(Color.WHITE)
                    setStroke(1, Color.parseColor("#33000000"))
                }
                gravity = Gravity.CENTER
            }
    
            tv.measure(
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            )
            val w = tv.measuredWidth
            val h = tv.measuredHeight
    
            val popup = PopupWindow(
                tv,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                false
            ).apply {
                isClippingEnabled = false
                elevation = 10f
            }
            swipeClearPopup = popup
    
            // ✅ 用 IME 自己的 decorView 做 parent（输入法里最稳）
            val parent = window?.window?.decorView ?: anchor.rootView
    
            // ✅ 坐标用 inWindow，跟 decorView 同坐标系
            val loc = IntArray(2)
            anchor.getLocationInWindow(loc)
    
            val x = loc[0] + anchor.width / 2 - w / 2
            val y = loc[1] - h - (10 * dp).toInt()
    
            try {
                popup.showAtLocation(parent, Gravity.NO_GRAVITY, x, y)
            } catch (t: Throwable) {
                swipeClearPopupShown = false
                Log.w(TAG, "showSwipeClearHint failed: ${t.message}", t)
            }
        }
    }
    
    //松手关闭气泡
    private fun dismissSwipeClearHint() {
        mainHandler.post {
            swipeClearPopup?.dismiss()
            swipeClearPopup = null
            swipeClearPopupShown = false
        }
    }
    

    private fun ensureNumberKeyboard(): NumberKeyboard {
        if (numberKeyboard == null) {
            numberKeyboard = NumberKeyboard(this)
            numberKeyboardView = numberKeyboard!!.rootView
    
            //  数字键盘删除键 key_del
            val delId = resources.getIdentifier("key_del", "id", packageName)
            numberKeyboardView?.findViewById<View?>(delId)?.let { attachRepeatDeleteInternal(it) }
        }
        return numberKeyboard!!
    }
    
    private fun ensureSymbolKeyboard(): SymbolKeyboard {
        if (symbolKeyboard == null) {
            symbolKeyboard = SymbolKeyboard(this)
            symbolKeyboardView = symbolKeyboard!!.rootView
    
            //  符号键盘删除键 key_del
            val delId = resources.getIdentifier("key_del", "id", packageName)
            symbolKeyboardView?.findViewById<View?>(delId)?.let { attachRepeatDeleteInternal(it) }
        }
        return symbolKeyboard!!
    }

    private fun ensureAiKeyboard(): AiKeyboard {
        if (aiKeyboard == null) {
            aiKeyboard = AiKeyboard(this)
            aiKeyboardView = aiKeyboard!!.rootView
        }
        return aiKeyboard!!
    }

    override fun showMainKeyboard() {
        val kb = ensureMainKeyboard()
        currentKeyboardView = kb.rootView
        setInputView(kb.rootView)
        kb.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
    }
    
    override fun showNumberKeyboard() {
        val kb = ensureNumberKeyboard()
        currentKeyboardView = kb.rootView
        setInputView(kb.rootView)
        kb.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
    }
    
    override fun showSymbolKeyboard() {
        val kb = ensureSymbolKeyboard()
        currentKeyboardView = kb.rootView
        setInputView(kb.rootView)
        kb.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
    }
    
    override fun showAiKeyboard() {
        val kb = ensureAiKeyboard()
        currentKeyboardView = kb.rootView
        setInputView(kb.rootView)
        kb.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
    }

    override fun showEmojiKeyboard() {
        val kb = ensureEmojiKeyboard()
        currentKeyboardView = kb.rootView
        setInputView(kb.rootView)
        kb.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
    }

    // Emoji 键盘
    private fun ensureEmojiKeyboard(): com.example.myapplication.keyboard.EmojiKeyboard {
        if (emojiKeyboard == null) {
            emojiKeyboard = com.example.myapplication.keyboard.EmojiKeyboard(this)
            emojiKeyboardView = emojiKeyboard!!.rootView
    
            // Emoji 页面删除键也支持长按连删（复用你现有 attachRepeatDeleteInternal）
            val delId = resources.getIdentifier("key_del", "id", packageName)
            emojiKeyboardView?.findViewById<View?>(delId)?.let { attachRepeatDeleteInternal(it) }
        }
        return emojiKeyboard!!
    }

    

    // ================== 文本输入核心逻辑 ==================

    /** 获取上一个完整英文词（纯字母），统一小写 */
    private fun getPrevWordBeforeCursor(maxLen: Int = 128): String? {
        val before = currentInputConnection
            ?.getTextBeforeCursor(maxLen, 0)
            ?.toString()
            ?: return null

        // 如果最后一个字符是字母，说明词不完整，返回 null
        if (before.isNotEmpty() && before.last().isLetter()) return null

        val toks = before
            .replace(Regex("[^A-Za-z]"), " ")
            .trim()
            .split(Regex("\\s+"))

        if (toks.isEmpty()) return null

        val word = toks.last().lowercase()
        if (word.isEmpty()) return null
        return word
    }

    // 提交一个字符（原 sendKey）
    override fun commitKey(c: Char) {
        val ic = currentInputConnection ?: return

        // 如果有清空过的文本，用户开始输入新内容时清空备份
        if (lastClearedText != null) {
            lastClearedText = null
            lastClearedSelStart = 0
            lastClearedSelEnd = 0
        }

        val toSend = if (isShiftOn && c in 'a'..'z') c.uppercaseChar() else c
        ic.commitText(toSend.toString(), 1)

        when {
            c.isLetter() -> {
                val prefix = getCurrentWordPrefix()
                updateCompletionsAndRender(prefix)
            }
            c == ' ' || !c.isLetter() -> {
                updateCompletionsAndRender(prefix = "")
            }
        }

        playKeyClick()
    }

    // 删除一个字符（原 handleBackspace）
    override fun deleteOne() {
        val ic = currentInputConnection ?: return
    
        // 删除时少做 IPC：selectedText 也可能慢，所以只在需要时取
        val selected = ic.getSelectedText(0)
        if (!selected.isNullOrEmpty()) {
            // 删选区
            ic.commitText("", 1)
        } else {
            // 删光标前一个字符（更同步）
            ic.deleteSurroundingText(1, 0)
        }
    
        scheduleRefreshSuggestions()
    
        playKeyClick()
    }

    private fun refreshSuggestionsAfterEdit() {
        val ic = currentInputConnection ?: return
    
        // ✅ 判空只取 1 个字符，避免 256/256 的 IPC 开销
        val before1 = ic.getTextBeforeCursor(1, 0)?.toString().orEmpty()
        val after1  = ic.getTextAfterCursor(1, 0)?.toString().orEmpty()
        val editorReallyEmpty = before1.isEmpty() && after1.isEmpty()
    
        if (editorReallyEmpty) {
            clearEditorState()
        } else {
            // prefix 也不要取太长
            val prefix = getCurrentWordPrefix(maxLen = 64)
            updateCompletionsAndRender(prefix)
        }
    }
    
    private fun scheduleRefreshSuggestions() {
        mainHandler.removeCallbacks(refreshAfterEditRunnable)
        mainHandler.postDelayed(refreshAfterEditRunnable, refreshAfterEditDelayMs)
    }
    
    

    // 发送（标准 SEND + 回车 fallback）
    override fun performSendAction() {
        val ic = currentInputConnection ?: return
        val info = currentInputEditorInfo
    
        var handled = false
    
        if (info != null) {
            // 取出当前 EditText 声明的 action
            val actionId = info.imeOptions and EditorInfo.IME_MASK_ACTION
    
            // 只有当它明确是 IME_ACTION_SEND 时，才当“发送”用
            if (actionId == EditorInfo.IME_ACTION_SEND) {
                handled = ic.performEditorAction(actionId)
            }
        }
    
        // 如果当前输入框不支持 SEND 或者 performEditorAction 返回了 false
        // 就降级为“标准回车”
        if (!handled) {
            sendEnterKey(ic)
        }
    
        playKeyClick()
        clearEditorState()
    }

    private fun sendEnterKey(ic: InputConnection) {
        // 按下+抬起 KEYCODE_ENTER
        ic.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER))
        ic.sendKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER))
    }

    // 按键音效
    override fun playKeyClick() {
        try {
            audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK)
        } catch (_: Throwable) {
        }
    }

    // ================== 补全 & 联想 ==================

    override fun getCurrentWordPrefix(maxLen: Int): String {
        val before = currentInputConnection?.getTextBeforeCursor(maxLen, 0)?.toString() ?: ""
        val match = Regex("[A-Za-z]+$").find(before)
        return (match?.value ?: "").lowercase()
    }

    // 统一处理补全/联想
    override fun updateCompletionsAndRender(prefix: String) {
        val ic = currentInputConnection
    
        // 先判断整个编辑框是不是“真的空”
        val beforeAll = ic?.getTextBeforeCursor(256, 0)?.toString().orEmpty()
        val afterAll = ic?.getTextAfterCursor(256, 0)?.toString().orEmpty()
        val editorReallyEmpty = beforeAll.isEmpty() && afterAll.isEmpty()
    
        // 当前输入前缀
        currentInput.clear()
        currentInput.append(prefix)
    
        // 如果整个编辑框都是空的：直接清空联想 & 刷新 UI，什么都不算
        if (editorReallyEmpty) {
            clearEditorState()
            return
        }
    
        // 否则再去算 lastWord
        val lastWord = getPrevWordBeforeCursor()
    
        Thread {
            val list = try {
                if (prefix.isEmpty()) {
                    if (lastWord == null) {
                        // 这里也保持 emptyList，防止空前缀 + 无上文时走全局高频随机词
                        emptyList()
                    } else {
                        suggestWithBigram("", lastWord, topK = 20)
                    }
                } else {
                    val fromBi = suggestWithBigram(prefix, lastWord, topK = 20)
                    if (fromBi.isNotEmpty()) {
                        fromBi.filter { it != prefix }
                    } else {
                        wordDictionary.wordTrie.startsWith(prefix)
                            .take(20)
                            .filter { it != prefix }
                    }
                }
            } catch (_: Throwable) {
                if (prefix.isNotEmpty()) {
                    wordDictionary.wordTrie.startsWith(prefix)
                        .take(20)
                        .filterNot { it == prefix }
                } else {
                    emptyList()
                }
            }
    
            mainHandler.post {
                completionSuggestions = suggestionStats.sortByCount(list.distinct().take(20))
                showCompletionSuggestions()
            }
        }.start()
    }
    

    // 显示自动完成建议（布局不变）
    private fun showCompletionSuggestions() {
        mainHandler.post {
            val suggestionsView =
                mainKeyboardView?.findViewById<LinearLayout>(R.id.completion_suggestions)

            // 新增：联想滚动条 & 控制面板
            val completionScroll =
                mainKeyboardView?.findViewById<HorizontalScrollView>(R.id.completion_scroll)
            val controlLayout =
                mainKeyboardView?.findViewById<LinearLayout>(R.id.control_layout)

            val suggestions = (0..20).map { i ->
                mainKeyboardView?.findViewById<TextView>(
                    resources.getIdentifier("suggestion_$i", "id", packageName)
                )
            }

            // 当前前缀
            val prefix = getCurrentWordPrefix()
            val hasPrefix = prefix.isNotEmpty()
            val hasSuggestions = completionSuggestions.isNotEmpty()

            // 判断光标前是否有任何输入（“没有输入”的判断）
            val hasAnyInput = try {
                val ic = currentInputConnection
                val before = ic?.getTextBeforeCursor(1, 0)
                !before.isNullOrEmpty()
            } catch (_: Throwable) {
                false
            }

            // 当：没有前缀 && 没有联想/补全词 && 光标前也没有任何输入
            // -> 隐藏联想条，显示控制面板
            if (!hasPrefix && !hasSuggestions && !hasAnyInput) {
                completionScroll?.visibility = View.GONE
                controlLayout?.visibility = View.VISIBLE
            } else {
                completionScroll?.visibility = View.VISIBLE
                controlLayout?.visibility = View.GONE
            }

            // suggestion_0 显示 prefix
            suggestions[0]?.text = prefix
            suggestions[0]?.visibility = if (prefix.isEmpty()) View.GONE else View.VISIBLE
            suggestions[0]?.setOnClickListener {
                insertCompletion(prefix)
            }

            // suggestion_1.. 按 completionSuggestions 填充
            suggestions.drop(1).forEachIndexed { index, textView ->
                textView?.text = completionSuggestions.getOrNull(index) ?: ""
                if (index < completionSuggestions.size) {
                    textView?.visibility = View.VISIBLE
                    textView?.setOnClickListener {
                        suggestionStats.incClick(completionSuggestions[index])
                        insertCompletion(completionSuggestions[index])
                    }
                } else {
                    textView?.visibility = View.GONE
                    textView?.setOnClickListener(null)
                }
            }

            scrollSuggestionsToStart()
        }
    }


    // 自动滚回到最左边
    private fun scrollSuggestionsToStart() {
        val sv = mainKeyboardView?.findViewById<HorizontalScrollView>(R.id.completion_scroll)
        sv?.post { sv.fullScroll(View.FOCUS_LEFT) }
    }

    // 插入一个补全词
    private fun insertCompletion(word: String) {
        val ic = currentInputConnection ?: return

        ic.beginBatchEdit()
        try {
            val len = currentInput.length
            if (len > 0) ic.deleteSurroundingText(len, 0)
            ic.commitText("$word ", 1)
        } finally {
            ic.endBatchEdit()
        }

        currentInput.clear()
        // 补完一个词后，直接联想下一个词
        updateCompletionsAndRender(prefix = "")
    }

    // ================== 长按删除 ==================
    private fun attachRepeatDeleteInternal(view: View) {
        val dp = resources.displayMetrics.density
        val triggerUp = 48f * dp                 // 触发“准备清空”的上滑距离
        val cancelBack = 48f * dp                // 回滑取消阈值（小于 triggerUp，防抖）
        val maxDx = 48f * dp
    
        var downX = 0f
        var downY = 0f
    
        var pendingSwipeClear = false            // 是否处于“准备清空”
        var resumeDeletingAfterCancel = false    // 取消后是否要恢复连删
    
        fun startRepeatDeleteNow() {
            if (!isDeleting) {
                isDeleting = true
                mainHandler.postDelayed(repeatDelRunnable, repeatDelInitialDelay)
                deleteOne()
            }
        }
    
        view.setOnLongClickListener {
            // 只要不是准备清空，就允许长按连删
            if (!pendingSwipeClear) {
                startRepeatDeleteNow()
            }
            true
        }
    
        view.setOnTouchListener { _, event ->
            when (event.actionMasked) {
    
                android.view.MotionEvent.ACTION_DOWN -> {
                    downX = event.x
                    downY = event.y
                    pendingSwipeClear = false
                    resumeDeletingAfterCancel = false
                    dismissSwipeClearHint()
                    false
                }
    
                android.view.MotionEvent.ACTION_MOVE -> {
                    val dy = event.y - downY      // 上滑 dy < 0
                    val dx = abs(event.x - downX)
                    if (dx > maxDx) return@setOnTouchListener pendingSwipeClear
    
                    // 1) 还没进入准备清空：检测上滑触发
                    if (!pendingSwipeClear) {
                        if (-dy >= triggerUp) {
                            pendingSwipeClear = true
    
                            // 如果此时正在连删（长按已触发），记录一下，方便取消时恢复
                            resumeDeletingAfterCancel = isDeleting
                            stopRepeatDelete()
    
                            showSwipeClearHint(view, "Clear")
                            return@setOnTouchListener true
                        }
                        return@setOnTouchListener false
                    }
    
                    // 2) 已经进入准备清空：允许“回滑取消”
                    // 当你往下滑回来，dy 变大（不那么负），达到 cancelBack 就取消
                    if (-dy <= cancelBack) {
                        pendingSwipeClear = false
                        dismissSwipeClearHint()
    
                        // 如果之前是长按连删途中进入的准备清空，那取消后恢复连删
                        if (resumeDeletingAfterCancel) {
                            resumeDeletingAfterCancel = false
                            startRepeatDeleteNow()
                        }
                        return@setOnTouchListener false
                    }
    
                    // 仍处于准备清空：持续消费，保证能收到 UP 来决定是否清空
                    true
                }
    
                android.view.MotionEvent.ACTION_UP,
                android.view.MotionEvent.ACTION_CANCEL,
                android.view.MotionEvent.ACTION_OUTSIDE -> {
                
                    // 如果我们处于“准备清空”，才由我们接管结束逻辑
                    if (pendingSwipeClear) {
                        stopRepeatDelete()
                
                        if (event.actionMasked == android.view.MotionEvent.ACTION_UP) {
                            clearAllAndBackup()
                        }
                
                        pendingSwipeClear = false
                        resumeDeletingAfterCancel = false
                        dismissSwipeClearHint()
                
                        // 消费 UP，避免 click/longclick 再触发
                        return@setOnTouchListener true
                    }
                
                    // 不在准备清空：不要吃掉 UP/CANCEL，让 View 收到 UP 取消长按检测
                    stopRepeatDelete() // 可留可不留；一般点按不会进入 isDeleting
                    pendingSwipeClear = false
                    resumeDeletingAfterCancel = false
                    dismissSwipeClearHint()
                
                    return@setOnTouchListener false
                }
    
                else -> false
            }
        }
    }
    
    

    private fun stopRepeatDelete() {
        if (isDeleting) {
            isDeleting = false
            mainHandler.removeCallbacks(repeatDelRunnable)
        }
    }

    // ================== 主题 / 样式接口（保留原 API） ==================

    // 键盘背景大图（LinearLayout R.id.background 用的）
    fun setKeyboardBackground(@DrawableRes resId: Int) {
        mainHandler.post {
            mainKeyboardView
                ?.findViewById<LinearLayout>(R.id.background)
                ?.setBackgroundResource(resId)

            numberKeyboardView
                ?.findViewById<LinearLayout>(R.id.background)
                ?.setBackgroundResource(resId)

            symbolKeyboardView
                ?.findViewById<LinearLayout>(R.id.background)
                ?.setBackgroundResource(resId)

            aiKeyboardView
                ?.findViewById<LinearLayout>(R.id.background)
                ?.setBackgroundResource(resId)
        }
    }

    // ===== 文字颜色 =====

    fun setKeyTextColorInt(@ColorInt colorInt: Int) {
        setKeyTextColor(ColorStateList.valueOf(colorInt))
    }

    fun setKeyTextColor(colorStateList: ColorStateList) {
        currentTextColor = colorStateList
        mainHandler.post { applyThemeToAllKeyboards() }
    }

    fun setKeyTextColorWithStates() {
        val states = arrayOf(
            intArrayOf(android.R.attr.state_pressed),
            intArrayOf(android.R.attr.state_activated),
            intArrayOf()
        )

        val colors = intArrayOf(
            Color.parseColor("#222222"), // pressed
            Color.parseColor("#2563EB"), // activated
            Color.BLACK                  // default
        )

        setKeyTextColor(ColorStateList(states, colors))
    }

    // ===== 边框颜色 =====

    fun setKeyBorderColorInt(@ColorInt colorInt: Int) {
        setKeyBorderColor(ColorStateList.valueOf(colorInt))
    }

    fun setKeyBorderColor(colorStateList: ColorStateList) {
        currentBorderColor = colorStateList
        mainHandler.post { applyThemeToAllKeyboards() }
    }

    // ===== 背景颜色（如果你有用纯色背景的话） =====

    fun setKeyBackgroundColor(colorStateList: ColorStateList) {
        currentBackgroundColor = colorStateList
        mainHandler.post { applyThemeToAllKeyboards() }
    }

    fun setKeyBackgroundColorInt(@ColorInt colorInt: Int) {
        setKeyBackgroundColor(ColorStateList.valueOf(colorInt))
    }

    private fun applyThemeToAllKeyboards() {
        mainKeyboard?.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
        numberKeyboard?.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
        symbolKeyboard?.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
        aiKeyboard?.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
        emojiKeyboard?.applyTheme(currentTextColor, currentBorderColor, currentBackgroundColor)
    }

    // ================== bigram & 联想实现 ==================

    private fun suggestWithBigram(prefix: String, lastWord: String?, topK: Int = 20): List<String> {
        val m = bigramModel
        if (m == null || !bigramReady) {
            return if (prefix.isNotEmpty()) {
                wordDictionary.wordTrie.startsWith(prefix).take(topK)
            } else {
                emptyList()
            }
        }

        val pf = prefix.lowercase()
        val last = lastWord?.lowercase()
        val lastId = last?.let { word2id[it] }

        if (lastId != null && lastId >= 0 && lastId + 1 < m.biRowptr.size) {
            val start = m.biRowptr[lastId]
            val end = m.biRowptr[lastId + 1]

            if (start in 0..end && end <= m.biCols.size) {

                // 带前缀过滤
                val buf = ArrayList<Pair<String, Float>>(maxOf(0, end - start))
                var i = start
                while (i < end) {
                    val nextId = m.biCols[i]
                    if (nextId in id2word.indices && !isSpecialToken[nextId]) {
                        val w = id2word[nextId]
                        if (pf.isEmpty() || w.startsWith(pf)) {
                            buf.add(w to m.biLogp[i])
                        }
                    }
                    i++
                }
                if (buf.isNotEmpty()) return topKByScore(buf, topK)

                // 无前缀兜底
                val allBuf = ArrayList<Pair<String, Float>>(maxOf(0, end - start))
                i = start
                while (i < end) {
                    val nextId = m.biCols[i]
                    if (nextId in id2word.indices && !isSpecialToken[nextId]) {
                        allBuf.add(id2word[nextId] to m.biLogp[i])
                    }
                    i++
                }
                if (allBuf.isNotEmpty()) return topKByScore(allBuf, topK)
            }
        }

        // —— 无上文 或 无出边 —— 
        return if (pf.isNotEmpty()) {
            wordDictionary.wordTrie.startsWith(pf).take(topK)
        } else {
            unigramTopKFiltered(topK)
        }
    }

    private fun unigramTopKFiltered(topK: Int): List<String> {
        val m = bigramModel ?: return emptyList()
        if (!bigramReady) return emptyList()

        val heap = java.util.PriorityQueue<Pair<String, Float>>(topK.coerceAtLeast(1)) { a, b ->
            a.second.compareTo(b.second)
        }

        var i = 0
        val n = id2word.size
        while (i < n) {
            if (!isSpecialToken[i]) {
                heap.offer(id2word[i] to m.uniLogp[i])
                if (heap.size > topK) heap.poll()
            }
            i++
        }

        val out = ArrayList<String>(heap.size)
        while (heap.isNotEmpty()) out.add(heap.poll().first)
        out.reverse()
        return out
    }

    private fun topKByScore(pairs: List<Pair<String, Float>>, k: Int): List<String> {
        val heap = java.util.PriorityQueue<Pair<String, Float>>(k.coerceAtLeast(1)) { a, b ->
            a.second.compareTo(b.second)
        }
        for (p in pairs) {
            heap.offer(p)
            if (heap.size > k) heap.poll()
        }
        val out = ArrayList<String>(heap.size)
        while (heap.isNotEmpty()) out.add(heap.poll().first)
        out.reverse()
        return out
    }

    override fun onUpdateSelection(
        oldSelStart: Int,
        oldSelEnd: Int,
        newSelStart: Int,
        newSelEnd: Int,
        candidatesStart: Int,
        candidatesEnd: Int
    ) {
        super.onUpdateSelection(
            oldSelStart,
            oldSelEnd,
            newSelStart,
            newSelEnd,
            candidatesStart,
            candidatesEnd
        )

        val ic = currentInputConnection ?: return

        // 这里不用取太多，1 个字符就够判断是否为空了
        val before = ic.getTextBeforeCursor(1, 0)?.toString().orEmpty()
        val after = ic.getTextAfterCursor(1, 0)?.toString().orEmpty()

        // 当编辑框光标前后都没有任何字符，说明真的完全空了
        if (before.isEmpty() && after.isEmpty()) {
            clearEditorState()
        }
    }


    // 清理本次编辑框相关的状态（光标、联想、长按等）
    private fun clearEditorState() {
        // 1. 文本联想/补全相关
        currentInput.clear()
        completionSuggestions = emptyList()
        lastWordForLM = null

        // 2. Shift 状态
        isShiftOn = false

        // 3. 停止长按删除
        stopRepeatDelete()

        // 4. UI：联想条隐藏 & 控制面板显示
        mainHandler.post {
            val completionScroll =
                mainKeyboardView?.findViewById<HorizontalScrollView>(R.id.completion_scroll)
            val controlLayout =
                mainKeyboardView?.findViewById<LinearLayout>(R.id.control_layout)

            completionScroll?.visibility = View.GONE
            controlLayout?.visibility = View.VISIBLE

            // 再让联想区域里的文本都清空一下
            val suggestions = (0..20).map { i ->
                mainKeyboardView?.findViewById<TextView>(
                    resources.getIdentifier("suggestion_$i", "id", packageName)
                )
            }
            suggestions.forEach { tv ->
                tv?.text = ""
                tv?.visibility = View.GONE
                tv?.setOnClickListener(null)
            }
        }
    }
}
