package com.rosetta.messenger.network.connection

import android.content.Context
import java.io.File
import java.util.Date
import java.util.Locale
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

internal class ProtocolDebugLogService(
    private val scope: CoroutineScope,
    private val maxDebugLogs: Int,
    private val debugLogFlushDelayMs: Long,
    private val heartbeatOkLogMinIntervalMs: Long,
    private val protocolTraceFileName: String,
    private val protocolTraceMaxBytes: Long,
    private val protocolTraceKeepBytes: Int,
    private val appContextProvider: () -> Context?
) {
    private var uiLogsEnabled = false
    private val _debugLogs = MutableStateFlow<List<String>>(emptyList())
    val debugLogs: StateFlow<List<String>> = _debugLogs.asStateFlow()
    private val debugLogsBuffer = ArrayDeque<String>(maxDebugLogs)
    private val debugLogsLock = Any()
    private val protocolTraceLock = Any()
    @Volatile private var debugFlushJob: Job? = null
    private val debugFlushPending = AtomicBoolean(false)
    @Volatile private var lastHeartbeatOkLogAtMs: Long = 0L
    @Volatile private var suppressedHeartbeatOkLogs: Int = 0

    fun addLog(message: String) {
        var normalizedMessage = message
        val isHeartbeatOk = normalizedMessage.startsWith("💓 Heartbeat OK")
        if (isHeartbeatOk) {
            val now = System.currentTimeMillis()
            if (now - lastHeartbeatOkLogAtMs < heartbeatOkLogMinIntervalMs) {
                suppressedHeartbeatOkLogs++
                return
            }
            if (suppressedHeartbeatOkLogs > 0) {
                normalizedMessage = "$normalizedMessage (+${suppressedHeartbeatOkLogs} skipped)"
                suppressedHeartbeatOkLogs = 0
            }
            lastHeartbeatOkLogAtMs = now
        }

        val timestamp =
            java.text.SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()).format(Date())
        val line = "[$timestamp] $normalizedMessage"
        if (shouldPersistProtocolTrace(normalizedMessage)) {
            persistProtocolTraceLine(line)
        }
        if (!uiLogsEnabled) return

        synchronized(debugLogsLock) {
            if (debugLogsBuffer.size >= maxDebugLogs) {
                debugLogsBuffer.removeFirst()
            }
            debugLogsBuffer.addLast(line)
        }
        flushDebugLogsThrottled()
    }

    fun enableUILogs(enabled: Boolean) {
        uiLogsEnabled = enabled
        if (enabled) {
            val snapshot = synchronized(debugLogsLock) { debugLogsBuffer.toList() }
            _debugLogs.value = snapshot
        } else {
            _debugLogs.value = emptyList()
        }
    }

    fun clearLogs() {
        synchronized(debugLogsLock) {
            debugLogsBuffer.clear()
        }
        suppressedHeartbeatOkLogs = 0
        lastHeartbeatOkLogAtMs = 0L
        _debugLogs.value = emptyList()
    }

    private fun shouldPersistProtocolTrace(message: String): Boolean {
        if (uiLogsEnabled) return true
        if (message.contains("rosettadev1", ignoreCase = true)) return true
        if (message.startsWith("❌") || message.startsWith("⚠️")) return true
        if (message.contains("STATE CHANGE")) return true
        if (message.contains("CONNECTION FULLY ESTABLISHED")) return true
        if (message.contains("HANDSHAKE COMPLETE")) return true
        if (message.contains("SYNC COMPLETE")) return true
        if (message.startsWith("🔌 CONNECT CALLED") || message.startsWith("🔌 Connecting to")) return true
        if (message.startsWith("✅ WebSocket OPEN")) return true
        if (message.startsWith("📡 NETWORK")) return true
        return false
    }

    private fun persistProtocolTraceLine(line: String) {
        val context = appContextProvider() ?: return
        runCatching {
            val dir = File(context.filesDir, "crash_reports")
            if (!dir.exists()) dir.mkdirs()
            val traceFile = File(dir, protocolTraceFileName)
            synchronized(protocolTraceLock) {
                if (traceFile.exists() && traceFile.length() > protocolTraceMaxBytes) {
                    val tail = runCatching {
                        traceFile.readText(Charsets.UTF_8).takeLast(protocolTraceKeepBytes)
                    }.getOrDefault("")
                    traceFile.writeText("=== PROTOCOL TRACE ROTATED ===\n$tail\n", Charsets.UTF_8)
                }
                traceFile.appendText("$line\n", Charsets.UTF_8)
            }
        }
    }

    private fun flushDebugLogsThrottled() {
        debugFlushPending.set(true)
        if (debugFlushJob?.isActive == true) return
        debugFlushJob =
            scope.launch {
                while (debugFlushPending.getAndSet(false)) {
                    delay(debugLogFlushDelayMs)
                    val snapshot = synchronized(debugLogsLock) { debugLogsBuffer.toList() }
                    _debugLogs.value = snapshot
                }
            }
    }
}
