fetchConfigFromCDN function

Future<CDNConfig> fetchConfigFromCDN([
  1. CDNConfigOptions? options
])

Fetches configuration from CDN with automatic caching This function never throws errors - it always returns a valid config from cache, CDN, or local file

Strategy:

  1. Check if cache is valid (within 5 minutes) -> return cached config
  2. If cache invalid/missing -> fetch from CDN URL
  3. If CDN fetch fails -> try to return stored config
  4. If all fails -> return local bundled kp-config.json

@param options CDN configuration options @returns Future

Implementation

Future<CDNConfig> fetchConfigFromCDN([CDNConfigOptions? options]) async {
  // Ensure CDN config is initialized
  await cdnConfigInstance.initialize();

  try {
    final opts = CDNConfigOptions();

    // Strategy 1: Check cache validity first
    final isValid = await cdnConfigInstance.isCacheValid();

    if (isValid) {
      final cachedConfig = await cdnConfigInstance.getCachedConfig();
      if (cachedConfig != null) {
        // Alert: Config fetched from local cache
        _showConfigSourceNotification('Config loaded from local cache', isLocal: true);
        return cachedConfig;
      }
    }

    // Strategy 2: Cache is invalid or doesn't exist, fetch from CDN
    final url = '${opts.cdnBaseUrl}${opts.configPath}.json';

    try {
      // Alert: Fetching config from remote CDN
      _showConfigSourceNotification('Fetching config from remote CDN...', isLocal: false);

      final dio = Dio();
      final response = await dio.get(
        url,
        options: Options(
          headers: {
            'Accept': 'application/json',
            'User-Agent': 'KwikPass-SDK-Flutter',
          },
          receiveTimeout: const Duration(seconds: 10),
          sendTimeout: const Duration(seconds: 10),
        ),
      );

      if (response.statusCode == 200) {
        // Process the CDN response (handles minified JSON)
        final jsonData = response.data is String
            ? jsonDecode(response.data as String) as Map<String, dynamic>
            : response.data as Map<String, dynamic>;
        final processedConfig = cdnConfigInstance.processCDNResponse(jsonData);

        // Cache the fresh config with new timestamp
        await cdnConfigInstance.cacheConfig(processedConfig);

        // Alert: Successfully fetched from remote CDN
        _showConfigSourceNotification('Config successfully fetched from remote CDN', isLocal: false);
        return processedConfig;
      } else {
        throw Exception('CDN returned status code: ${response.statusCode}');
      }
    } catch (cdnError) {
      // print('[CDN Config] ⚠️ CDN fetch failed: $cdnError');

      // Strategy 3: Try to return existing stored config if valid
      try {
        final storedConfig = await cdnConfigInstance.getStoredCDNConfig();
        if (storedConfig != null) {
          // Alert: Using stored config as fallback
          _showConfigSourceNotification('Using stored config (CDN unavailable)', isLocal: true);
          return storedConfig;
        }
      } catch (storageError) {
        // print('[CDN Config] ⚠️ Failed to retrieve stored config: $storageError');
      }

      // Strategy 4: Final fallback - use the local kp-config.json file
      // Alert: Using local bundled config as final fallback
      _showConfigSourceNotification('Using local bundled config (fallback)', isLocal: true);
      final localConfig = await _loadLocalConfig();

      // Cache the local config for future use
      try {
        await cdnConfigInstance.cacheConfig(localConfig);
      } catch (cacheError) {
        // print('[CDN Config] ⚠️ Failed to cache local config: $cacheError');
      }

      return localConfig;
    }
  } catch (error) {
    // Ultimate fallback - if everything fails, return local config
    // print('[CDN Config] ❌ Unexpected error, using local kp-config.json: $error');
    return await _loadLocalConfig();
  }
}