import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/evaluation_history.dart';

/// Service for managing local storage using SharedPreferences
///
/// Handles storage of evaluation history, cache management,
/// and other persistent data (excluding secure credentials)
///
/// Usage example:
/// ```dart
/// final storageService = StorageService();
///
/// // Save evaluation history
/// await storageService.saveEvaluationHistory(history);
///
/// // Load evaluation history
/// final histories = await storageService.loadEvaluationHistory();
///
/// // Clear all data
/// await storageService.clearAllData();
/// ```
class StorageService {
  static const String _evaluationHistoryKey = 'evaluation_history';
  static const String _lastSyncTimeKey = 'last_sync_time';
  static const String _cacheVersionKey = 'cache_version';
  static const int _maxHistoryItems = 100; // Maximum history items to keep

  /// Save evaluation history to local storage
  ///
  /// Appends new history item to existing list
  /// Automatically trims list if it exceeds max items
  ///
  /// [history] - The evaluation history to save
  Future<void> saveEvaluationHistory(EvaluationHistory history) async {
    try {
      final prefs = await SharedPreferences.getInstance();

      // Load existing history
      final histories = await loadEvaluationHistory();

      // Add new history at the beginning
      histories.insert(0, history);

      // Trim if exceeds max items
      if (histories.length > _maxHistoryItems) {
        histories.removeRange(_maxHistoryItems, histories.length);
      }

      // Convert to JSON and save
      final jsonList = histories.map((h) => h.toJson()).toList();
      final jsonString = jsonEncode(jsonList);
      await prefs.setString(_evaluationHistoryKey, jsonString);
    } catch (e) {
      throw StorageException('Failed to save evaluation history: $e');
    }
  }

  /// Save multiple evaluation histories at once
  ///
  /// Useful for batch operations
  ///
  /// [histories] - List of evaluation histories to save
  Future<void> saveEvaluationHistories(
    List<EvaluationHistory> histories,
  ) async {
    try {
      final prefs = await SharedPreferences.getInstance();

      // Load existing history
      final existingHistories = await loadEvaluationHistory();

      // Merge new histories at the beginning
      final mergedHistories = [...histories, ...existingHistories];

      // Trim if exceeds max items
      final trimmedHistories = mergedHistories.length > _maxHistoryItems
          ? mergedHistories.sublist(0, _maxHistoryItems)
          : mergedHistories;

      // Convert to JSON and save
      final jsonList = trimmedHistories.map((h) => h.toJson()).toList();
      final jsonString = jsonEncode(jsonList);
      await prefs.setString(_evaluationHistoryKey, jsonString);
    } catch (e) {
      throw StorageException('Failed to save evaluation histories: $e');
    }
  }

  /// Load evaluation history from local storage
  ///
  /// Returns list of evaluation histories sorted by timestamp (newest first)
  /// Returns empty list if no history exists
  Future<List<EvaluationHistory>> loadEvaluationHistory() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      final jsonString = prefs.getString(_evaluationHistoryKey);

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

      final jsonList = jsonDecode(jsonString) as List<dynamic>;
      final histories = jsonList
          .map(
            (json) => EvaluationHistory.fromJson(json as Map<String, dynamic>),
          )
          .toList();

      // Sort by timestamp (newest first)
      histories.sort((a, b) => b.timestamp.compareTo(a.timestamp));

      return histories;
    } catch (e) {
      throw StorageException('Failed to load evaluation history: $e');
    }
  }

  /// Get evaluation history for a specific course
  ///
  /// [courseId] - The course ID to filter by
  /// Returns list of histories for the specified course
  Future<List<EvaluationHistory>> getHistoryByCourse(String courseId) async {
    try {
      final allHistories = await loadEvaluationHistory();
      return allHistories.where((h) => h.course.id == courseId).toList();
    } catch (e) {
      throw StorageException('Failed to get history by course: $e');
    }
  }

  /// Get successful evaluation count
  ///
  /// Returns the total number of successful evaluations
  Future<int> getSuccessfulEvaluationCount() async {
    try {
      final histories = await loadEvaluationHistory();
      return histories.where((h) => h.success).length;
    } catch (e) {
      throw StorageException('Failed to get successful evaluation count: $e');
    }
  }

  /// Get failed evaluation count
  ///
  /// Returns the total number of failed evaluations
  Future<int> getFailedEvaluationCount() async {
    try {
      final histories = await loadEvaluationHistory();
      return histories.where((h) => !h.success).length;
    } catch (e) {
      throw StorageException('Failed to get failed evaluation count: $e');
    }
  }

  /// Clear evaluation history
  ///
  /// Removes all stored evaluation history
  Future<void> clearEvaluationHistory() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.remove(_evaluationHistoryKey);
    } catch (e) {
      throw StorageException('Failed to clear evaluation history: $e');
    }
  }

  /// Save last sync time
  ///
  /// Records when the last data synchronization occurred
  ///
  /// [time] - The timestamp to save
  Future<void> saveLastSyncTime(DateTime time) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString(_lastSyncTimeKey, time.toIso8601String());
    } catch (e) {
      throw StorageException('Failed to save last sync time: $e');
    }
  }

  /// Load last sync time
  ///
  /// Returns the timestamp of the last synchronization
  /// Returns null if no sync has occurred
  Future<DateTime?> loadLastSyncTime() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      final timeString = prefs.getString(_lastSyncTimeKey);

      if (timeString == null) {
        return null;
      }

      return DateTime.parse(timeString);
    } catch (e) {
      throw StorageException('Failed to load last sync time: $e');
    }
  }

  /// Get cache version
  ///
  /// Returns the current cache version number
  /// Used for cache invalidation when data structure changes
  Future<int> getCacheVersion() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      return prefs.getInt(_cacheVersionKey) ?? 1;
    } catch (e) {
      throw StorageException('Failed to get cache version: $e');
    }
  }

  /// Set cache version
  ///
  /// Updates the cache version number
  ///
  /// [version] - The new version number
  Future<void> setCacheVersion(int version) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setInt(_cacheVersionKey, version);
    } catch (e) {
      throw StorageException('Failed to set cache version: $e');
    }
  }

  /// Clear cache if version mismatch
  ///
  /// Compares current cache version with expected version
  /// Clears cache if they don't match
  ///
  /// [expectedVersion] - The expected cache version
  /// Returns true if cache was cleared, false otherwise
  Future<bool> clearCacheIfVersionMismatch(int expectedVersion) async {
    try {
      final currentVersion = await getCacheVersion();

      if (currentVersion != expectedVersion) {
        await clearEvaluationHistory();
        await setCacheVersion(expectedVersion);
        return true;
      }

      return false;
    } catch (e) {
      throw StorageException('Failed to check cache version: $e');
    }
  }

  /// Clear all local data
  ///
  /// Removes all data stored by this service
  /// Does NOT clear secure storage (credentials)
  /// Does NOT clear theme preferences
  Future<void> clearAllData() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.remove(_evaluationHistoryKey);
      await prefs.remove(_lastSyncTimeKey);
      // Keep cache version to maintain compatibility
    } catch (e) {
      throw StorageException('Failed to clear all data: $e');
    }
  }

  /// Get storage statistics
  ///
  /// Returns information about stored data
  Future<StorageStats> getStorageStats() async {
    try {
      final histories = await loadEvaluationHistory();
      final lastSync = await loadLastSyncTime();
      final cacheVersion = await getCacheVersion();

      return StorageStats(
        totalHistoryItems: histories.length,
        successfulEvaluations: histories.where((h) => h.success).length,
        failedEvaluations: histories.where((h) => !h.success).length,
        lastSyncTime: lastSync,
        cacheVersion: cacheVersion,
      );
    } catch (e) {
      throw StorageException('Failed to get storage stats: $e');
    }
  }
}

/// Storage statistics data class
class StorageStats {
  final int totalHistoryItems;
  final int successfulEvaluations;
  final int failedEvaluations;
  final DateTime? lastSyncTime;
  final int cacheVersion;

  StorageStats({
    required this.totalHistoryItems,
    required this.successfulEvaluations,
    required this.failedEvaluations,
    this.lastSyncTime,
    required this.cacheVersion,
  });

  @override
  String toString() {
    return 'StorageStats('
        'total: $totalHistoryItems, '
        'success: $successfulEvaluations, '
        'failed: $failedEvaluations, '
        'lastSync: $lastSyncTime, '
        'cacheVersion: $cacheVersion'
        ')';
  }
}

/// Exception thrown when storage operations fail
class StorageException implements Exception {
  final String message;

  StorageException(this.message);

  @override
  String toString() => 'StorageException: $message';
}
