package eu.brassepc.fitnessdroid.data import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.put import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import java.util.concurrent.TimeUnit class GraphQlException(val messages: List) : Exception(messages.joinToString("; ")) /** * Minimal GraphQL-klient över OkHttp. Samma request-form som webbklienten * (gymTrackerApi.js): POST {query, variables} + "Authorization: Bearer ". */ class GraphQlClient(private val urlProvider: suspend () -> String) { private val http = OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build() val json = Json { ignoreUnknownKeys = true } suspend fun execute( query: String, variables: JsonObject = buildJsonObject {}, token: String? = null, ): JsonObject = withContext(Dispatchers.IO) { val body = buildJsonObject { put("query", query) put("variables", variables) } val request = Request.Builder() .url(urlProvider()) .post(body.toString().toRequestBody("application/json".toMediaType())) .apply { if (token != null) header("Authorization", "Bearer $token") } .build() http.newCall(request).execute().use { response -> val text = response.body?.string().orEmpty() // 5xx = servern/proxyn nere — behandla som nätfel (retry) och inte // som ett logiskt API-fel (som t.ex. loggar ut användaren). if (response.code >= 500) { throw java.io.IOException("HTTP ${response.code} från servern") } val root = runCatching { json.parseToJsonElement(text).jsonObject }.getOrNull() ?: throw GraphQlException(listOf("HTTP ${response.code}: oväntat svar från servern")) root["errors"]?.jsonArray?.takeIf { it.isNotEmpty() }?.let { errors -> throw GraphQlException( errors.map { it.jsonObject["message"]?.jsonPrimitive?.content ?: "Okänt fel" } ) } root["data"]?.jsonObject ?: throw GraphQlException(listOf("HTTP ${response.code}: tomt svar från servern")) } } }