package com.erwan.revwatch.obd

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import android.util.Log
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.UUID

/**
 * Transport Bluetooth optimisé exclusivement pour l'OBDLink LX.
 *
 * Optimisations clés :
 * - Annulation du scan BT avant connexion (critique pour la stabilité)
 * - Fallback insecure RFCOMM si secure échoue
 * - Buffer de lecture 4 Ko (réduit les appels système)
 * - Polling 1 ms (vs 10 ms générique) pour minimiser la latence
 * - Écriture avec flush immédiat
 */
class OBDLinkLXTransport private constructor(
    private val socket: BluetoothSocket
) : ElmTransport {

    private val input: InputStream = socket.inputStream
    private val output: OutputStream = socket.outputStream

    companion object {
        private const val TAG = "OBDLinkLX"
        private val SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")

        /**
         * Ouvre une connexion RFCOMM vers l'OBDLink LX.
         * Annule le scan BT d'abord — c'est critique pour la fiabilité et la vitesse.
         */
        fun connect(device: BluetoothDevice, adapter: BluetoothAdapter): OBDLinkLXTransport {
            // Annuler la découverte BT — réduit drastiquement la latence de connexion
            adapter.cancelDiscovery()

            val socket = try {
                device.createRfcommSocketToServiceRecord(SPP_UUID)
            } catch (e: Exception) {
                Log.w(TAG, "RFCOMM sécurisé indisponible, tentative non-sécurisé: ${e.message}")
                device.createInsecureRfcommSocketToServiceRecord(SPP_UUID)
            }

            try {
                socket.connect()
            } catch (e: IOException) {
                socket.close()
                throw e
            }

            Log.d(TAG, "Connecté à ${device.name} (${device.address})")
            ObdLogger.logConnection("OBDLink LX connecté: ${device.name} ${device.address}")
            return OBDLinkLXTransport(socket)
        }
    }

    override fun write(data: ByteArray): Int {
        return try {
            output.write(data)
            output.flush()
            ObdLogger.logTransport("TX", data.size)
            data.size
        } catch (e: Exception) {
            ObdLogger.logTransport("TX", 0, error = "${e.javaClass.simpleName}: ${e.message}")
            throw e
        }
    }

    /**
     * Lecture optimisée : polling à 1 ms pour minimiser la latence.
     * L'OBDLink LX répond typiquement en 20-80 ms sur CAN.
     * Une boucle à 1 ms garantit que la réponse est lue dans les 2 ms suivant son arrivée.
     */
    override fun read(buffer: ByteArray, timeoutMs: Int): Int {
        val deadline = System.currentTimeMillis() + timeoutMs
        while (System.currentTimeMillis() < deadline) {
            val available = try {
                input.available()
            } catch (e: Exception) {
                ObdLogger.logTransport("RX", 0, error = "${e.javaClass.simpleName}: ${e.message}")
                return 0
            }
            if (available > 0) {
                val n = try {
                    input.read(buffer, 0, minOf(buffer.size, available))
                } catch (e: Exception) {
                    ObdLogger.logTransport("RX", 0, error = "${e.javaClass.simpleName}: ${e.message}")
                    return 0
                }
                if (n > 0) {
                    ObdLogger.logTransport("RX", n)
                    return n
                }
            }
            try {
                Thread.sleep(1L)
            } catch (_: InterruptedException) {
                break
            }
        }
        ObdLogger.logTransport("RX", 0, timeoutMs = timeoutMs)
        return 0
    }

    override fun setRts(enabled: Boolean): Boolean = false

    override fun close() {
        ObdLogger.logConnection("OBDLink LX transport: fermeture")
        try { input.close() } catch (_: Exception) {}
        try { output.close() } catch (_: Exception) {}
        try { socket.close() } catch (_: Exception) {}
    }
}
