initialize method

Future<void> initialize({
  1. required ObslyConfig localConfig,
  2. required String apiKey,
  3. required String instanceURL,
  4. String? remoteConfigURL,
  5. String? appVersion,
  6. String? appName,
  7. String? userId,
  8. String? release,
  9. bool? proEnv,
})

Inicializa con configuración local, luego intenta obtener remote

Implementation

Future<void> initialize({
  required ObslyConfig localConfig,
  required String apiKey,
  required String instanceURL,
  String? remoteConfigURL,
  String? appVersion,
  String? appName,
  String? userId,
  String? release,
  bool? proEnv,
}) async {
  if (_isInitialized) {
    ObslyLogger.warn('ConfigController already initialized');
    return;
  }

  try {
    // 1. Aplicar configuración local con defaults (validada)
    ObslyLogger.debug(
        'Local config enableScreenshotOnUi: ${localConfig.enableScreenshotOnUi}');
    _currentConfig = ObslyConfig.withDefaults(
      enableScreenshotOnUi: localConfig.enableScreenshotOnUi,
      requestBlacklist: localConfig.requestBlacklist,
      requestBodyWhitelist: localConfig.requestBodyWhitelist,
      requestHeadersWhitelist: localConfig.requestHeadersWhitelist,
      rageClick: localConfig.rageClick,
      anonymization:
          localConfig.anonymization, // 🔥 FIX: Pass anonymization config!
      enableCrashes: localConfig.enableCrashes,
      enableLifeCycleLog: localConfig.enableLifeCycleLog,
      enableRequestLog: localConfig.enableRequestLog,
      enableTagger: localConfig.enableTagger,
      enablePerformance: localConfig.enablePerformance,
      enableMetrics: localConfig.enableMetrics,
      enableUI: localConfig.enableUI,
      automaticViewDetection: localConfig.automaticViewDetection,
      sessionMaxLengthMins: localConfig.sessionMaxLengthMins,
      bufferSize: localConfig.bufferSize,
      captureConsole: localConfig.captureConsole,
      captureBodyOnError: localConfig.captureBodyOnError,
      messengerInterval: localConfig.messengerInterval,
      enableDebugTools: localConfig.enableDebugTools,
      obslyTools: localConfig.obslyTools,
      rateLimits: localConfig.rateLimits,
      enableRateLimit: localConfig.enableRateLimit,
    ).validate();

    ObslyLogger.debug(
        '[PIIFILT] ConfigController final anonymization config: ${_currentConfig!.anonymization != null}');
    if (_currentConfig!.anonymization?.piiFilterRules != null) {
      ObslyLogger.debug(
          '[PIIFILT] ConfigController final PII rules count: ${_currentConfig!.anonymization!.piiFilterRules!.length}');
      for (final rule in _currentConfig!.anonymization!.piiFilterRules!) {
        ObslyLogger.debug(
            '[PIIFILT]   Final rule: ${rule.name} (${rule.id})');
      }
    }

    // Initialize tracking for local config (all parameters are local or default)
    _initializeLocalConfigTracking(localConfig);

    await _storeCurrentConfig();

    ObslyLogger.debug('Local config applied');

    // 2. Validar integridad de la caché antes de usarla
    final cacheValid = await _validateCacheIntegrity();
    if (!cacheValid) {
      ObslyLogger.debug(
          'Cache integrity validation failed - will fetch from server');
    }

    // 3. Intentar obtener remote config o usar caché
    final shouldFetch = cacheValid ? await _shouldFetchRemoteConfig() : true;
    ObslyLogger.debug(
        'Should fetch remote config: $shouldFetch (cache valid: $cacheValid)');

    if (shouldFetch) {
      ObslyLogger.debug('Fetching remote config from server...');
      final remoteConfig = await _fetchRemoteConfig(
        apiKey: apiKey,
        instanceURL: instanceURL,
        remoteConfigURL: remoteConfigURL,
        appVersion: appVersion ?? 'unknown',
        proEnv: proEnv ?? false,
      );

      if (remoteConfig != null) {
        // 3. Remote config SOBREESCRIBE local config, manteniendo defaults
        _updateRemoteConfigStatus(
          wasRequested: true,
          isCached: false,
          lastFetched: DateTime.now(),
          fetchSuccessful: true,
        );
        _currentConfig =
            _mergeWithDefaults(_currentConfig!, remoteConfig).validate();
        await _storeCurrentConfig();

        // Almacenar remote config y timestamp de forma atómica
        try {
          await _storeRemoteConfigWithTimestamp(remoteConfig);
          ObslyLogger.debug('Remote config stored in cache successfully');
        } catch (e) {
          ObslyLogger.warn('Failed to store remote config in cache: $e');
          // Continue anyway - we still have the config in memory
        }

        // 4. Fetch obsly_tools si están disponibles
        await _fetchObslyToolsIfAvailable(apiKey);

        ObslyLogger.debug('Remote config applied and overwrote local config');
      } else {
        ObslyLogger.warn('Failed to fetch remote config from server');
        _updateRemoteConfigStatus(
          wasRequested: true,
          fetchSuccessful: false,
          fetchError: 'Failed to fetch remote config',
        );

        // Si el fetch falla, intentar usar caché existente como fallback
        ObslyLogger.debug(
            'Attempting to use cached remote config as fallback...');
        final cachedRemote = await _loadRemoteConfigFromCache();
        if (cachedRemote != null) {
          final cacheTimestamp = await _getCacheTimestamp();
          ObslyLogger.debug(
              'Using expired cache as fallback due to network failure');
          _updateRemoteConfigStatus(
            wasRequested: true,
            isCached: true,
            lastCacheTime: cacheTimestamp,
            fetchSuccessful: false,
            fetchError: 'Using expired cache due to network failure',
          );
          _currentConfig =
              _mergeWithDefaults(_currentConfig!, cachedRemote).validate();
          await _storeCurrentConfig();

          // Fetch obsly_tools si están disponibles en caché
          await _fetchObslyToolsIfAvailable(apiKey);
        }
      }
    } else {
      ObslyLogger.debug('Using cached remote config (cache is valid)');
      final cachedRemote = await _loadRemoteConfigFromCache();
      if (cachedRemote != null) {
        final cacheTimestamp = await _getCacheTimestamp();
        _updateRemoteConfigStatus(
          wasRequested: true,
          isCached: true,
          lastCacheTime: cacheTimestamp,
          fetchSuccessful: true,
        );
        _currentConfig =
            _mergeWithDefaults(_currentConfig!, cachedRemote).validate();
        await _storeCurrentConfig();

        // Fetch obsly_tools si están disponibles en caché
        await _fetchObslyToolsIfAvailable(apiKey);

        ObslyLogger.debug('Cached remote config applied successfully');
      } else {
        ObslyLogger.warn(
            'Cache indicated as valid but no cached config found - forcing fetch');
        // El timestamp indica que hay caché, pero no se encontró el config
        // Esto puede indicar corrupción de datos - forzar fetch
        final remoteConfig = await _fetchRemoteConfig(
          apiKey: apiKey,
          instanceURL: instanceURL,
          remoteConfigURL: remoteConfigURL,
          appVersion: appVersion ?? 'unknown',
          proEnv: proEnv ?? false,
        );

        if (remoteConfig != null) {
          _updateRemoteConfigStatus(
            wasRequested: true,
            isCached: false,
            lastFetched: DateTime.now(),
            fetchSuccessful: true,
          );
          _currentConfig =
              _mergeWithDefaults(_currentConfig!, remoteConfig).validate();
          await _storeCurrentConfig();
          await _storeRemoteConfigWithTimestamp(remoteConfig);
          await _fetchObslyToolsIfAvailable(apiKey);
          ObslyLogger.debug('Remote config recovered after cache corruption');
        } else {
          _updateRemoteConfigStatus(
            wasRequested: true,
            fetchSuccessful: false,
            fetchError: 'Cache corruption detected and server fetch failed',
          );
        }
      }
    }

    _isInitialized = true;
    ObslyLogger.log('ConfigController initialized with final config');
  } catch (e) {
    ObslyLogger.error('Error initializing ConfigController: $e');
    // En caso de error, usar solo configuración local
    _currentConfig = localConfig.validate();
    _isInitialized = true;
  }
}