package app.voltplan.cable.analytics

import android.content.Context
import android.os.Build
import android.util.Log
import app.voltplan.cable.BuildConfig
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.TimeZone
import java.util.UUID
import java.util.concurrent.TimeUnit

/**
 * Aptabase analytics, talking to the same instance the iOS app uses
 * (app key A-SH-4260269603, host https://apta.yuzuhub.com).
 *
 * Implements the Aptabase ingestion protocol directly (`POST /api/v0/event`) so behaviour and
 * payload shape match the official SDK without depending on an Android artifact. The public
 * [log] entry point mirrors `AnalyticsTracker.log(_:properties:)`.
 */
object Analytics {
    private const val TAG = "Analytics"
    private const val SDK_VERSION = "cable-android@1.0.0"
    private const val SESSION_TIMEOUT_MS = 60 * 60 * 1000L // 1 hour, matching Aptabase SDKs

    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    private val json = Json { encodeDefaults = true }
    private val client = OkHttpClient.Builder()
        .callTimeout(15, TimeUnit.SECONDS)
        .build()

    private val isoFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply {
        timeZone = TimeZone.getTimeZone("UTC")
    }

    private var appKey: String = BuildConfig.APTABASE_APP_KEY
    private var eventsUrl: String = BuildConfig.APTABASE_HOST.trimEnd('/') + "/api/v0/event"
    private var enabled: Boolean = true

    @Volatile private var sessionId: String = newSessionId()
    @Volatile private var lastTouch: Long = 0L

    private var osVersion: String = Build.VERSION.RELEASE ?: "unknown"
    private var localeTag: String = Locale.getDefault().toLanguageTag()
    private var appVersion: String = "1.0"
    private var appBuild: String = "1"

    fun init(context: Context) {
        appKey = BuildConfig.APTABASE_APP_KEY
        eventsUrl = BuildConfig.APTABASE_HOST.trimEnd('/') + "/api/v0/event"
        enabled = appKey.isNotBlank()
        localeTag = Locale.getDefault().toLanguageTag()
        osVersion = Build.VERSION.RELEASE ?: "unknown"
        runCatching {
            val pkg = context.packageManager.getPackageInfo(context.packageName, 0)
            appVersion = pkg.versionName ?: "1.0"
            appBuild = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                pkg.longVersionCode.toString()
            } else {
                @Suppress("DEPRECATION") pkg.versionCode.toString()
            }
        }
    }

    /** Tracks an event. [properties] values are coerced to String/Number/Boolean like the iOS tracker. */
    fun log(event: String, properties: Map<String, Any?> = emptyMap()) {
        if (BuildConfig.DEBUG) {
            if (properties.isEmpty()) {
                Log.d(TAG, "Analytics: $event")
            } else {
                val formatted = properties.entries.sortedBy { it.key }
                    .joinToString(", ") { "${it.key}=${it.value}" }
                Log.d(TAG, "Analytics: $event { $formatted }")
            }
        }
        val props = buildJsonObject {
            for ((key, value) in properties) {
                when (value) {
                    null -> {}
                    is String -> put(key, value)
                    is Boolean -> put(key, value)
                    is Int -> put(key, value)
                    is Long -> put(key, value)
                    is Float -> put(key, value)
                    is Double -> put(key, value)
                    else -> put(key, value.toString())
                }
            }
        }
        if (enabled) send(event, props)
    }

    private fun send(event: String, props: JsonObject) {
        val body = buildJsonObject {
            put("timestamp", isoFormatter.format(Date()))
            put("sessionId", currentSessionId())
            put("eventName", event)
            put("systemProps", buildJsonObject {
                put("isDebug", BuildConfig.DEBUG)
                put("osName", "Android")
                put("osVersion", osVersion)
                put("locale", localeTag)
                put("appVersion", appVersion)
                put("appBuildNumber", appBuild)
                put("sdkVersion", SDK_VERSION)
            })
            put("props", props)
        }

        val request = Request.Builder()
            .url(eventsUrl)
            .addHeader("App-Key", appKey)
            .addHeader("Content-Type", "application/json")
            .post(json.encodeToString(JsonObject.serializer(), body).toRequestBody(JSON_MEDIA))
            .build()

        scope.launch {
            runCatching {
                client.newCall(request).execute().use { response ->
                    if (!response.isSuccessful && BuildConfig.DEBUG) {
                        Log.w(TAG, "Aptabase responded ${response.code} for $event")
                    }
                }
            }.onFailure { if (BuildConfig.DEBUG) Log.w(TAG, "Aptabase send failed: ${it.message}") }
        }
    }

    @Synchronized
    private fun currentSessionId(): String {
        val now = System.currentTimeMillis()
        if (now - lastTouch > SESSION_TIMEOUT_MS) {
            sessionId = newSessionId()
        }
        lastTouch = now
        return sessionId
    }

    private fun newSessionId(): String =
        (System.currentTimeMillis() / 1000).toString() + UUID.randomUUID().toString().replace("-", "").take(20)

    private val JSON_MEDIA = "application/json; charset=utf-8".toMediaType()
}
