package uk.silverlabs.silverdroid.config import android.content.Context import android.util.Log import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import java.net.HttpURLConnection import java.net.URL private const val TAG = "RemoteConfigLoader" /** * Loads configuration from remote AppStore server */ object RemoteConfigLoader { private val json = Json { ignoreUnknownKeys = true isLenient = true } /** * Fetch configuration from remote URL with optional authentication */ suspend fun fetchConfig( remoteSettings: RemoteConfigSettings, userId: String? = null ): kotlin.Result = withContext(Dispatchers.IO) { try { val url = buildConfigUrl(remoteSettings, userId) Log.i(TAG, "Fetching config from: $url") val connection = URL(url).openConnection() as HttpURLConnection connection.requestMethod = "GET" connection.connectTimeout = 10000 connection.readTimeout = 10000 // Add authentication if provided remoteSettings.authToken?.let { connection.setRequestProperty("Authorization", "Bearer $it") } // Add user ID if user-specific config userId?.let { connection.setRequestProperty("X-User-ID", it) } val responseCode = connection.responseCode if (responseCode == HttpURLConnection.HTTP_OK) { val response = connection.inputStream.bufferedReader().use { it.readText() } val config = json.decodeFromString(response) Log.i(TAG, "Successfully loaded remote config for: ${config.appName}") kotlin.Result.success(config) } else { val error = "HTTP $responseCode: ${connection.responseMessage}" Log.e(TAG, "Failed to fetch config: $error") kotlin.Result.failure(Exception(error)) } } catch (e: Exception) { Log.e(TAG, "Error fetching remote config", e) kotlin.Result.failure(e) } } private fun buildConfigUrl(settings: RemoteConfigSettings, userId: String?): String { var url = settings.url // Add user parameter if user-specific if (settings.userSpecific && userId != null) { url = if (url.contains("?")) { "$url&userId=$userId" } else { "$url?userId=$userId" } } return url } /** * Load config with remote fallback * 1. Try to load local config * 2. If remote is configured, fetch from server * 3. Merge remote with local (remote takes precedence) */ suspend fun loadConfigWithRemote( context: Context, userId: String? = null ): AppConfig { // Load local config first val localConfig = ConfigLoader.loadConfig(context) // Check if remote config is enabled val remoteSettings = localConfig.remoteConfig if (remoteSettings == null || !remoteSettings.enabled) { Log.i(TAG, "Remote config disabled, using local config") return localConfig } // Fetch remote config return fetchConfig(remoteSettings, userId).getOrElse { error -> Log.w(TAG, "Remote config failed, falling back to local", error) localConfig }.also { if (it != localConfig) { Log.i(TAG, "Using remote configuration") } } } }