@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.model.communication

import android.content.Context
import eu.gaudian.translator.R
import eu.gaudian.translator.model.LanguageModel
import eu.gaudian.translator.model.repository.ApiRepository
import eu.gaudian.translator.model.repository.SettingsRepository
import eu.gaudian.translator.utils.ApiCallback
import eu.gaudian.translator.utils.Log
import eu.gaudian.translator.utils.StatusAction
import eu.gaudian.translator.utils.StatusMessageService
import eu.gaudian.translator.viewmodel.MessageAction
import eu.gaudian.translator.viewmodel.MessageDisplayType
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException
import retrofit2.Response

class ApiManager(private val context: Context) {

    /**
     * Checks whether a base URL is reachable and the (optional) port is open.
     * - If no scheme is provided, http is assumed.
     * - If no port is provided, defaults to 80 for http and 443 for https.
     * Tries a TCP socket connect first, then a lightweight HTTP GET to the root.
     * Returns Pair<isAvailable, message>.
     */
    suspend fun checkProviderAvailability(baseUrl: String): Pair<Boolean, String> = withContext(Dispatchers.IO) {
        try {
            val normalized = try {

                var url = baseUrl.trim()
                if (url.isEmpty()) url = "http://localhost/"
                if (!url.startsWith("http://") && !url.startsWith("https://")) url = "http://$url"
                if (!url.endsWith('/')) url += "/"
                url
            } catch (_: Exception) {
                var url = baseUrl.trim()
                if (url.isEmpty()) url = "http://localhost/"
                if (!url.startsWith("http://") && !url.startsWith("https://")) url = "http://$url"
                if (!url.endsWith('/')) url += "/"
                url
            }

            val uri = java.net.URI(normalized)
            val host = uri.host ?: return@withContext Pair(false, "Invalid host")
            val scheme = (uri.scheme ?: "http").lowercase()
            val port = if (uri.port != -1) uri.port else if (scheme == "https") 443 else 80

            // 1) TCP connect test
            try {
                java.net.Socket().use { socket ->
                    socket.connect(java.net.InetSocketAddress(host, port), 1500)
                }
            } catch (e: Exception) {
                return@withContext Pair(false, "Cannot connect to $host:$port (${e.message})")
            }

            // 2) HTTP GET test (non-fatal if it fails; we already know port is open)
            return@withContext try {
                val client = okhttp3.OkHttpClient.Builder()
                    .connectTimeout(2, java.util.concurrent.TimeUnit.SECONDS)
                    .readTimeout(3, java.util.concurrent.TimeUnit.SECONDS)
                    .build()
                val request = okhttp3.Request.Builder().url(normalized).get().build()
                client.newCall(request).execute().use { resp ->
                    val code = resp.code
                    if (code in 200..499) {
                        Pair(true, "Reachable ($code)")
                    } else {
                        Pair(true, "Port open; HTTP $code")
                    }
                }
            } catch (e: Exception) {
                Pair(true, "Port open; HTTP check failed: ${e.message}")
            }
        } catch (e: Exception) {
            Log.e("ApiManager", "Availability check error: ${e.message}", e)
            Pair(false, e.message ?: "Unknown error")
        }
    }

    private val apiLogRepository = eu.gaudian.translator.model.repository.ApiLogRepository(context)
    private val gson = com.google.gson.Gson()

    private val apiRepository = ApiRepository(context)
    private val settingsRepository = SettingsRepository(context)

    /**
     * Validates a given API key against a specific provider's endpoint.
     */
    suspend fun validateApiKey(apiKey: String, provider: ApiProvider): Pair<Boolean, String?> {
        val validationMessage = "Validating API key for ${provider.displayName}..."
        Log.d("ApiManager", validationMessage)

        val cleanApiKey = apiKey.trim()
        if (cleanApiKey.isEmpty()) {
            return Pair(false, "API key cannot be empty.")
        }

        val tempApiService = RetrofitClient.getApiClient(cleanApiKey, provider.baseUrl, provider)
            .create(LlmApiService::class.java)

        val endpointUrl = provider.endpoint
        val validationPrompt = "Just respond with the word \"success\" if you received this message."

        // For custom providers or local hosts, allow saving the key without requiring a model.
        val base = provider.baseUrl.trim()
        val lower = base.lowercase()
        val isLocalHost = (
                lower.contains("localhost") ||
                lower.contains("127.0.0.1") ||
                lower.startsWith("192.168.") ||
                lower.startsWith("http://192.168.") ||
                lower.startsWith("10.") ||
                lower.startsWith("http://10.") ||
                Regex("^172\\.(1[6-9]|2[0-9]|3[0-1])\\.").containsMatchIn(lower) ||
                Regex("^http://172\\.(1[6-9]|2[0-9]|3[0-1])\\.").containsMatchIn(lower)
        )
        if (provider.isCustom || isLocalHost) {
            // We cannot reliably validate without a model; accept non-empty key for setup.
            return Pair(true, "Key accepted for ${provider.displayName}.")
        }

        val defaultModel = provider.defaultModel ?: return Pair(true, "No default model found; key saved. Configure a model to fully validate.")

        val result = withTimeoutOrNull(10000) { // 10-second timeout
            try {
                when (provider.key) {
                    "gemini" -> {
                        val testRequest = GeminiRequest(
                            contents = listOf(GeminiContent("user", listOf(GeminiPart(validationPrompt))))
                        )
                        val response: Response<GeminiResponse> = tempApiService.sendGeminiRequest(endpointUrl, testRequest).execute()
                        Pair(response.isSuccessful, response.body()?.toString() ?: response.errorBody()?.string())
                    }
                    else -> {
                        val testRequest = Request(
                            model = defaultModel,
                            messages = listOf(Request.Message("user", validationPrompt))
                        )
                        val response: Response<ApiResponse> = tempApiService.sendRequest(endpointUrl, testRequest).execute()
                        Pair(response.isSuccessful, response.body()?.toString() ?: response.errorBody()?.string())
                    }
                }
            } catch (e: Exception) {
                Log.e("ApiManager", "Error validating API key: ${e.message}", e)
                Pair(false, e.message)
            }
        } ?: Pair(false, "Timeout validating API key.")

        return result
    }

    /**
     * Fetch available models from provider if supported (e.g., OpenAI/OpenRouter/Mistral-compatible GET v1/models).
     * Returns Pair<List<LanguageModel>, String?> where second is an error message if any.
     */
    suspend fun fetchAvailableModels(apiKey: String?, provider: ApiProvider): Pair<List<LanguageModel>, String?> {
        val base = provider.baseUrl.trim()
        val lower = base.lowercase()
        val isLocalHost = (
                lower.contains("localhost") ||
                lower.contains("127.0.0.1") ||
                lower.startsWith("192.168.") ||
                lower.startsWith("http://192.168.") ||
                lower.startsWith("10.") ||
                lower.startsWith("http://10.") ||
                Regex("^172\\.(1[6-9]|2[0-9]|3[0-1])\\.").containsMatchIn(lower) ||
                Regex("^http://172\\.(1[6-9]|2[0-9]|3[0-1])\\.").containsMatchIn(lower)
        )

        if (provider.key == "gemini") {
            // Gemini uses a different endpoint and response shape for model listing.
            val key = apiKey?.trim().orEmpty()
            if (key.isEmpty() && !provider.isCustom) {
                return Pair(emptyList(), "API key required to list models for this provider.")
            }
            return withContext(Dispatchers.IO) {
                try {
                    val service = RetrofitClient.getApiClient(key, provider.baseUrl, provider).create(LlmApiService::class.java)
                    // Gemini model list endpoint (v1beta). We keep it relative to baseUrl like other calls.
                    val url = "v1beta/models"
                    val response = service.listGeminiModels(url).execute()
                    if (!response.isSuccessful) {
                        val err = response.errorBody()?.string() ?: "HTTP ${response.code()}"
                        Pair(emptyList(), err)
                    } else {
                        val body = response.body()
                        val items = body?.models ?: emptyList()
                        val mapped = items.map { item ->
                            val niceName = item.displayName ?: item.name ?: "unknown"
                            val parts = mutableListOf<String>()
                            item.description?.let { if (it.isNotBlank()) parts.add(it) }
                            item.inputTokenLimit?.let { if (it > 0) parts.add("in $it ctx") }
                            item.outputTokenLimit?.let { if (it > 0) parts.add("out $it ctx") }
                            item.supportedGenerationMethods?.takeIf { it.isNotEmpty() }?.let { parts.add(it.joinToString("/")) }
                            val desc = parts.joinToString(" • ")
                            val modelIdClean = (item.name ?: niceName).substringAfterLast('/')
                            LanguageModel(
                                modelId = modelIdClean,
                                displayName = niceName,
                                providerKey = provider.key,
                                description = desc,
                                isCustom = provider.isCustom
                            )
                        }
                        Pair(mapped, null)
                    }
                } catch (e: Exception) {
                    Log.e("ApiManager", "Error fetching Gemini models: ${e.message}", e)
                    Pair(emptyList(), e.message)
                }
            }
        }

        val key = apiKey?.trim().orEmpty()
        // For custom providers (and local endpoints), allow scanning without key by attempting unauthenticated GET v1/models.
        val allowNoKey = provider.isCustom || isLocalHost
        val effectiveKey = if (key.isNotEmpty()) key else if (allowNoKey) "" else key

        // Perplexity does not support listing models via /v1/models; fail fast with a clear message
        if (provider.key.equals("perplexity", ignoreCase = true)) {
            return Pair(emptyList(), "Perplexity does not support fetching modeles.") //TODO this must be transalted!
        }

        return withContext(Dispatchers.IO) {
            try {
                val service = RetrofitClient.getApiClient(effectiveKey, provider.baseUrl, provider).create(LlmApiService::class.java)
                val url =  "v1/models"
                val response = service.listModels(url).execute()
                Log.d("ApiManager", "Listing models response success=${response}")
                if (!response.isSuccessful) {
                    val err = response.errorBody()?.string() ?: "HTTP ${response.code()}"
                    Pair(emptyList(), err)
                } else {
                    val body = response.body()
                    val items = body?.data ?: emptyList()
                    val mapped = items.map { item ->
                        val niceName = item.displayName ?: item.title ?: item.name ?: item.id
                        // Build a compact description with the most useful info
                        val parts = mutableListOf<String>()
                        (item.description ?: item.ownedBy ?: item.organization)?.let { if (it.isNotBlank()) parts.add(it) }
                        val ctx = item.contextLength ?: item.maxContext ?: item.tokenLimit
                        if (ctx != null && ctx > 0) parts.add("$ctx ctx")
                        item.capabilities?.let { cap ->
                            val caps = mutableListOf<String>()
                            if (cap.vision == true) caps.add("vision")
                            if (cap.audio == true) caps.add("audio")
                            if (cap.tools == true) caps.add("tools")
                            if (cap.json == true) caps.add("json")
                            if (cap.reasoning == true) caps.add("reasoning")
                            if (caps.isNotEmpty()) parts.add(caps.joinToString("/"))
                        }
                        item.pricing?.let { p ->
                            val inPrice = p.input ?: p.inputAlt
                            val outPrice = p.output ?: p.outputAlt
                            val currency = p.currency ?: "$"
                            val unit = p.unit ?: "1K tok"
                            if (inPrice != null || outPrice != null) {
                                val priceStr = listOfNotNull(
                                    inPrice?.let { "in ${currency}${it}/${unit}" },
                                    outPrice?.let { "out ${currency}${it}/${unit}" }
                                ).joinToString(" · ")
                                if (priceStr.isNotBlank()) parts.add(priceStr)
                            }
                        }
                        if (item.deprecated == true) parts.add("deprecated")
                        val desc = parts.joinToString(" • ")
                        LanguageModel(
                            modelId = item.id,
                            displayName = niceName,
                            providerKey = provider.key,
                            description = desc,
                            isCustom = provider.isCustom
                        )
                    }
                    Pair(mapped, null)
                }
            } catch (e: Exception) {
                Log.e("ApiManager", "Error fetching models: ${e.message}", e)
                Pair(emptyList(), e.message)
            }
        }
    }

    /**
     * Validates a specific model for a given provider and API key.
     */
    suspend fun validateModel(apiKey: String, provider: ApiProvider, model: LanguageModel): Pair<Boolean, String?> {
        val validationMessage = "Validating model '${model.displayName}' for provider '${provider.displayName}'..."
        Log.d("ApiManager", validationMessage)

        val cleanApiKey = apiKey.trim()
        if (cleanApiKey.isEmpty()) {
            return Pair(false, "API key is missing for this provider.")
        }

        val tempApiService = RetrofitClient.getApiClient(cleanApiKey, provider.baseUrl, provider)
            .create(LlmApiService::class.java)

        val validationPrompt = "This is a test message to validate the model."

        val result = withTimeoutOrNull(10_000) { // 10-second timeout
            try {
                Log.d("ApiManager", "Starting validation for provider=${provider.key}, model=${model.modelId}")

                when (provider.key) {
                    "gemini" -> {
                        val modelSpecificEndpoint = "v1beta/models/${model.modelId}:generateContent"
                        val testRequest = GeminiRequest(
                            contents = listOf(GeminiContent("user", listOf(GeminiPart(validationPrompt))))
                        )

                        Log.d("ApiManager", "Sending Gemini request to $modelSpecificEndpoint with prompt=$validationPrompt")

                        val response: Response<GeminiResponse> = withContext(Dispatchers.IO) {
                            tempApiService.sendGeminiRequest(modelSpecificEndpoint, testRequest).execute()
                        }

                        Log.d("ApiManager", "Gemini response success=${response.isSuccessful}")

                        val bodyOrError = try {
                            response.body()?.toString()
                                ?: response.errorBody()?.string()
                                ?: "Unknown error"
                        } catch (ioe: Exception) {
                            Log.e("ApiManager", "Error extracting Gemini response body: ${ioe.message}", ioe)
                            "Failed to parse error body: ${ioe.message}"
                        }

                        Pair(response.isSuccessful, bodyOrError)
                    }

                    else -> {
                        val testRequest = Request(
                            model = model.modelId,
                            messages = listOf(Request.Message("user", validationPrompt))
                        )

                        Log.d("ApiManager", "Sending generic request to ${provider.endpoint} with model=${model.modelId}")

                        val response: Response<ApiResponse> = withContext(Dispatchers.IO) {
                            tempApiService.sendRequest(provider.endpoint, testRequest).execute()
                        }

                        Log.d("ApiManager", "Generic response success=${response.isSuccessful}")

                        val bodyOrError = try {
                            response.body()?.toString()
                                ?: response.errorBody()?.string()
                                ?: "Unknown error"
                        } catch (ioe: Exception) {
                            Log.e("ApiManager", "Error extracting generic response body: ${ioe.message}", ioe)
                            "Failed to parse error body: ${ioe.message}"
                        }

                        Pair(response.isSuccessful, bodyOrError)
                    }
                }
            } catch (e: Exception) {
                val errorMessage = buildString {
                    append("Exception type=${e::class.java.simpleName}")
                    e.message?.let { append(", message=$it") }
                    if (e is HttpException) {
                        append(", httpCode=${e.code()}")
                        try {
                            append(", errorBody=${e.response()?.errorBody()?.string()} ")
                        } catch (ioe: Exception) {
                            append(", failed to read errorBody: ${ioe.message}")
                        }
                    }
                }
                Log.e("ApiManager", "Exception while validating model=${model.modelId}: $errorMessage", e)
                Pair(false, errorMessage)
            }
        } ?: Pair(false, "Timeout validating model.")

        return result
    }

    /**
     * The primary method for making API requests for various tasks within the app.
     */
    fun  getCompletion(
        prompt: String,
        modelType: ModelType,
        callback: ApiCallback
    ) {
        CoroutineScope(Dispatchers.IO).launch {
            val languageModel: LanguageModel? = when (modelType) {
                ModelType.TRANSLATION -> apiRepository.getTranslationModel().first()
                ModelType.EXERCISE -> apiRepository.getExerciseModel().first()
                ModelType.VOCABULARY -> apiRepository.getVocabularyModel().first()
                ModelType.DICTIONARY -> apiRepository.getDictionaryModel().first()
            }

            if (languageModel == null) {
                val errorMsg = context.getString(R.string.no_model_selected_for_the_task, modelType.getLocalizedName(context))
                StatusMessageService.trigger(StatusAction.ShowActionableMessage(
                    text = errorMsg,
                    type = MessageDisplayType.ACTIONABLE_ERROR,
                    action = MessageAction.NAVIGATE_TO_API_KEYS
                ))
                callback.onFailure(errorMsg)
                return@launch
            }

            val allProviders = apiRepository.getProviders().first()
            val provider = allProviders.find { it.key == languageModel.providerKey }

            if (provider == null) {
                val errorMsg = "Provider '${languageModel.providerKey}' not found for the selected model."
                StatusMessageService.trigger(StatusAction.ShowMessage(errorMsg, MessageDisplayType.ERROR, 5))
                callback.onFailure(errorMsg)
                return@launch
            }

            val allApiKeys = settingsRepository.getAllApiKeys().first()
            val apiKey = allApiKeys[provider.key] ?: ""

            if (apiKey.isBlank() && !provider.isCustom) {
                val errorMsg = "API key for ${provider.displayName} is missing."
                StatusMessageService.trigger(StatusAction.ShowActionableMessage(
                    text = errorMsg,
                    type = MessageDisplayType.ACTIONABLE_ERROR,
                    action = MessageAction.NAVIGATE_TO_API_KEYS
                ))
                callback.onFailure(errorMsg)
                return@launch
            }

            val apiService = RetrofitClient.getApiClient(apiKey, provider.baseUrl, provider)
                .create(LlmApiService::class.java)

            val endpointUrl = provider.endpoint
            Log.d("ApiManager", "Sending request to ${provider.displayName} with model ${languageModel.modelId} for task $modelType and URL  ${provider.baseUrl}$endpointUrl")

            when(provider.key) {
                "gemini" -> {
                    val request = GeminiRequest(contents = listOf(GeminiContent("user", listOf(GeminiPart(prompt)))))
                    val requestJson = try { gson.toJson(request) } catch (_: Exception) { null }
                    val logId = java.util.UUID.randomUUID().toString()
                    val logTimestamp = System.currentTimeMillis()
                    val startTime = System.nanoTime()
                    apiService.sendGeminiRequest(endpointUrl, request).enqueue(object: Callback<GeminiResponse> {
                        override fun onResponse(call: Call<GeminiResponse>, response: Response<GeminiResponse>) {
                            val durationMs = ((System.nanoTime() - startTime) / 1_000_000).coerceAtLeast(0)
                            var parseError: String? = null
                            val responseJson = try {
                                gson.toJson(response.body())
                            } catch (e: Exception) {
                                parseError = "JSON parse error: ${e.message}"
                                try { response.errorBody()?.string() } catch (_: Exception) { null }
                            }
                            val entry = ApiLogEntry(
                                id = logId,
                                timestamp = logTimestamp,
                                providerKey = provider.key,
                                endpoint = endpointUrl,
                                method = "POST",
                                model = languageModel.modelId,
                                requestJson = requestJson,
                                responseCode = response.code(),
                                responseMessage = response.message(),
                                responseJson = responseJson,
                                errorMessage = if (response.isSuccessful) null else (parseError ?: response.message()),
                                durationMs = durationMs,
                                exceptionType = null,
                                isTimeout = null,
                                parseErrorMessage = parseError,
                                url = endpointUrl
                            )
                            CoroutineScope(Dispatchers.IO).launch { apiLogRepository.addLog(entry) }

                            if (response.isSuccessful && parseError == null) {
                                try {
                                    callback.onSuccess(response.body()?.getResponse())
                                } catch (e: Exception) {
                                    val err = "Response processing failed: ${e.message}"
                                    CoroutineScope(Main).launch {
                                        StatusMessageService.trigger(StatusAction.ShowMessage(err, MessageDisplayType.ERROR, 5))
                                    }
                                    callback.onFailure(err)
                                }
                            } else {
                                val errorMsg = parseError ?: "Response error: ${response.code()} ${response.message()}"
                                CoroutineScope(Main).launch {
                                    StatusMessageService.trigger(StatusAction.ShowMessage(errorMsg, MessageDisplayType.ERROR, 5))
                                }
                                callback.onFailure(errorMsg)
                            }
                        }

                        override fun onFailure(call: Call<GeminiResponse>, t: Throwable) {
                            val durationMs = ((System.nanoTime() - startTime) / 1_000_000).coerceAtLeast(0)
                            val isTimeout = t is java.net.SocketTimeoutException || t is java.io.InterruptedIOException
                            val entry = ApiLogEntry(
                                id = logId,
                                timestamp = logTimestamp,
                                providerKey = provider.key,
                                endpoint = endpointUrl,
                                method = "POST",
                                model = languageModel.modelId,
                                requestJson = requestJson,
                                responseCode = null,
                                responseMessage = null,
                                responseJson = null,
                                errorMessage = t.message,
                                durationMs = durationMs,
                                exceptionType = t::class.java.simpleName,
                                isTimeout = isTimeout,
                                parseErrorMessage = null,
                                url = endpointUrl
                            )
                            CoroutineScope(Dispatchers.IO).launch { apiLogRepository.addLog(entry) }

                            val errorPrefix = if (isTimeout) "Request timeout" else "Request failed"
                            val errorMsg = "$errorPrefix: ${t.message}"
                            CoroutineScope(Main).launch { StatusMessageService.trigger(StatusAction.ShowMessage(errorMsg, MessageDisplayType.ERROR, 5)) }
                            callback.onFailure(errorMsg)
                        }
                    })
                }
                else -> {
                    val request = Request(languageModel.modelId, listOf(Request.Message("user", prompt)))
                    val requestJson = try { gson.toJson(request) } catch (_: Exception) { null }
                    val logId = java.util.UUID.randomUUID().toString()
                    val logTimestamp = System.currentTimeMillis()
                    val startTime = System.nanoTime()
                    apiService.sendRequest(endpointUrl, request).enqueue(object : Callback<ApiResponse> {
                        override fun onResponse(call: Call<ApiResponse>, response: Response<ApiResponse>) {
                            val durationMs = ((System.nanoTime() - startTime) / 1_000_000).coerceAtLeast(0)
                            var parseError: String? = null
                            val responseJson = try {
                                gson.toJson(response.body())
                            } catch (e: Exception) {
                                parseError = "JSON parse error: ${e.message}"
                                try { response.errorBody()?.string() } catch (_: Exception) { null }
                            }
                            val entry = ApiLogEntry(
                                id = logId,
                                timestamp = logTimestamp,
                                providerKey = provider.key,
                                endpoint = endpointUrl,
                                method = "POST",
                                model = languageModel.modelId,
                                requestJson = requestJson,
                                responseCode = response.code(),
                                responseMessage = response.message(),
                                responseJson = responseJson,
                                errorMessage = if (response.isSuccessful) null else (parseError ?: response.message()),
                                durationMs = durationMs,
                                exceptionType = null,
                                isTimeout = null,
                                parseErrorMessage = parseError,
                                url = endpointUrl
                            )
                            CoroutineScope(Dispatchers.IO).launch { apiLogRepository.addLog(entry) }

                            if (response.isSuccessful && parseError == null) {
                                try {
                                    callback.onSuccess(response.body()?.getResponse())
                                } catch (e: Exception) {
                                    val err = "Response processing failed: ${e.message}"
                                    CoroutineScope(Main).launch { StatusMessageService.trigger(StatusAction.ShowMessage(err, MessageDisplayType.ERROR, 5)) }
                                    callback.onFailure(err)
                                }
                            } else {
                                val errorMsg = parseError ?: "Response error: ${response.code()} ${response.message()}"
                                CoroutineScope(Main).launch { StatusMessageService.trigger(StatusAction.ShowMessage(
                                    errorMsg, MessageDisplayType.ERROR, 5)) }
                                callback.onFailure(errorMsg)
                            }
                        }

                        override fun onFailure(call: Call<ApiResponse>, t: Throwable) {
                            val durationMs = ((System.nanoTime() - startTime) / 1_000_000).coerceAtLeast(0)
                            val isTimeout = t is java.net.SocketTimeoutException || t is java.io.InterruptedIOException
                            val entry = ApiLogEntry(
                                id = logId,
                                timestamp = logTimestamp,
                                providerKey = provider.key,
                                endpoint = endpointUrl,
                                method = "POST",
                                model = languageModel.modelId,
                                requestJson = requestJson,
                                responseCode = null,
                                responseMessage = null,
                                responseJson = null,
                                errorMessage = t.message,
                                durationMs = durationMs,
                                exceptionType = t::class.java.simpleName,
                                isTimeout = isTimeout,
                                parseErrorMessage = null,
                                url = endpointUrl
                            )
                            CoroutineScope(Dispatchers.IO).launch { apiLogRepository.addLog(entry) }

                            val errorPrefix = if (isTimeout) "Request timeout" else "Request failed"
                            val errorMsg = "$errorPrefix: ${t.message}"
                            CoroutineScope(Main).launch { StatusMessageService.trigger(StatusAction.ShowMessage(errorMsg, MessageDisplayType.ERROR, 5)) }
                            callback.onFailure(errorMsg)
                        }
                    })
            }
        }
    }

    }
}