package com.erwan.revwatch.obd

import com.erwan.revwatch.model.MetricSnapshot
import com.erwan.revwatch.model.PidSet
import com.erwan.revwatch.model.VehicleProfile

/**
 * Poller OBD-II optimisé pour l'OBDLink LX via Bluetooth.
 *
 * Stratégie de polling par batch multi-PID :
 * - Batch 1 : RPM + Vitesse + Refroidissement + MAP + Papillon (1 seule requête CAN)
 * - Batch 2 : Temp huile + Niveau carburant + Consommation (1 seule requête CAN)
 * - Voltage  : commande AT séparée (ATRV)
 *
 * Sur un véhicule CAN 500 kbps, cette stratégie réduit le nombre de RTT Bluetooth
 * de ~8-10 à ~3 par cycle, ce qui permet d'atteindre 10-20 Hz de refresh.
 *
 * Nota bene sur la temp huile :
 * - PID 0x5C (Mode 01) est optionnel selon SAE J1979 — la plupart des véhicules ne
 *   l'implémentent pas, notamment les Ford EcoBoost (confirmé terrain).
 * - Les PIDs Mode 22 Ford pour la temp huile ne sont pas documentés publiquement ;
 *   ils seront ajoutés par profil une fois vérifiés sur véhicule réel.
 * - Pour les Honda D/B-series 1996-2000, il n'y a pas de capteur huile raccordé à l'ECU.
 */
class ObdPoller(
    private val elm: Elm327Client,
    private val profile: VehicleProfile
) {

    // Batch principal : PIDs critiques pour l'affichage dashboard
    private val BATCH_PRIMARY = intArrayOf(
        0x0C,  // RPM                       (A*256+B)/4 tr/min
        0x0D,  // Vitesse                   A km/h
        0x05,  // Température refroidissement A-40 °C
        0x0B,  // MAP (pression admission)   A kPa absolu
        0x11   // Position papillon          A*100/255 %
    )

    // Batch secondaire : PIDs optionnels selon SAE J1979
    // 0x5C : temp huile  — formule A-40 °C, optionnel, souvent absent (Ford EcoBoost notamment)
    // 0x2F : niveau carburant — A*100/255 %
    // 0x5E : débit carburant — (A*256+B)*0.05 L/h, optionnel, rare sur véhicules européens
    private val BATCH_SECONDARY = intArrayOf(
        0x5C,
        0x2F,
        0x5E
    )

    fun pollSnapshot(): MetricSnapshot {
        val start = System.currentTimeMillis()
        ObdLogger.log("POLL", "── Cycle poll (${profile.pidSet.name}) ──")

        val snapshot = when (profile.pidSet) {
            PidSet.FORD_ECOBOOST,
            PidSet.FORD_ECOBOOST_RS,
            PidSet.FORD_DURATEC_25,
            PidSet.OBD2_STANDARD    -> pollBatch()
            PidSet.OBD2_STANDARD_NA -> pollBatchNA()
        }

        val elapsed = System.currentTimeMillis() - start
        ObdLogger.logSnapshot(
            snapshot.rpm, snapshot.speedKph, snapshot.coolantTempC, snapshot.oilTempC,
            snapshot.mapKpa, snapshot.baroKpa, snapshot.boostKpa, snapshot.voltage,
            elapsed
        )
        return snapshot
    }

    // ──────── Polling batch générique (Mode 01 uniquement) ────────

    /**
     * Polling batch pour les véhicules OBD-II standard (turbo et Ford EcoBoost).
     *
     * Boost = MAP (0x0B) - baro (0x33) : méthode confirmée et fiable.
     * Temp huile via 0x5C : optionnel, retourne null si non supporté (→ affiché "--").
     */
    private fun pollBatch(): MetricSnapshot {
        val b1 = elm.readBatchPids(0x01, *BATCH_PRIMARY)
        val b2 = elm.readBatchPids(0x01, *BATCH_SECONDARY)
        val voltage = elm.readVoltage()

        val rpm      = b1[0x0C]?.takeIf { it.size >= 2 }?.let { ((it[0] * 256) + it[1]) / 4.0 }
        val speed    = b1[0x0D]?.firstOrNull()?.toDouble()
        val coolant  = b1[0x05]?.firstOrNull()?.let { it - 40.0 }
        val map      = b1[0x0B]?.firstOrNull()?.toDouble()

        // Temp huile : PID 0x5C optionnel — null si non supporté par le véhicule
        val oil      = b2[0x5C]?.firstOrNull()?.let { it - 40.0 }
        val fuelLevel = b2[0x2F]?.firstOrNull()?.let { it * 100.0 / 255.0 }
        val fuelRate  = b2[0x5E]?.takeIf { it.size >= 2 }?.let { ((it[0] * 256) + it[1]) * 0.05 }

        // Baro via PID 0x33 — uniquement si MAP disponible (évite une requête inutile)
        val baro  = if (map != null) elm.readPid(0x01, 0x33)?.firstOrNull()?.toDouble() else null
        val boost = if (map != null && baro != null) map - baro else null

        return MetricSnapshot(
            ts = System.currentTimeMillis(),
            rpm = rpm, speedKph = speed, coolantTempC = coolant,
            oilTempC = oil, mapKpa = map, baroKpa = baro,
            boostKpa = boost, voltage = voltage,
            fuelLevelPct = fuelLevel, fuelRateLph = fuelRate
        )
    }

    /**
     * Polling batch pour les véhicules naturellement aspirés Honda 1996-2000
     * (D14, D15, D16, B16).
     *
     * Ces ECU ne supportent pas :
     *  - PID 0x5C (pas de capteur huile raccordé à l'ECU)
     *  - PID 0x2F (jauge carburant câblée directement au tableau de bord)
     *  - PID 0x5E (débit carburant non implémenté)
     *  - Pas de MAP/boost (moteurs atmosphériques)
     */
    private fun pollBatchNA(): MetricSnapshot {
        val b1 = elm.readBatchPids(0x01, 0x0C, 0x0D, 0x05, 0x11)
        val voltage = elm.readVoltage()

        val rpm     = b1[0x0C]?.takeIf { it.size >= 2 }?.let { ((it[0] * 256) + it[1]) / 4.0 }
        val speed   = b1[0x0D]?.firstOrNull()?.toDouble()
        val coolant = b1[0x05]?.firstOrNull()?.let { it - 40.0 }

        return MetricSnapshot(
            ts = System.currentTimeMillis(),
            rpm = rpm, speedKph = speed, coolantTempC = coolant,
            oilTempC = null, mapKpa = null, baroKpa = null,
            boostKpa = null, voltage = voltage,
            fuelLevelPct = null, fuelRateLph = null
        )
    }
}
