package com.erwan.revwatch.obd

import android.content.ContentValues
import android.content.Context
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import java.io.File
import java.io.OutputStreamWriter
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.concurrent.ConcurrentLinkedQueue

/**
 * Debug logger that writes a comprehensive OBD diagnostic file to Downloads.
 *
 * Usage:
 *   ObdLogger.start(context)   — call once when service starts
 *   ObdLogger.log(tag, msg)    — log from anywhere
 *   ObdLogger.flush()          — force write buffered lines
 *   ObdLogger.stop(context)    — finalize and close file
 *
 * Output: Downloads/revwatch_obd_debug_<timestamp>.log
 */
object ObdLogger {

    private const val TAG = "ObdLogger"
    private const val BUFFER_FLUSH_SIZE = 40

    @Volatile
    var enabled = false
        private set

    private val buffer = ConcurrentLinkedQueue<String>()
    private var writer: OutputStreamWriter? = null
    private var startTimeMs = 0L
    private val timeFmt = SimpleDateFormat("HH:mm:ss.SSS", Locale.US)
    private val reusableDate = Date()

    // ── Counters for summary ──
    @Volatile var totalCommands = 0; private set
    @Volatile var totalTimeouts = 0; private set
    @Volatile var totalParseErrors = 0; private set
    @Volatile var totalSnapshots = 0; private set
    @Volatile var totalEmptySnapshots = 0; private set
    @Volatile var totalReconnects = 0; private set
    @Volatile var totalBytesWritten = 0L; private set
    @Volatile var totalBytesRead = 0L; private set

    fun start(context: Context) {
        if (enabled) return
        try {
            startTimeMs = System.currentTimeMillis()
            totalCommands = 0; totalTimeouts = 0; totalParseErrors = 0
            totalSnapshots = 0; totalEmptySnapshots = 0; totalReconnects = 0
            totalBytesWritten = 0; totalBytesRead = 0

            val ts = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
            val fileName = "revwatch_obd_debug_$ts.log"

            writer = if (Build.VERSION.SDK_INT >= 29) {
                // Android 10+: MediaStore (no permission needed)
                val values = ContentValues().apply {
                    put(MediaStore.Downloads.DISPLAY_NAME, fileName)
                    put(MediaStore.Downloads.MIME_TYPE, "text/plain")
                    put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
                }
                val uri = context.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
                if (uri != null) {
                    OutputStreamWriter(context.contentResolver.openOutputStream(uri, "wa"), Charsets.UTF_8)
                } else null
            } else {
                // Legacy: direct file access
                val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
                dir.mkdirs()
                OutputStreamWriter(File(dir, fileName).outputStream(), Charsets.UTF_8)
            }

            enabled = writer != null

            if (enabled) {
                val header = buildString {
                    appendLine("═══════════════════════════════════════════════════════")
                    appendLine("  RevWatch OBD Debug Log")
                    appendLine("  Started: ${SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).format(Date())}")
                    appendLine("  Device: ${Build.MANUFACTURER} ${Build.MODEL} (API ${Build.VERSION.SDK_INT})")
                    appendLine("═══════════════════════════════════════════════════════")
                    appendLine()
                }
                writer?.write(header)
                writer?.flush()
                Log.i(TAG, "Debug logging started: $fileName")
            }
        } catch (e: Exception) {
            Log.e(TAG, "Failed to start logger", e)
            enabled = false
        }
    }

    fun stop(@Suppress("UNUSED_PARAMETER") context: Context) {
        if (!enabled) return
        try {
            flush()
            // Write summary
            val elapsed = (System.currentTimeMillis() - startTimeMs) / 1000.0
            val summary = buildString {
                appendLine()
                appendLine("═══════════════════════════════════════════════════════")
                appendLine("  SESSION SUMMARY (${String.format("%.1f", elapsed)}s)")
                appendLine("═══════════════════════════════════════════════════════")
                appendLine("  Commands sent:       $totalCommands")
                appendLine("  Timeouts:            $totalTimeouts")
                appendLine("  Parse errors:        $totalParseErrors")
                appendLine("  Snapshots:           $totalSnapshots")
                appendLine("  Empty snapshots:     $totalEmptySnapshots")
                appendLine("  Reconnects:          $totalReconnects")
                appendLine("  Bytes TX:            $totalBytesWritten")
                appendLine("  Bytes RX:            $totalBytesRead")
                appendLine("═══════════════════════════════════════════════════════")
            }
            writer?.write(summary)
            writer?.flush()
            writer?.close()
        } catch (_: Exception) {}
        writer = null
        enabled = false
        Log.i(TAG, "Debug logging stopped")
    }

    fun log(tag: String, msg: String) {
        if (!enabled) return
        val now = System.currentTimeMillis()
        val elapsed = now - startTimeMs
        reusableDate.time = now
        val time = timeFmt.format(reusableDate)
        val line = "$time [+${elapsed}ms] [$tag] $msg\n"
        buffer.add(line)
        if (buffer.size >= BUFFER_FLUSH_SIZE) flush()
    }

    /** Log a raw ELM command/response pair with timing. */
    fun logCommand(cmd: String, response: String, elapsedMs: Long, timedOut: Boolean) {
        if (!enabled) return
        totalCommands++
        if (timedOut) totalTimeouts++
        val status = when {
            timedOut -> "TIMEOUT"
            response.isBlank() -> "EMPTY"
            response.contains("NO DATA") -> "NO_DATA"
            response.contains("UNABLE") -> "UNABLE"
            response.contains("ERROR") -> "ERROR"
            response.contains(">") -> "OK"
            else -> "PARTIAL"
        }
        // Sanitize response for readability
        val clean = response.replace("\r", "\\r").replace("\n", "\\n").take(200)
        log("ELM", "TX: $cmd | RX($status ${elapsedMs}ms): $clean")
    }

    /** Log a transport-level read/write. */
    fun logTransport(direction: String, bytes: Int, timeoutMs: Int = 0, error: String? = null) {
        if (!enabled) return
        if (direction == "TX") totalBytesWritten += bytes
        else totalBytesRead += bytes
        val extra = if (error != null) " ERR=$error" else if (timeoutMs > 0 && bytes == 0) " timeout=${timeoutMs}ms" else ""
        log("TRANSPORT", "$direction ${bytes}B$extra")
    }

    /** Log a polling snapshot result. */
    fun logSnapshot(
        rpm: Double?, speed: Double?, coolant: Double?, oil: Double?,
        map: Double?, baro: Double?, boost: Double?, voltage: Double?,
        pollDurationMs: Long
    ) {
        if (!enabled) return
        totalSnapshots++
        val hasAny = rpm != null || speed != null || coolant != null || voltage != null
        if (!hasAny) totalEmptySnapshots++

        val fields = buildString {
            append("rpm="); append(rpm?.let { "%.0f".format(it) } ?: "null")
            append(" spd="); append(speed?.let { "%.0f".format(it) } ?: "null")
            append(" cool="); append(coolant?.let { "%.0f".format(it) } ?: "null")
            append(" oil="); append(oil?.let { "%.0f".format(it) } ?: "null")
            append(" map="); append(map?.let { "%.1f".format(it) } ?: "null")
            append(" baro="); append(baro?.let { "%.1f".format(it) } ?: "null")
            append(" boost="); append(boost?.let { "%.1f".format(it) } ?: "null")
            append(" volt="); append(voltage?.let { "%.2f".format(it) } ?: "null")
        }
        val status = if (hasAny) "OK" else "EMPTY"
        log("SNAPSHOT", "$status (${pollDurationMs}ms) $fields")
    }

    fun logConnection(event: String) {
        if (!enabled) return
        if (event.contains("reconnex", ignoreCase = true) || event.contains("Perte", ignoreCase = true))
            totalReconnects++
        log("CONN", event)
    }

    fun logInit(step: String, result: String) {
        if (!enabled) return
        log("INIT", "$step -> $result")
    }

    fun logCalibration(pidTimeoutMs: Int, mode22TimeoutMs: Int, atTimeoutMs: Int) {
        if (!enabled) return
        log("CALIB", "PID=${pidTimeoutMs}ms Mode22=${mode22TimeoutMs}ms AT=${atTimeoutMs}ms")
    }

    fun logConfig(mode: String, vehicle: String) {
        if (!enabled) return
        log("CONFIG", "mode=$mode vehicle=$vehicle")
    }

    fun logParse(cmd: String, rawResponse: String, tokens: List<Int>?, result: String) {
        if (!enabled) return
        if (tokens == null && rawResponse.isNotBlank()) totalParseErrors++
        val clean = rawResponse.replace("\r", "\\r").replace("\n", "\\n").take(150)
        log("PARSE", "cmd=$cmd tokens=${tokens?.size ?: "null"} result=$result raw=[$clean]")
    }

    fun logAdaptiveTimeout(pidMs: Int, mode22Ms: Int, reason: String) {
        if (!enabled) return
        log("TIMEOUT", "PID=${pidMs}ms Mode22=${mode22Ms}ms ($reason)")
    }

    fun flush() {
        val w = writer ?: return
        try {
            val lines = buildString {
                while (true) {
                    val line = buffer.poll() ?: break
                    append(line)
                }
            }
            if (lines.isNotEmpty()) {
                w.write(lines)
                w.flush()
            }
        } catch (_: Exception) {}
    }
}
