/*
    Предрейсовые осмотры - мобильное приложение
    Главная Activity: встроенный сканер — сессия по триггеру, ENTER в буфер как \n, отправка по таймауту неактивности
*/

package com.parus_pre_trip_inspections

import android.os.Handler
import android.os.Looper
import android.view.KeyCharacterMap
import android.view.KeyEvent
import android.view.Window
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate

class MainActivity : ReactActivity() {

    private val scannerBuffer = StringBuilder()
    private var scannerSessionActive = false
    private var lastScannerKeyTime = 0L
    private var lastBarcodeSentTime = 0L
    private var scannerDeviceId: Int? = null
    private var dispatchFromWindowCallback = false

    private val mainHandler = Handler(Looper.getMainLooper())

    private fun normalizeBarcodeForSend(raw: String): String = raw.trim()

    override fun onCreate(savedInstanceState: android.os.Bundle?) {
        setDefaultUncaughtExceptionHandler()
        super.onCreate(savedInstanceState)
        installScannerKeyInterceptor()
    }

    private fun installScannerKeyInterceptor() {
        val w = window ?: return
        val orig = w.callback ?: return
        w.callback = object : Window.Callback by orig {
            override fun dispatchKeyEvent(event: KeyEvent): Boolean {
                if (handleScannerKeyEvent(event)) return true
                dispatchFromWindowCallback = true
                return try {
                    orig.dispatchKeyEvent(event)
                } finally {
                    dispatchFromWindowCallback = false
                }
            }
        }
    }

    private fun setDefaultUncaughtExceptionHandler() {
        val prev = Thread.getDefaultUncaughtExceptionHandler()
        Thread.setDefaultUncaughtExceptionHandler { t, e ->
            prev?.uncaughtException(t, e)
        }
    }
    private val sessionIdleRunnable = Runnable {
        if (!scannerSessionActive) return@Runnable
        if (scannerBuffer.isEmpty()) {
            scannerSessionActive = false
            return@Runnable
        }
        val barcode = normalizeBarcodeForSend(scannerBuffer.toString())
        scannerBuffer.setLength(0)
        scannerSessionActive = false
        lastBarcodeSentTime = System.currentTimeMillis()
        sendBarcodeToReactNative(barcode)
    }

    companion object {
        private const val LOG_TAG = "PTI_HwScan"
        private const val SCANNER_SESSION_TIMEOUT_MS = 2000L
        private const val SESSION_IDLE_SEND_MS = 1000L
        private const val POST_BARCODE_SILENT_MS = 800L

        private val TRIGGER_KEY_CODES = setOf(
            KeyEvent.KEYCODE_NUMPAD_DOT,
            139, 140, 141, 142, 143, 144,
            KeyEvent.KEYCODE_ASSIST
        )
    }

    private fun isTriggerKey(keyCode: Int, event: KeyEvent): Boolean {
        if (event.getUnicodeChar(event.metaState) != 0) return false
        return keyCode in TRIGGER_KEY_CODES
    }

    private fun isPrintableBarcodeChar(code: Int): Boolean =
        (code in 32..126) || (code in 160..255) || (code in 0x400..0x4FF) || (code in 0x370..0x3FF)

    override fun getMainComponentName(): String = "Pre-Trip_Inspections"

    override fun createReactActivityDelegate(): ReactActivityDelegate =
        DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)

    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        //Если событие пришло через установленный Window.Callback, здесь не повторяем обработку.
        if (dispatchFromWindowCallback) return super.dispatchKeyEvent(event)
        if (handleScannerKeyEvent(event)) return true
        return super.dispatchKeyEvent(event)
    }

    private fun handleScannerKeyEvent(event: KeyEvent): Boolean {
        val now = System.currentTimeMillis()
        val keyCode = event.keyCode

        if (lastBarcodeSentTime != 0L && (now - lastBarcodeSentTime) < POST_BARCODE_SILENT_MS) return true

        if (scannerSessionActive) {
            if (event.action != KeyEvent.ACTION_DOWN) return true
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                mainHandler.removeCallbacks(sessionIdleRunnable)
                scannerSessionActive = false
                scannerBuffer.setLength(0)
                return false
            }
            if (lastScannerKeyTime != 0L && now - lastScannerKeyTime > SCANNER_SESSION_TIMEOUT_MS) {
                mainHandler.removeCallbacks(sessionIdleRunnable)
                if (scannerBuffer.isNotEmpty()) {
                    val barcode = normalizeBarcodeForSend(scannerBuffer.toString())
                    scannerBuffer.setLength(0)
                    scannerSessionActive = false
                    lastBarcodeSentTime = now
                    sendBarcodeToReactNative(barcode)
                } else scannerSessionActive = false
                return true
            }
            lastScannerKeyTime = now
            when {
                isTriggerKey(keyCode, event) -> { }
                keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER -> {
                    scannerBuffer.append('\n')
                    mainHandler.postDelayed(sessionIdleRunnable, SESSION_IDLE_SEND_MS)
                }
                else -> {
                    val appended = appendKeyToBuffer(event)
                    if (appended) mainHandler.postDelayed(sessionIdleRunnable, SESSION_IDLE_SEND_MS)
                }
            }
            return true
        }

        val fromScanner = when {
            event.deviceId == -1 -> false
            scannerDeviceId != null -> event.deviceId == scannerDeviceId
            isTriggerKey(keyCode, event) -> true
            else -> false
        }
        if (!fromScanner) {
            mainHandler.removeCallbacks(sessionIdleRunnable)
            if (scannerSessionActive) {
                scannerSessionActive = false
                scannerBuffer.setLength(0)
            }
            return false
        }

        if (event.action != KeyEvent.ACTION_DOWN) return true
        if (keyCode == KeyEvent.KEYCODE_BACK) return false

        mainHandler.removeCallbacks(sessionIdleRunnable)
        scannerSessionActive = true
        scannerBuffer.setLength(0)
        lastScannerKeyTime = now
        if (scannerDeviceId == null && event.deviceId > 0) scannerDeviceId = event.deviceId
        if (isTriggerKey(keyCode, event)) {
            HardwareScannerModule.emitTriggerFromActivity(applicationContext)
            return true
        }
        val appended = appendKeyToBuffer(event)
        if (appended) mainHandler.postDelayed(sessionIdleRunnable, SESSION_IDLE_SEND_MS)
        return true
    }

    private fun appendKeyToBuffer(event: KeyEvent): Boolean {
        val keyCode = event.keyCode
        if (keyCode in TRIGGER_KEY_CODES && event.getUnicodeChar(event.metaState) == 0) return false
        var unicode = event.getUnicodeChar(event.metaState)
        if (unicode == 0 && event.action == KeyEvent.ACTION_DOWN) {
            try {
                val map = KeyCharacterMap.load(event.deviceId)
                unicode = map.get(keyCode, event.metaState)
            } catch (_: Exception) {}
        }
        if (unicode != 0 && Character.isBmpCodePoint(unicode)) {
            scannerBuffer.append(unicode.toChar())
            return true
        }
        if (keyCode == KeyEvent.KEYCODE_UNKNOWN || keyCode == 0) {
            val label = event.displayLabel
            val labelCode = label.code
            if (labelCode != 0 && isPrintableBarcodeChar(labelCode)) {
                scannerBuffer.append(label)
                return true
            }
        }
        when (keyCode) {
            KeyEvent.KEYCODE_0, KeyEvent.KEYCODE_1, KeyEvent.KEYCODE_2, KeyEvent.KEYCODE_3, KeyEvent.KEYCODE_4,
            KeyEvent.KEYCODE_5, KeyEvent.KEYCODE_6, KeyEvent.KEYCODE_7, KeyEvent.KEYCODE_8, KeyEvent.KEYCODE_9 ->
                scannerBuffer.append('0' + (keyCode - KeyEvent.KEYCODE_0))
            KeyEvent.KEYCODE_NUMPAD_0, KeyEvent.KEYCODE_NUMPAD_1, KeyEvent.KEYCODE_NUMPAD_2, KeyEvent.KEYCODE_NUMPAD_3,
            KeyEvent.KEYCODE_NUMPAD_4, KeyEvent.KEYCODE_NUMPAD_5, KeyEvent.KEYCODE_NUMPAD_6, KeyEvent.KEYCODE_NUMPAD_7,
            KeyEvent.KEYCODE_NUMPAD_8, KeyEvent.KEYCODE_NUMPAD_9 ->
                scannerBuffer.append('0' + (keyCode - KeyEvent.KEYCODE_NUMPAD_0))
            KeyEvent.KEYCODE_MINUS, KeyEvent.KEYCODE_NUMPAD_SUBTRACT -> scannerBuffer.append('-')
            KeyEvent.KEYCODE_SLASH -> scannerBuffer.append('/')
            KeyEvent.KEYCODE_PERIOD, KeyEvent.KEYCODE_NUMPAD_DOT ->
                if (event.getUnicodeChar(event.metaState) != 0) scannerBuffer.append('.')
            in KeyEvent.KEYCODE_A..KeyEvent.KEYCODE_Z -> scannerBuffer.append('A' + (keyCode - KeyEvent.KEYCODE_A))
            KeyEvent.KEYCODE_SPACE -> scannerBuffer.append(' ')
            else -> return false
        }
        return true
    }

    private fun sendBarcodeToReactNative(barcode: String) {
        HardwareScannerModule.emitBarcodeToJs(barcode)
    }
}
