package com.a2i.forwarder.core

import com.a2i.forwarder.model.BarkMessage
import com.a2i.forwarder.model.BarkServer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.IOException
import java.util.concurrent.TimeUnit

class BarkClient(private val server: BarkServer) {
    private val json = Json { encodeDefaults = false; ignoreUnknownKeys = true }
    private val http = OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .readTimeout(15, TimeUnit.SECONDS)
        .retryOnConnectionFailure(true)
        .build()

    suspend fun push(msg: BarkMessage): Result<Unit> = withContext(Dispatchers.IO) {
        runCatching {
            require(server.deviceKey.isNotBlank()) { "device key 为空" }
            doPushWithRetry(msg)
        }
    }

    /**
     * 带重试的推送：对 connection closed / 超时 / TLS 中断等瞬时 IOException 自动重试。
     * OkHttp 的 retryOnConnectionFailure 不覆盖 POST 读响应阶段的连接中断，故在此手动补。
     * HTTP 4xx/5xx 是服务端拒绝，重试无意义，直接抛出。
     */
    private suspend fun doPushWithRetry(msg: BarkMessage) {
        val body = json.encodeToString(msg).toRequestBody(JSON)
        val url = server.server.trim().removeSuffix("/") + "/push"
        val req = Request.Builder().url(url).post(body).build()
        var lastError: IOException? = null
        repeat(MAX_ATTEMPTS) { attempt ->
            try {
                http.newCall(req).execute().use { res ->
                    if (res.isSuccessful) return
                    error("HTTP ${res.code}")
                }
            } catch (e: IOException) {
                lastError = e
                if (attempt < MAX_ATTEMPTS - 1) delay(BACKOFF_MS.getOrElse(attempt) { 1000L })
            }
        }
        throw lastError ?: error("推送失败")
    }

    companion object {
        private val JSON = "application/json; charset=utf-8".toMediaType()
        private const val MAX_ATTEMPTS = 3
        private val BACKOFF_MS = longArrayOf(600L, 1200L)
    }
}
