import 'package:flutter/foundation.dart';
import 'package:hive_ce/hive.dart';

import 'package:worker/core/constants/storage_constants.dart';
import 'package:worker/core/database/hive_service.dart';

/// Database Manager for common Hive operations
///
/// Provides high-level database operations and utilities for working
/// with Hive boxes across the application.
///
/// Features:
/// - CRUD operations with error handling
/// - Cache management with expiration
/// - Bulk operations
/// - Data validation
/// - Sync state tracking
class DatabaseManager {
  DatabaseManager({HiveService? hiveService})
    : _hiveService = hiveService ?? HiveService();

  final HiveService _hiveService;

  /// Get a box safely
  Box<T> _getBox<T>(String boxName) {
    if (!_hiveService.isBoxOpen(boxName)) {
      throw HiveError(
        'Box $boxName is not open. Initialize HiveService first.',
      );
    }
    return _hiveService.getBox<T>(boxName);
  }

  // ==================== Generic CRUD Operations ====================

  /// Save a value to a box
  Future<void> save<T>({
    required String boxName,
    required String key,
    required T value,
  }) async {
    try {
      final box = _getBox<T>(boxName);
      await box.put(key, value);
      debugPrint('DatabaseManager: Saved $key to $boxName');
    } catch (e, stackTrace) {
      debugPrint('DatabaseManager: Error saving $key to $boxName: $e');
      debugPrint('StackTrace: $stackTrace');
      rethrow;
    }
  }

  /// Get a value from a box
  T? get<T>({required String boxName, required String key, T? defaultValue}) {
    try {
      final box = _getBox<T>(boxName);
      return box.get(key, defaultValue: defaultValue);
    } catch (e, stackTrace) {
      debugPrint('DatabaseManager: Error getting $key from $boxName: $e');
      debugPrint('StackTrace: $stackTrace');
      return defaultValue;
    }
  }

  /// Delete a value from a box
  Future<void> delete({required String boxName, required String key}) async {
    try {
      final box = _getBox<dynamic>(boxName);
      await box.delete(key);
      debugPrint('DatabaseManager: Deleted $key from $boxName');
    } catch (e, stackTrace) {
      debugPrint('DatabaseManager: Error deleting $key from $boxName: $e');
      debugPrint('StackTrace: $stackTrace');
      rethrow;
    }
  }

  /// Check if a key exists in a box
  bool exists({required String boxName, required String key}) {
    try {
      final box = _getBox<dynamic>(boxName);
      return box.containsKey(key);
    } catch (e) {
      debugPrint('DatabaseManager: Error checking $key in $boxName: $e');
      return false;
    }
  }

  /// Get all values from a box
  List<T> getAll<T>({required String boxName}) {
    try {
      final box = _getBox<T>(boxName);
      return box.values.toList();
    } catch (e, stackTrace) {
      debugPrint('DatabaseManager: Error getting all from $boxName: $e');
      debugPrint('StackTrace: $stackTrace');
      return [];
    }
  }

  /// Save multiple values to a box
  Future<void> saveAll<T>({
    required String boxName,
    required Map<String, T> entries,
  }) async {
    try {
      final box = _getBox<T>(boxName);
      await box.putAll(entries);
      debugPrint('DatabaseManager: Saved ${entries.length} items to $boxName');
    } catch (e, stackTrace) {
      debugPrint('DatabaseManager: Error saving all to $boxName: $e');
      debugPrint('StackTrace: $stackTrace');
      rethrow;
    }
  }

  /// Clear all data from a box
  Future<void> clearBox({required String boxName}) async {
    try {
      final box = _getBox<dynamic>(boxName);
      await box.clear();
      debugPrint('DatabaseManager: Cleared $boxName');
    } catch (e, stackTrace) {
      debugPrint('DatabaseManager: Error clearing $boxName: $e');
      debugPrint('StackTrace: $stackTrace');
      rethrow;
    }
  }

  // ==================== Cache Operations ====================

  /// Save data to cache with timestamp
  Future<void> saveToCache<T>({required String key, required T data}) async {
    try {
      final cacheBox = _getBox<dynamic>(HiveBoxNames.cacheBox);
      await cacheBox.put(key, {
        'data': data,
        'timestamp': DateTime.now().toIso8601String(),
      });
      debugPrint('DatabaseManager: Cached $key');
    } catch (e, stackTrace) {
      debugPrint('DatabaseManager: Error caching $key: $e');
      debugPrint('StackTrace: $stackTrace');
      rethrow;
    }
  }

  /// Get data from cache
  ///
  /// Returns null if cache is expired or doesn't exist
  T? getFromCache<T>({required String key, Duration? maxAge}) {
    try {
      final cacheBox = _getBox<dynamic>(HiveBoxNames.cacheBox);
      final cachedData = cacheBox.get(key) as Map<dynamic, dynamic>?;

      if (cachedData == null) {
        debugPrint('DatabaseManager: Cache miss for $key');
        return null;
      }

      // Check if cache is expired
      if (maxAge != null) {
        final timestamp = DateTime.parse(cachedData['timestamp'] as String);
        final age = DateTime.now().difference(timestamp);

        if (age > maxAge) {
          debugPrint('DatabaseManager: Cache expired for $key (age: $age)');
          return null;
        }
      }

      debugPrint('DatabaseManager: Cache hit for $key');
      return cachedData['data'] as T?;
    } catch (e, stackTrace) {
      debugPrint('DatabaseManager: Error getting cache $key: $e');
      debugPrint('StackTrace: $stackTrace');
      return null;
    }
  }

  /// Check if cache is valid (exists and not expired)
  bool isCacheValid({required String key, Duration? maxAge}) {
    try {
      final cacheBox = _getBox<dynamic>(HiveBoxNames.cacheBox);
      final cachedData = cacheBox.get(key) as Map<dynamic, dynamic>?;

      if (cachedData == null) return false;

      if (maxAge != null) {
        final timestamp = DateTime.parse(cachedData['timestamp'] as String);
        final age = DateTime.now().difference(timestamp);
        return age <= maxAge;
      }

      return true;
    } catch (e) {
      debugPrint('DatabaseManager: Error checking cache validity $key: $e');
      return false;
    }
  }

  /// Clear expired cache entries
  Future<void> clearExpiredCache() async {
    try {
      final cacheBox = _getBox<dynamic>(HiveBoxNames.cacheBox);
      final keysToDelete = <String>[];

      for (final key in cacheBox.keys) {
        final cachedData = cacheBox.get(key) as Map<dynamic, dynamic>?;
        if (cachedData != null) {
          try {
            final timestamp = DateTime.parse(cachedData['timestamp'] as String);
            final age = DateTime.now().difference(timestamp);

            // Use default max age of 24 hours
            if (age > const Duration(hours: 24)) {
              keysToDelete.add(key as String);
            }
          } catch (e) {
            // Invalid cache entry, mark for deletion
            keysToDelete.add(key as String);
          }
        }
      }

      for (final key in keysToDelete) {
        await cacheBox.delete(key);
      }

      debugPrint(
        'DatabaseManager: Cleared ${keysToDelete.length} expired cache entries',
      );
    } catch (e, stackTrace) {
      debugPrint('DatabaseManager: Error clearing expired cache: $e');
      debugPrint('StackTrace: $stackTrace');
    }
  }

  // ==================== Sync State Operations ====================

  /// Update sync timestamp for a data type
  Future<void> updateSyncTime(String dataType) async {
    try {
      final syncBox = _getBox<dynamic>(HiveBoxNames.syncStateBox);
      await syncBox.put(dataType, DateTime.now().toIso8601String());
      debugPrint('DatabaseManager: Updated sync time for $dataType');
    } catch (e, stackTrace) {
      debugPrint('DatabaseManager: Error updating sync time for $dataType: $e');
      debugPrint('StackTrace: $stackTrace');
    }
  }

  /// Get last sync time for a data type
  DateTime? getLastSyncTime(String dataType) {
    try {
      final syncBox = _getBox<dynamic>(HiveBoxNames.syncStateBox);
      final timestamp = syncBox.get(dataType);

      if (timestamp == null) return null;

      return DateTime.parse(timestamp as String);
    } catch (e) {
      debugPrint('DatabaseManager: Error getting sync time for $dataType: $e');
      return null;
    }
  }

  /// Check if data needs sync
  bool needsSync({required String dataType, required Duration syncInterval}) {
    final lastSync = getLastSyncTime(dataType);

    if (lastSync == null) return true;

    final timeSinceSync = DateTime.now().difference(lastSync);
    return timeSinceSync > syncInterval;
  }

  // ==================== Settings Operations ====================

  /// Save a setting
  Future<void> saveSetting<T>({required String key, required T value}) async {
    await save(boxName: HiveBoxNames.settingsBox, key: key, value: value);
  }

  /// Get a setting
  T? getSetting<T>({required String key, T? defaultValue}) {
    return get(
      boxName: HiveBoxNames.settingsBox,
      key: key,
      defaultValue: defaultValue,
    );
  }

  // ==================== Offline Queue Operations ====================

  /// Add request to offline queue
  Future<void> addToOfflineQueue(Map<String, dynamic> request) async {
    try {
      final queueBox = _getBox<dynamic>(HiveBoxNames.offlineQueueBox);

      // Check queue size limit
      if (queueBox.length >= HiveDatabaseConfig.maxOfflineQueueSize) {
        debugPrint(
          'DatabaseManager: Offline queue is full, removing oldest item',
        );
        await queueBox.deleteAt(0);
      }

      await queueBox.add({
        ...request,
        'timestamp': DateTime.now().toIso8601String(),
      });

      debugPrint('DatabaseManager: Added request to offline queue');
    } catch (e, stackTrace) {
      debugPrint('DatabaseManager: Error adding to offline queue: $e');
      debugPrint('StackTrace: $stackTrace');
      rethrow;
    }
  }

  /// Get all offline queue items
  List<Map<String, dynamic>> getOfflineQueue() {
    try {
      final queueBox = _getBox<dynamic>(HiveBoxNames.offlineQueueBox);
      return queueBox.values
          .map((e) => Map<String, dynamic>.from(e as Map<dynamic, dynamic>))
          .toList();
    } catch (e) {
      debugPrint('DatabaseManager: Error getting offline queue: $e');
      return [];
    }
  }

  /// Remove item from offline queue
  Future<void> removeFromOfflineQueue(int index) async {
    try {
      final queueBox = _getBox<dynamic>(HiveBoxNames.offlineQueueBox);
      await queueBox.deleteAt(index);
      debugPrint('DatabaseManager: Removed item $index from offline queue');
    } catch (e, stackTrace) {
      debugPrint('DatabaseManager: Error removing from offline queue: $e');
      debugPrint('StackTrace: $stackTrace');
      rethrow;
    }
  }

  /// Clear offline queue
  Future<void> clearOfflineQueue() async {
    await clearBox(boxName: HiveBoxNames.offlineQueueBox);
  }

  // ==================== Statistics ====================

  /// Get database statistics
  Map<String, dynamic> getStatistics() {
    final stats = <String, dynamic>{};

    for (final boxName in HiveBoxNames.allBoxes) {
      try {
        if (_hiveService.isBoxOpen(boxName)) {
          final box = _getBox<dynamic>(boxName);
          stats[boxName] = {'count': box.length, 'keys': box.keys.length};
        }
      } catch (e) {
        stats[boxName] = {'error': e.toString()};
      }
    }

    return stats;
  }

  /// Print database statistics
  void printStatistics() {
    final stats = getStatistics();
    debugPrint('=== Hive Database Statistics ===');
    stats.forEach((boxName, data) {
      debugPrint('$boxName: $data');
    });
    debugPrint('================================');
  }
}
