package com.jishisongfu.duobibi.heartbeat

import android.content.Context
import android.util.Log
import com.jishisongfu.duobibi.BuildConfig
import com.jishisongfu.duobibi.push.MiPushReceiver
import com.jishisongfu.duobibi.util.SettingsPrefs
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.TimeUnit

/**
 * 心跳发送的唯一入口。所有调用点(Application onCreate / Activity onResume /
 * a11y onServiceConnected / FloatingService onCreate / regId 收到 / Alarm 触发)
 * 都走这两个方法,trigger 标识区分。
 *
 * 网络层用 OkHttp 直发,**不走** [com.jishisongfu.duobibi.di.NetworkModule]
 * 配置的 Retrofit 实例 —— 那个 baseUrl 指向 api.duobibi.com 跑业务接口,
 * 心跳临时打到本地 mock(BuildConfig.HEARTBEAT_BASE_URL),完全独立链路。
 *
 * 调用方负责协程切线程(Dispatchers.IO),本类内部同步 execute 简化。
 */
object HeartbeatSender {

    private const val TAG = "DUOBIBI-HB"

    private val client by lazy {
        OkHttpClient.Builder()
            .connectTimeout(5, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS)
            .build()
    }

    private val json = Json { ignoreUnknownKeys = true; encodeDefaults = true }

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

    /** 拿到 regId 立刻调一次,后续即可只走 /heartbeat。失败静默,下个心跳还会带 regId。 */
    fun registerDevice(context: Context) {
        val regId = readRegId(context)
        if (regId.isNullOrEmpty()) {
            Log.w(TAG, "registerDevice skipped: no regId yet")
            return
        }
        val body = RegisterDeviceBody(
            deviceId = DeviceIdProvider.get(context),
            regId = regId,
            appVersion = BuildConfig.VERSION_NAME ?: "?",
        )
        post("/register-device", json.encodeToString(RegisterDeviceBody.serializer(), body))
    }

    /**
     * 发心跳。trigger 给后端做诊断分类用(alarm / onResume / a11y_connected / floating_create / regid_received)。
     * 失败只记 log 不重试 —— 5 分钟一次 alarm 自然会下次再来。
     */
    fun send(context: Context, trigger: String) {
        val body = HeartbeatBody(
            deviceId = DeviceIdProvider.get(context),
            regId = readRegId(context),
            a11yEnabled = A11yStateProbe.isEnabled(context),
            floatingEnabled = SettingsPrefs.floatingEnabled(context),
            appInForeground = AppForegroundProbe.isInForeground(context),
            trigger = trigger,
            clientTs = System.currentTimeMillis(),
            appVersion = BuildConfig.VERSION_NAME ?: "?",
        )
        post("/heartbeat", json.encodeToString(HeartbeatBody.serializer(), body))
    }

    private fun post(path: String, jsonBody: String) {
        val url = BuildConfig.HEARTBEAT_BASE_URL.trimEnd('/') + path
        val req = Request.Builder()
            .url(url)
            .post(jsonBody.toRequestBody(mediaJson))
            .build()
        try {
            client.newCall(req).execute().use { resp ->
                if (resp.isSuccessful) {
                    Log.i(TAG, "POST $path ok ${resp.code}")
                } else {
                    Log.w(TAG, "POST $path http ${resp.code} body=${resp.body?.string()?.take(120)}")
                }
            }
        } catch (e: Exception) {
            // 后端不在 / WiFi 没连 / 域名不通时常见,不要崩
            Log.w(TAG, "POST $path failed: ${e.javaClass.simpleName} ${e.message}")
        }
    }

    private fun readRegId(context: Context): String? =
        context.getSharedPreferences(MiPushReceiver.PREFS, Context.MODE_PRIVATE)
            .getString(MiPushReceiver.KEY_REG_ID, null)
            ?.takeIf { it.isNotEmpty() }
}
