import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';

import 'package:unionflow_mobile_apps/features/settings/presentation/bloc/system_settings_bloc.dart';
import 'package:unionflow_mobile_apps/features/settings/presentation/bloc/system_settings_event.dart';
import 'package:unionflow_mobile_apps/features/settings/presentation/bloc/system_settings_state.dart';
import 'package:unionflow_mobile_apps/features/settings/domain/usecases/get_settings.dart';
import 'package:unionflow_mobile_apps/features/settings/domain/usecases/update_settings.dart';
import 'package:unionflow_mobile_apps/features/settings/domain/usecases/get_cache_stats.dart';
import 'package:unionflow_mobile_apps/features/settings/domain/usecases/clear_cache.dart' as uc;
import 'package:unionflow_mobile_apps/features/settings/domain/usecases/reset_settings.dart';
import 'package:unionflow_mobile_apps/features/settings/domain/repositories/system_config_repository.dart';
import 'package:unionflow_mobile_apps/features/settings/data/models/system_config_model.dart';
import 'package:unionflow_mobile_apps/features/settings/data/models/cache_stats_model.dart';
import 'package:unionflow_mobile_apps/features/settings/data/models/system_metrics_model.dart';

@GenerateMocks([
  GetSettings,
  UpdateSettings,
  GetCacheStats,
  uc.ClearCache,
  ResetSettings,
  ISystemConfigRepository,
])
import 'system_settings_bloc_test.mocks.dart';

void main() {
  late SystemSettingsBloc bloc;
  late MockGetSettings mockGetSettings;
  late MockUpdateSettings mockUpdateSettings;
  late MockGetCacheStats mockGetCacheStats;
  late MockClearCache mockClearCache;
  late MockResetSettings mockResetSettings;
  late MockISystemConfigRepository mockRepository;

  // ── Fixtures ──────────────────────────────────────────────────────────────

  const fakeConfig = SystemConfigModel(
    applicationName: 'UnionFlow',
    version: '3.0.0',
    maintenanceMode: false,
    systemStatus: 'HEALTHY',
  );

  const fakeCacheStats = CacheStatsModel(
    totalSizeBytes: 1048576,
    totalSizeFormatted: '1 MB',
    totalEntries: 250,
    hitRate: 0.85,
  );

  const fakeMetrics = SystemMetricsModel(
    cpuUsagePercent: 30.0,
    memoryUsagePercent: 50.0,
    activeConnections: 20,
  );

  setUp(() {
    mockGetSettings = MockGetSettings();
    mockUpdateSettings = MockUpdateSettings();
    mockGetCacheStats = MockGetCacheStats();
    mockClearCache = MockClearCache();
    mockResetSettings = MockResetSettings();
    mockRepository = MockISystemConfigRepository();

    bloc = SystemSettingsBloc(
      mockGetSettings,
      mockUpdateSettings,
      mockGetCacheStats,
      mockClearCache,
      mockResetSettings,
      mockRepository,
    );
  });

  tearDown(() => bloc.close());

  // ── Initial state ─────────────────────────────────────────────────────────

  test('initial state is SystemSettingsInitial', () {
    expect(bloc.state, isA<SystemSettingsInitial>());
  });

  // ── LoadSystemConfig ──────────────────────────────────────────────────────

  group('LoadSystemConfig', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemConfigLoaded] on success',
      build: () {
        when(mockGetSettings()).thenAnswer((_) async => fakeConfig);
        return bloc;
      },
      act: (b) => b.add(LoadSystemConfig()),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemConfigLoaded>().having(
          (s) => s.config.applicationName,
          'applicationName',
          'UnionFlow',
        ),
      ],
      verify: (_) => verify(mockGetSettings()).called(1),
    );

    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsError] on failure',
      build: () {
        when(mockGetSettings()).thenThrow(Exception('config not found'));
        return bloc;
      },
      act: (b) => b.add(LoadSystemConfig()),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemSettingsError>().having(
          (s) => s.error,
          'error',
          contains('Erreur de chargement'),
        ),
      ],
    );
  });

  // ── UpdateSystemConfig ────────────────────────────────────────────────────

  group('UpdateSystemConfig', () {
    final configData = <String, dynamic>{'maintenanceMode': true};

    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemConfigLoaded, SystemSettingsSuccess] on success',
      build: () {
        when(mockUpdateSettings(configData))
            .thenAnswer((_) async => const SystemConfigModel(maintenanceMode: true));
        return bloc;
      },
      act: (b) => b.add(UpdateSystemConfig(configData)),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemConfigLoaded>(),
        isA<SystemSettingsSuccess>().having(
          (s) => s.message,
          'message',
          'Configuration mise à jour',
        ),
      ],
    );

    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsError] on update failure',
      build: () {
        when(mockUpdateSettings(any)).thenThrow(Exception('update error'));
        return bloc;
      },
      act: (b) => b.add(UpdateSystemConfig({})),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsError>()],
    );
  });

  // ── LoadCacheStats ────────────────────────────────────────────────────────

  group('LoadCacheStats', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, CacheStatsLoaded] on success',
      build: () {
        when(mockGetCacheStats()).thenAnswer((_) async => fakeCacheStats);
        return bloc;
      },
      act: (b) => b.add(LoadCacheStats()),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<CacheStatsLoaded>().having(
          (s) => s.stats.hitRate,
          'hitRate',
          0.85,
        ),
      ],
    );

    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsError] on cache stats failure',
      build: () {
        when(mockGetCacheStats()).thenThrow(Exception('cache error'));
        return bloc;
      },
      act: (b) => b.add(LoadCacheStats()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsError>()],
    );
  });

  // ── LoadSystemMetrics ─────────────────────────────────────────────────────

  group('LoadSystemMetrics', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemMetricsLoaded] on success',
      build: () {
        when(mockRepository.getMetrics()).thenAnswer((_) async => fakeMetrics);
        return bloc;
      },
      act: (b) => b.add(LoadSystemMetrics()),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemMetricsLoaded>().having(
          (s) => s.metrics.cpuUsagePercent,
          'cpuUsagePercent',
          30.0,
        ),
      ],
    );

    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsError] on metrics failure',
      build: () {
        when(mockRepository.getMetrics()).thenThrow(Exception('metrics error'));
        return bloc;
      },
      act: (b) => b.add(LoadSystemMetrics()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsError>()],
    );
  });

  // ── ClearCache ────────────────────────────────────────────────────────────

  group('ClearCache', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockClearCache()).thenAnswer((_) async {});
        return bloc;
      },
      act: (b) => b.add(ClearCache()),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemSettingsSuccess>().having(
          (s) => s.message,
          'message',
          'Cache vidé avec succès',
        ),
      ],
    );

    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsError] on clear cache failure',
      build: () {
        when(mockClearCache()).thenThrow(Exception('clear failed'));
        return bloc;
      },
      act: (b) => b.add(ClearCache()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsError>()],
    );
  });

  // ── TestDatabaseConnection ────────────────────────────────────────────────

  group('TestDatabaseConnection', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] when testDatabase returns success=true',
      build: () {
        when(mockRepository.testDatabase()).thenAnswer(
          (_) async => {'success': true, 'message': 'Connexion OK'},
        );
        return bloc;
      },
      act: (b) => b.add(TestDatabaseConnection()),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemSettingsSuccess>().having(
          (s) => s.message,
          'message',
          'Connexion OK',
        ),
      ],
    );

    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsError] when testDatabase returns success=false',
      build: () {
        when(mockRepository.testDatabase()).thenAnswer(
          (_) async => {'success': false, 'message': 'Connection refused'},
        );
        return bloc;
      },
      act: (b) => b.add(TestDatabaseConnection()),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemSettingsError>().having(
          (s) => s.error,
          'error',
          'Connection refused',
        ),
      ],
    );

    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsError] on exception',
      build: () {
        when(mockRepository.testDatabase()).thenThrow(Exception('db error'));
        return bloc;
      },
      act: (b) => b.add(TestDatabaseConnection()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsError>()],
    );
  });

  // ── TestEmailConfiguration ────────────────────────────────────────────────

  group('TestEmailConfiguration', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] when email test returns success=true',
      build: () {
        when(mockRepository.testEmail()).thenAnswer(
          (_) async => {'success': true, 'message': 'Email envoyé'},
        );
        return bloc;
      },
      act: (b) => b.add(TestEmailConfiguration()),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemSettingsSuccess>().having(
          (s) => s.message,
          'message',
          'Email envoyé',
        ),
      ],
    );

    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsError] when email test fails',
      build: () {
        when(mockRepository.testEmail()).thenThrow(Exception('smtp error'));
        return bloc;
      },
      act: (b) => b.add(TestEmailConfiguration()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsError>()],
    );
  });

  // ── ResetSystemConfig ─────────────────────────────────────────────────────

  group('ResetSystemConfig', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemConfigLoaded, SystemSettingsSuccess] on reset success',
      build: () {
        when(mockResetSettings()).thenAnswer((_) async => fakeConfig);
        return bloc;
      },
      act: (b) => b.add(ResetSystemConfig()),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemConfigLoaded>(),
        isA<SystemSettingsSuccess>().having(
          (s) => s.message,
          'message',
          'Configuration réinitialisée',
        ),
      ],
    );

    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsError] on reset failure',
      build: () {
        when(mockResetSettings()).thenThrow(Exception('reset error'));
        return bloc;
      },
      act: (b) => b.add(ResetSystemConfig()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsError>()],
    );
  });

  // ── Repository-delegated actions ──────────────────────────────────────────

  group('OptimizeDatabase', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.optimizeDatabase())
            .thenAnswer((_) async => {'message': 'Base de données optimisée'});
        return bloc;
      },
      act: (b) => b.add(OptimizeDatabase()),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemSettingsSuccess>().having(
          (s) => s.message,
          'message',
          'Base de données optimisée',
        ),
      ],
    );

    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsError] on failure',
      build: () {
        when(mockRepository.optimizeDatabase()).thenThrow(Exception('optimize error'));
        return bloc;
      },
      act: (b) => b.add(OptimizeDatabase()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsError>()],
    );
  });

  group('ForceGlobalLogout', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.forceGlobalLogout())
            .thenAnswer((_) async => {'message': 'Déconnexion globale déclenchée'});
        return bloc;
      },
      act: (b) => b.add(ForceGlobalLogout()),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemSettingsSuccess>(),
      ],
    );
  });

  group('CleanupSessions', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.cleanupSessions())
            .thenAnswer((_) async => {'message': 'Sessions nettoyées'});
        return bloc;
      },
      act: (b) => b.add(CleanupSessions()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsSuccess>()],
    );
  });

  group('CleanOldLogs', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.cleanOldLogs())
            .thenAnswer((_) async => {'message': 'Logs nettoyés'});
        return bloc;
      },
      act: (b) => b.add(CleanOldLogs()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsSuccess>()],
    );
  });

  group('ExportLogs', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] with count message',
      build: () {
        when(mockRepository.exportLogs())
            .thenAnswer((_) async => {'count': 42});
        return bloc;
      },
      act: (b) => b.add(ExportLogs()),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemSettingsSuccess>().having(
          (s) => s.message,
          'message',
          '42 log(s) exporté(s)',
        ),
      ],
    );
  });

  group('PurgeExpiredData', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.purgeExpiredData())
            .thenAnswer((_) async => {'message': 'Données purgées'});
        return bloc;
      },
      act: (b) => b.add(PurgeExpiredData()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsSuccess>()],
    );
  });

  group('ScheduleMaintenance', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.scheduleMaintenance(
          scheduledAt: anyNamed('scheduledAt'),
          reason: anyNamed('reason'),
        )).thenAnswer((_) async => {'message': 'Maintenance planifiée'});
        return bloc;
      },
      act: (b) => b.add(const ScheduleMaintenance(
        scheduledAt: '2026-05-01T02:00:00',
        reason: 'Mise à jour',
      )),
      expect: () => [
        isA<SystemSettingsLoading>(),
        isA<SystemSettingsSuccess>().having(
          (s) => s.message,
          'message',
          'Maintenance planifiée',
        ),
      ],
    );
  });

  group('CreateBackup (settings)', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.createBackup())
            .thenAnswer((_) async => {'message': 'Sauvegarde créée'});
        return bloc;
      },
      act: (b) => b.add(CreateBackup()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsSuccess>()],
    );
  });

  group('GenerateAuditReport', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.generateAuditReport())
            .thenAnswer((_) async => {'message': "Rapport d'audit généré"});
        return bloc;
      },
      act: (b) => b.add(GenerateAuditReport()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsSuccess>()],
    );
  });

  group('GenerateUsageReport', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.generateUsageReport())
            .thenAnswer((_) async => {'message': 'Rapport généré'});
        return bloc;
      },
      act: (b) => b.add(GenerateUsageReport()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsSuccess>()],
    );
  });

  group('ExportGDPRData', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.exportGDPRData())
            .thenAnswer((_) async => {'message': 'Export RGPD initié'});
        return bloc;
      },
      act: (b) => b.add(ExportGDPRData()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsSuccess>()],
    );
  });

  group('CheckUpdates', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.checkUpdates())
            .thenAnswer((_) async => {'message': 'Vérification terminée'});
        return bloc;
      },
      act: (b) => b.add(CheckUpdates()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsSuccess>()],
    );
  });

  group('EmergencyMaintenance', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.emergencyMaintenance())
            .thenAnswer((_) async => {'message': "Maintenance d'urgence activée"});
        return bloc;
      },
      act: (b) => b.add(EmergencyMaintenance()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsSuccess>()],
    );
  });

  group('AnalyzePerformance', () {
    blocTest<SystemSettingsBloc, SystemSettingsState>(
      'emits [Loading, SystemSettingsSuccess] on success',
      build: () {
        when(mockRepository.analyzePerformance())
            .thenAnswer((_) async => {'message': 'Analyse terminée'});
        return bloc;
      },
      act: (b) => b.add(AnalyzePerformance()),
      expect: () => [isA<SystemSettingsLoading>(), isA<SystemSettingsSuccess>()],
    );
  });
}
