/// Storage for pending operations that failed due to network issues
library pending_operations_store;

import 'dart:convert';
import 'package:injectable/injectable.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../utils/logger.dart' show AppLogger;

/// Store for persisting failed operations to retry later
@singleton
class PendingOperationsStore {
  static const String _keyPendingOperations = 'pending_operations';
  final SharedPreferences _preferences;

  PendingOperationsStore(this._preferences);

  /// Add a pending operation to the store
  Future<void> addPendingOperation({
    required String operationType,
    required String endpoint,
    required Map<String, dynamic> data,
    Map<String, String>? headers,
  }) async {
    try {
      final operations = await getPendingOperations();

      final newOperation = {
        'id': DateTime.now().millisecondsSinceEpoch.toString(),
        'operationType': operationType,
        'endpoint': endpoint,
        'data': data,
        'headers': headers ?? {},
        'timestamp': DateTime.now().toIso8601String(),
        'retryCount': 0,
      };

      operations.add(newOperation);

      await _saveOperations(operations);

      AppLogger.info('Pending operation added: $operationType on $endpoint');
    } catch (e) {
      AppLogger.error('Failed to add pending operation', error: e);
      rethrow;
    }
  }

  /// Get all pending operations
  Future<List<Map<String, dynamic>>> getPendingOperations() async {
    try {
      final json = _preferences.getString(_keyPendingOperations);

      if (json == null || json.isEmpty) {
        return [];
      }

      final List<dynamic> decoded = jsonDecode(json);
      return decoded.cast<Map<String, dynamic>>();
    } catch (e) {
      AppLogger.error('Failed to get pending operations', error: e);
      return [];
    }
  }

  /// Remove a pending operation by ID
  Future<void> removePendingOperation(String id) async {
    try {
      final operations = await getPendingOperations();
      operations.removeWhere((op) => op['id'] == id);
      await _saveOperations(operations);

      AppLogger.info('Pending operation removed: $id');
    } catch (e) {
      AppLogger.error('Failed to remove pending operation', error: e);
      rethrow;
    }
  }

  /// Update retry count for an operation
  Future<void> incrementRetryCount(String id) async {
    try {
      final operations = await getPendingOperations();

      final index = operations.indexWhere((op) => op['id'] == id);
      if (index != -1) {
        operations[index]['retryCount'] = (operations[index]['retryCount'] ?? 0) + 1;
        operations[index]['lastRetryTimestamp'] = DateTime.now().toIso8601String();
        await _saveOperations(operations);
      }
    } catch (e) {
      AppLogger.error('Failed to increment retry count', error: e);
      rethrow;
    }
  }

  /// Clear all pending operations
  Future<void> clearAll() async {
    try {
      await _preferences.remove(_keyPendingOperations);
      AppLogger.info('All pending operations cleared');
    } catch (e) {
      AppLogger.error('Failed to clear pending operations', error: e);
      rethrow;
    }
  }

  /// Remove operations older than a certain duration
  Future<void> removeOldOperations({Duration maxAge = const Duration(days: 7)}) async {
    try {
      final operations = await getPendingOperations();
      final now = DateTime.now();

      final filtered = operations.where((op) {
        final timestamp = DateTime.parse(op['timestamp'] as String);
        return now.difference(timestamp) < maxAge;
      }).toList();

      if (filtered.length != operations.length) {
        await _saveOperations(filtered);
        AppLogger.info('Removed ${operations.length - filtered.length} old operations');
      }
    } catch (e) {
      AppLogger.error('Failed to remove old operations', error: e);
    }
  }

  /// Get operations by type
  Future<List<Map<String, dynamic>>> getOperationsByType(String operationType) async {
    try {
      final operations = await getPendingOperations();
      return operations.where((op) => op['operationType'] == operationType).toList();
    } catch (e) {
      AppLogger.error('Failed to get operations by type', error: e);
      return [];
    }
  }

  /// Get count of pending operations
  Future<int> getCount() async {
    final operations = await getPendingOperations();
    return operations.length;
  }

  /// Save operations to storage
  Future<void> _saveOperations(List<Map<String, dynamic>> operations) async {
    try {
      final json = jsonEncode(operations);
      await _preferences.setString(_keyPendingOperations, json);
    } catch (e) {
      AppLogger.error('Failed to save operations', error: e);
      rethrow;
    }
  }
}
