/// Offline-first manager for connectivity monitoring and operation queueing
library offline_manager;

import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:injectable/injectable.dart';
import '../storage/pending_operations_store.dart';
import '../utils/logger.dart' show AppLogger;

/// Status of network connectivity
enum ConnectivityStatus {
  online,
  offline,
  unknown,
}

/// Offline manager that monitors connectivity and manages offline operations
@singleton
class OfflineManager {
  final Connectivity _connectivity;
  final PendingOperationsStore _operationsStore;

  ConnectivityStatus _currentStatus = ConnectivityStatus.unknown;
  final _statusController = StreamController<ConnectivityStatus>.broadcast();
  StreamSubscription<List<ConnectivityResult>>? _connectivitySubscription;

  OfflineManager(
    this._connectivity,
    this._operationsStore,
  ) {
    _initConnectivityMonitoring();
  }

  /// Current connectivity status
  ConnectivityStatus get currentStatus => _currentStatus;

  /// Stream of connectivity status changes
  Stream<ConnectivityStatus> get statusStream => _statusController.stream;

  /// Check if device is currently online
  Future<bool> get isOnline async {
    final result = await _connectivity.checkConnectivity();
    return result.any((r) => r != ConnectivityResult.none);
  }

  /// Initialize connectivity monitoring
  void _initConnectivityMonitoring() {
    // Check initial connectivity
    _connectivity.checkConnectivity().then((result) {
      _updateStatus(result);
    });

    // Listen for connectivity changes
    _connectivitySubscription = _connectivity.onConnectivityChanged.listen(
      _updateStatus,
      onError: (error) {
        AppLogger.error('Connectivity monitoring error', error: error);
        _updateStatus([ConnectivityResult.none]);
      },
    );
  }

  /// Update connectivity status
  void _updateStatus(List<ConnectivityResult> results) {
    final isConnected = results.any((r) => r != ConnectivityResult.none);
    final newStatus = isConnected
        ? ConnectivityStatus.online
        : ConnectivityStatus.offline;

    if (newStatus != _currentStatus) {
      final previousStatus = _currentStatus;
      _currentStatus = newStatus;
      _statusController.add(newStatus);

      AppLogger.info('Connectivity changed: $previousStatus → $newStatus');

      // When back online, process pending operations
      if (newStatus == ConnectivityStatus.online &&
          previousStatus == ConnectivityStatus.offline) {
        _processPendingOperations();
      }
    }
  }

  /// Queue an operation for later retry when offline
  Future<void> queueOperation({
    required String operationType,
    required String endpoint,
    required Map<String, dynamic> data,
    Map<String, String>? headers,
  }) async {
    try {
      await _operationsStore.addPendingOperation(
        operationType: operationType,
        endpoint: endpoint,
        data: data,
        headers: headers,
      );
      AppLogger.info('Operation queued: $operationType on $endpoint');
    } catch (e) {
      AppLogger.error('Failed to queue operation', error: e);
    }
  }

  /// Process all pending operations when back online
  Future<void> _processPendingOperations() async {
    AppLogger.info('Processing pending operations...');

    try {
      final operations = await _operationsStore.getPendingOperations();

      if (operations.isEmpty) {
        AppLogger.info('No pending operations to process');
        return;
      }

      AppLogger.info('Found ${operations.length} pending operations');

      // Process operations one by one
      for (final operation in operations) {
        try {
          // Note: Actual retry logic is delegated to the calling code
          // This manager only provides the queuing mechanism
          AppLogger.info('Pending operation ready for retry: ${operation['operationType']}');
        } catch (e) {
          AppLogger.error('Error processing pending operation', error: e);
        }
      }
    } catch (e) {
      AppLogger.error('Failed to process pending operations', error: e);
    }
  }

  /// Manually trigger processing of pending operations
  Future<void> retryPendingOperations() async {
    if (_currentStatus == ConnectivityStatus.online) {
      await _processPendingOperations();
    } else {
      AppLogger.warning('Cannot retry pending operations while offline');
    }
  }

  /// Clear all pending operations
  Future<void> clearPendingOperations() async {
    try {
      await _operationsStore.clearAll();
      AppLogger.info('Pending operations cleared');
    } catch (e) {
      AppLogger.error('Failed to clear pending operations', error: e);
    }
  }

  /// Get count of pending operations
  Future<int> getPendingOperationsCount() async {
    try {
      final operations = await _operationsStore.getPendingOperations();
      return operations.length;
    } catch (e) {
      AppLogger.error('Failed to get pending operations count', error: e);
      return 0;
    }
  }

  /// Dispose resources
  void dispose() {
    _connectivitySubscription?.cancel();
    _statusController.close();
  }
}
