package com.bplscale

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.bluetooth.le.BluetoothLeScanner
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.Context
import android.os.Handler
import android.os.Looper
import com.facebook.react.bridge.*
import com.facebook.react.module.annotations.ReactModule

@ReactModule(name = BplScaleModule.NAME)
class BplScaleModule(
    reactContext: ReactApplicationContext
) : ReactContextBaseJavaModule(reactContext) {

    private val bluetoothAdapter: BluetoothAdapter? by lazy {
        val manager =
            reactContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
        manager.adapter
    }

    private var scanner: BluetoothLeScanner? = null
    private var scanCallback: ScanCallback? = null

    override fun getName(): String = NAME

    data class ScaleData(val weight: Float, val impedance: Float)

    /**
     * Scan BLE devices and return weight and impedance
     */
    @ReactMethod
    fun scanDevices(scanDurationMs: Int, promise: Promise) {
        val adapter = bluetoothAdapter
        if (adapter == null || !adapter.isEnabled) {
            promise.reject("BLE_DISABLED", "Bluetooth is disabled")
            return
        }

        scanner = adapter.bluetoothLeScanner
        if (scanner == null) {
            promise.reject("BLE_ERROR", "BLE scanner unavailable")
            return
        }

        val devicesMap = HashMap<String, WritableMap>()
        val resultArray = Arguments.createArray()

        val settings = ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
            .build()

        scanCallback = object : ScanCallback() {
            override fun onScanResult(callbackType: Int, result: ScanResult) {
                processDevice(result, devicesMap)
            }

            override fun onBatchScanResults(results: MutableList<ScanResult>) {
                results.forEach { processDevice(it, devicesMap) }
            }

            override fun onScanFailed(errorCode: Int) {
                promise.reject("BLE_SCAN_FAILED", "Scan failed with code $errorCode")
            }

            private fun processDevice(result: ScanResult, map: HashMap<String, WritableMap>) {
                val device = result.device ?: return
                val address = device.address ?: return
                val scanRecord = result.scanRecord?.bytes ?: return

                val scaleData = parseScaleData(scanRecord) ?: return

                if (!map.containsKey(address)) {
                    val deviceMap = Arguments.createMap()
                    deviceMap.putString("name", device.name ?: "")
                    deviceMap.putString("address", address)
                    deviceMap.putDouble("weight", scaleData.weight.toDouble())
                    deviceMap.putDouble("impedance", scaleData.impedance.toDouble())
                    map[address] = deviceMap
                }
            }
        }

        scanner?.startScan(null, settings, scanCallback)

        Handler(Looper.getMainLooper()).postDelayed({
            scanner?.stopScan(scanCallback)
            devicesMap.values.forEach { resultArray.pushMap(it) }
            promise.resolve(resultArray)
        }, scanDurationMs.toLong())
    }

    // ────── Parse weight and impedance from advertising packet ──────
    private fun parseScaleData(scanRecord: ByteArray): ScaleData? {
        var i = 0
        while (i < scanRecord.size - 2) {
            val len = scanRecord[i].toInt() and 0xFF
            if (len == 0) break
            val type = scanRecord[i + 1].toInt() and 0xFF
            if (type == 0xFF && i + len <= scanRecord.size) {
                val mfg = scanRecord.copyOfRange(i + 2, i + 1 + len)
                if (mfg.size >= 15) {
                    val flag = mfg[0].toInt() and 0xFF
                    return when (flag) {
                        0xC0 -> parseV30Protocol(mfg)
                        0xCA -> parseV21Protocol(mfg)
                        else -> null
                    }
                }
            }
            i += len
        }
        return null
    }

    private fun parseV30Protocol(data: ByteArray): ScaleData {
        val weight = ((data[2].toInt() and 0xFF) shl 8 or (data[3].toInt() and 0xFF)) / 10f
        val impedance = ((data[4].toInt() and 0xFF) shl 8 or (data[5].toInt() and 0xFF)) / 10f
        return ScaleData(weight, impedance)
    }

    private fun parseV21Protocol(data: ByteArray): ScaleData {
        val weight = ((data[10].toInt() and 0xFF) shl 8 or (data[11].toInt() and 0xFF)) / 10f
        val impedance = ((data[12].toInt() and 0xFF) shl 8 or (data[13].toInt() and 0xFF)) / 10f
        return ScaleData(weight, impedance)
    }

    companion object {
        const val NAME = "BplScale"
    }
}
