import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:hive_ce_flutter/hive_flutter.dart';
import 'package:path_provider/path_provider.dart';

import 'package:worker/core/constants/storage_constants.dart';
import 'package:worker/hive_registrar.g.dart';

/// Hive CE (Community Edition) Database Service
///
/// This service manages the initialization, configuration, and lifecycle
/// of the Hive database for offline-first functionality.
///
/// Features:
/// - Box initialization and registration
/// - Type adapter registration
/// - Encryption support
/// - Database compaction
/// - Migration handling
/// - Error recovery
class HiveService {
  HiveService._internal();

  // Singleton pattern
  factory HiveService() => _instance;

  static final HiveService _instance = HiveService._internal();

  /// Indicates whether Hive has been initialized
  bool _isInitialized = false;
  bool get isInitialized => _isInitialized;

  /// Encryption cipher (if enabled)
  HiveAesCipher? _encryptionCipher;

  /// Initialize Hive database
  ///
  /// This should be called once during app startup, before any
  /// Hive operations are performed.
  ///
  /// [encryptionKey] - Optional 256-bit encryption key for secure storage
  /// If not provided and encryption is enabled, a new key will be generated.
  Future<void> initialize({List<int>? encryptionKey}) async {
    if (_isInitialized) {
      debugPrint('HiveService: Already initialized');
      return;
    }

    try {
      debugPrint('HiveService: Initializing Hive CE...');

      // Initialize Hive for Flutter
      await Hive.initFlutter();

      // Setup encryption if enabled
      if (HiveDatabaseConfig.enableEncryption) {
        _encryptionCipher = HiveAesCipher(
          encryptionKey ?? Hive.generateSecureKey(),
        );
        debugPrint('HiveService: Encryption enabled');
      }

      // Register all type adapters
      await _registerTypeAdapters();

      // Open all boxes
      await _openBoxes();

      // Check and perform migrations if needed
      await _performMigrations();

      // Perform initial cleanup/compaction if needed
      await _performMaintenance();

      _isInitialized = true;
      debugPrint('HiveService: Initialization complete');
    } catch (e, stackTrace) {
      debugPrint('HiveService: Initialization failed: $e');
      debugPrint('StackTrace: $stackTrace');
      rethrow;
    }
  }

  /// Register all Hive type adapters
  ///
  /// Type adapters must be registered before opening boxes.
  /// Uses auto-generated registrar from hive_registrar.g.dart
  Future<void> _registerTypeAdapters() async {
    debugPrint('HiveService: Registering type adapters...');

    // Register all adapters using the auto-generated extension
    // This automatically registers all model and enum adapters
    Hive.registerAdapters();

    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.loyaltyTier) ? "✓" : "✗"} LoyaltyTier adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.userRole) ? "✓" : "✗"} UserRole adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.orderStatus) ? "✓" : "✗"} OrderStatus adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.orderStatusModel) ? "✓" : "✗"} OrderStatusModel adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.projectType) ? "✓" : "✗"} ProjectType adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.projectStatusModel) ? "✓" : "✗"} ProjectStatusModel adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.projectProgressModel) ? "✓" : "✗"} ProjectProgressModel adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.entryType) ? "✓" : "✗"} EntryType adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.giftStatus) ? "✓" : "✗"} GiftStatus adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.paymentStatus) ? "✓" : "✗"} PaymentStatus adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.paymentMethod) ? "✓" : "✗"} PaymentMethod adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.cachedData) ? "✓" : "✗"} CachedData adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.favoriteModel) ? "✓" : "✗"} FavoriteModel adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.productModel) ? "✓" : "✗"} ProductModel adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.userModel) ? "✓" : "✗"} UserModel adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.cityModel) ? "✓" : "✗"} CityModel adapter',
    );
    debugPrint(
      'HiveService: ${Hive.isAdapterRegistered(HiveTypeIds.wardModel) ? "✓" : "✗"} WardModel adapter',
    );

    debugPrint('HiveService: Type adapters registered successfully');
  }

  /// Open all Hive boxes
  ///
  /// Opens boxes for immediate access. Some boxes use encryption if enabled.
  Future<void> _openBoxes() async {
    debugPrint('HiveService: Opening boxes...');

    try {
      // Open non-encrypted boxes
      await Future.wait([
        // Settings and preferences (non-sensitive)
        Hive.openBox<dynamic>(HiveBoxNames.settingsBox),

        // Cache boxes (non-sensitive)
        Hive.openBox<dynamic>(HiveBoxNames.cacheBox),
        Hive.openBox<dynamic>(HiveBoxNames.syncStateBox),

        // Product and catalog data (non-sensitive)
        Hive.openBox<dynamic>(HiveBoxNames.productBox),
        Hive.openBox<dynamic>(HiveBoxNames.rewardsBox),

        // Notification box (non-sensitive)
        Hive.openBox<dynamic>(HiveBoxNames.notificationBox),

        // Favorite products box (non-sensitive) - caches Product entities from wishlist API
        Hive.openBox<dynamic>(HiveBoxNames.favoriteProductsBox),

        // Location boxes (non-sensitive) - caches cities and wards for address forms
        Hive.openBox<dynamic>(HiveBoxNames.cityBox),
        Hive.openBox<dynamic>(HiveBoxNames.wardBox),

        // Order status box (non-sensitive) - caches order status list from API
        Hive.openBox<dynamic>(HiveBoxNames.orderStatusBox),

        // Project status box (non-sensitive) - caches project status list from API
        Hive.openBox<dynamic>(HiveBoxNames.projectStatusBox),

        // Project progress box (non-sensitive) - caches construction progress stages from API
        Hive.openBox<dynamic>(HiveBoxNames.projectProgressBox),
      ]);

      // Open potentially encrypted boxes (sensitive data)
      final encryptedBoxes = [
        HiveBoxNames.userBox,
        HiveBoxNames.cartBox,
        HiveBoxNames.orderBox,
        HiveBoxNames.projectBox,
        HiveBoxNames.loyaltyBox,
        HiveBoxNames.addressBox,
        HiveBoxNames.offlineQueueBox,
      ];

      for (final boxName in encryptedBoxes) {
        await Hive.openBox<dynamic>(
          boxName,
          encryptionCipher: _encryptionCipher,
        );
      }

      debugPrint('HiveService: All boxes opened successfully');
    } catch (e, stackTrace) {
      debugPrint('HiveService: Error opening boxes: $e');
      debugPrint('StackTrace: $stackTrace');
      rethrow;
    }
  }

  /// Perform database migrations
  ///
  /// Handles schema version upgrades and data migrations.
  Future<void> _performMigrations() async {
    final settingsBox = Hive.box<dynamic>(HiveBoxNames.settingsBox);
    final currentVersion =
        settingsBox.get(HiveKeys.schemaVersion, defaultValue: 0) as int;

    debugPrint('HiveService: Current schema version: $currentVersion');
    debugPrint(
      'HiveService: Target schema version: ${HiveDatabaseConfig.currentSchemaVersion}',
    );

    if (currentVersion < HiveDatabaseConfig.currentSchemaVersion) {
      debugPrint('HiveService: Performing migrations...');

      // Perform migrations sequentially
      for (
        int version = currentVersion + 1;
        version <= HiveDatabaseConfig.currentSchemaVersion;
        version++
      ) {
        await _migrateToVersion(version);
      }

      // Update schema version
      await settingsBox.put(
        HiveKeys.schemaVersion,
        HiveDatabaseConfig.currentSchemaVersion,
      );

      debugPrint('HiveService: Migrations complete');
    } else {
      debugPrint('HiveService: No migrations needed');
    }
  }

  /// Migrate to a specific version
  Future<void> _migrateToVersion(int version) async {
    debugPrint('HiveService: Migrating to version $version');

    switch (version) {
      case 1:
        // Initial version - no migration needed
        break;

      // Future migrations will be added here
      // case 2:
      //   await _migrateV1ToV2();
      //   break;

      default:
        debugPrint('HiveService: Unknown migration version: $version');
    }
  }

  /// Perform database maintenance
  ///
  /// Includes compaction, cleanup of expired cache, etc.
  Future<void> _performMaintenance() async {
    debugPrint('HiveService: Performing maintenance...');

    try {
      // Compact boxes if needed
      await _compactBoxes();

      // Clear expired cache
      await _clearExpiredCache();

      // Limit offline queue size
      await _limitOfflineQueue();

      debugPrint('HiveService: Maintenance complete');
    } catch (e, stackTrace) {
      debugPrint('HiveService: Maintenance error: $e');
      debugPrint('StackTrace: $stackTrace');
      // Don't throw - maintenance errors shouldn't prevent app startup
    }
  }

  /// Compact boxes to reduce file size
  Future<void> _compactBoxes() async {
    for (final boxName in HiveBoxNames.allBoxes) {
      try {
        if (Hive.isBoxOpen(boxName)) {
          final box = Hive.box<dynamic>(boxName);
          await box.compact();
          debugPrint('HiveService: Compacted box: $boxName');
        }
      } catch (e) {
        debugPrint('HiveService: Error compacting box $boxName: $e');
      }
    }
  }

  /// Clear expired cache entries
  Future<void> _clearExpiredCache() async {
    // TODO: Implement cache expiration logic
    // This will be implemented when cache models are created
    // final cacheBox = Hive.box<dynamic>(HiveBoxNames.cacheBox);

    debugPrint('HiveService: Cleared expired cache entries');
  }

  /// Limit offline queue size
  Future<void> _limitOfflineQueue() async {
    final queueBox = Hive.box<dynamic>(HiveBoxNames.offlineQueueBox);

    if (queueBox.length > HiveDatabaseConfig.maxOfflineQueueSize) {
      final itemsToRemove =
          queueBox.length - HiveDatabaseConfig.maxOfflineQueueSize;

      // Remove oldest items
      for (int i = 0; i < itemsToRemove; i++) {
        await queueBox.deleteAt(0);
      }

      debugPrint(
        'HiveService: Removed $itemsToRemove old items from offline queue',
      );
    }
  }

  /// Get a box by name
  ///
  /// Returns an already opened box. Throws if box is not open.
  Box<T> getBox<T>(String boxName) {
    if (!Hive.isBoxOpen(boxName)) {
      throw HiveError('Box $boxName is not open');
    }
    return Hive.box<T>(boxName);
  }

  /// Check if a box is open
  bool isBoxOpen(String boxName) {
    return Hive.isBoxOpen(boxName);
  }

  /// Clear all data from all boxes
  ///
  /// WARNING: This will delete all local data. Use with caution.
  Future<void> clearAllData() async {
    debugPrint('HiveService: Clearing all data...');

    for (final boxName in HiveBoxNames.allBoxes) {
      try {
        if (Hive.isBoxOpen(boxName)) {
          final box = Hive.box<dynamic>(boxName);
          await box.clear();
          debugPrint('HiveService: Cleared box: $boxName');
        }
      } catch (e) {
        debugPrint('HiveService: Error clearing box $boxName: $e');
      }
    }

    debugPrint('HiveService: All data cleared');
  }

  /// Clear user-specific data (logout)
  ///
  /// Clears user data while preserving app settings and cache
  Future<void> clearUserData() async {
    debugPrint('HiveService: Clearing user data...');

    final boxesToClear = [
      HiveBoxNames.userBox,
      HiveBoxNames.cartBox,
      HiveBoxNames.orderBox,
      HiveBoxNames.projectBox,
      HiveBoxNames.loyaltyBox,
      HiveBoxNames.addressBox,
      HiveBoxNames.notificationBox,
    ];

    for (final boxName in boxesToClear) {
      try {
        if (Hive.isBoxOpen(boxName)) {
          final box = Hive.box<dynamic>(boxName);
          await box.clear();
          debugPrint('HiveService: Cleared box: $boxName');
        }
      } catch (e) {
        debugPrint('HiveService: Error clearing box $boxName: $e');
      }
    }

    debugPrint('HiveService: User data cleared');
  }

  /// Close all boxes
  ///
  /// Should be called when app is terminating
  Future<void> close() async {
    debugPrint('HiveService: Closing all boxes...');

    try {
      await Hive.close();
      _isInitialized = false;
      debugPrint('HiveService: All boxes closed');
    } catch (e, stackTrace) {
      debugPrint('HiveService: Error closing boxes: $e');
      debugPrint('StackTrace: $stackTrace');
      rethrow;
    }
  }

  /// Delete all Hive data from disk
  ///
  /// WARNING: This completely removes the database. Use only for testing or reset.
  Future<void> deleteFromDisk() async {
    debugPrint('HiveService: Deleting database from disk...');

    try {
      // Close all boxes first
      await close();

      // Delete Hive directory
      final appDocDir = await getApplicationDocumentsDirectory();
      final hiveDir = Directory('${appDocDir.path}/hive');

      if (await hiveDir.exists()) {
        await hiveDir.delete(recursive: true);
        debugPrint('HiveService: Database deleted from disk');
      }
    } catch (e, stackTrace) {
      debugPrint('HiveService: Error deleting database: $e');
      debugPrint('StackTrace: $stackTrace');
      rethrow;
    }
  }

  /// Generate a secure encryption key
  ///
  /// Returns a 256-bit encryption key for secure box encryption.
  /// Store this key securely (e.g., in flutter_secure_storage).
  static List<int> generateEncryptionKey() {
    return Hive.generateSecureKey();
  }
}
