package cl.homelogic.platform.common.logging

import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import kotlin.time.Clock
import kotlin.time.ExperimentalTime
import kotlin.time.Instant

enum class Severity {
    Debug,
    Verbose,
    Info,
    Warn,
    Error,
    None,
}

enum class TreeRoots {
    Navigation,
    Koin,
    AssetsParser,
    DesignSystem,
    Settings,
}

private data class LogEntry(
    val timestamp: Instant,
    val severity: Severity,
    val tag: String,
    val message: String,
)

private interface LogTree {
    val maxEntries: Int
    val logs: ArrayDeque<LogEntry>

    fun log(
        severity: Severity,
        tag: String,
        message: String,
    )

    fun getFormattedLogs(): String
}

private class MemoryLogTree(
    override val maxEntries: Int,
) : LogTree {
    override val logs = ArrayDeque<LogEntry>(maxEntries)
    private val logsLock = SynchronizedObject()

    override fun log(
        severity: Severity,
        tag: String,
        message: String,
    ) {
        synchronized(logsLock) {
            if (logs.size >= maxEntries) {
                logs.removeFirst()
            }
            logs.addLast(LogEntry(Clock.System.now(), severity, tag, message))
        }
    }

    override fun getFormattedLogs(): String =
        buildString {
            synchronized(logsLock) {
                logs.forEach { entry ->
                    appendLine(
                        "${entry.timestamp.toLocalDateTime(TimeZone.currentSystemDefault())} " +
                            "${entry.severity.name.padEnd(7)} " +
                            "${entry.tag}: ${entry.message}",
                    )
                }
            }
        }
}

internal expect fun logPlatform(
    severity: Severity,
    tag: String,
    message: String,
)

class Trace {
    companion object {
        private const val DEFAULT_LOG_MESSAGE = "The message content was empty"

        private var _isDebug: Boolean = false
        private var _minSeverity: Severity = Severity.None
        private var _treeSize: Int = 20
        private val _trees = HashMap<String, LogTree>()

        fun initialize(isDebug: Boolean) {
            _isDebug = isDebug
            _minSeverity = if (_isDebug) Severity.Verbose else Severity.None
            plantTree(TreeRoots.Navigation)
            plantTree(TreeRoots.Koin)
            plantTree(TreeRoots.AssetsParser)
            plantTree(TreeRoots.DesignSystem)
            plantTree(TreeRoots.Settings)
        }

        fun setLoggingSeverity(severity: Severity) {
            _minSeverity = severity
        }

        fun setTreeSizes(size: Int) {
            _treeSize = size
        }

        private fun plantTree(name: TreeRoots) {
            _trees[name.name] = MemoryLogTree(_treeSize)
        }

        fun plantTree(name: String) {
            _trees[name] = MemoryLogTree(_treeSize)
        }

        fun getTreeContent(name: String): String = _trees[name]?.getFormattedLogs() ?: "Tree '$name' not found"

        private fun log(
            severity: Severity,
            tag: String,
            message: String,
        ) {
            if (severity == Severity.None || severity.ordinal < _minSeverity.ordinal) return
            logPlatform(severity, "Trace - $tag", message)
            _trees.values.forEach { tree ->
                tree.log(severity, tag, message)
            }
        }

        fun v(
            tag: TreeRoots,
            message: String,
        ) {
            v(tag.name, message)
        }

        fun d(
            tag: TreeRoots,
            message: String,
        ) {
            d(tag.name, message)
        }

        fun i(
            tag: TreeRoots,
            message: String,
        ) {
            i(tag.name, message)
        }

        fun w(
            tag: TreeRoots,
            message: String,
        ) {
            w(tag.name, message)
        }

        fun e(
            tag: TreeRoots,
            message: String,
        ) {
            e(tag.name, message)
        }

        fun v(
            tag: String,
            message: String?,
        ) {
            log(Severity.Verbose, tag, message ?: DEFAULT_LOG_MESSAGE)
        }

        fun d(
            tag: String,
            message: String?,
        ) {
            log(Severity.Debug, tag, message ?: DEFAULT_LOG_MESSAGE)
        }

        fun i(
            tag: String,
            message: String?,
        ) {
            log(Severity.Info, tag, message ?: DEFAULT_LOG_MESSAGE)
        }

        fun w(
            tag: String,
            message: String?,
        ) {
            log(Severity.Warn, tag, message ?: DEFAULT_LOG_MESSAGE)
        }

        fun e(
            tag: String,
            message: String?,
        ) {
            log(Severity.Error, tag, message ?: DEFAULT_LOG_MESSAGE)
        }

        fun e(
            tag: String,
            message: String,
            throwable: Throwable,
        ) {
            log(
                Severity.Error,
                tag,
                (message + "\n" + throwable.message + "\n" + throwable.stackTraceToString()),
            )
        }

        fun e(
            tag: String,
            message: String,
            exception: Exception,
        ) {
            log(
                Severity.Error,
                tag,
                (message + "\n" + exception.message + "\n" + exception.stackTraceToString()),
            )
        }
    }
}
