import 'package:get/get.dart';
import 'package:sigap/src/cores/services/location_service.dart';
import 'package:sigap/src/cores/services/supabase_service.dart';
import 'package:sigap/src/features/personalization/data/models/models/user_metadata_model.dart';
import 'package:sigap/src/utils/exceptions/exceptions.dart';

class PanicButtonRepository extends GetxController {
  static PanicButtonRepository get instance => Get.find();
  
  final _supabase = SupabaseService.instance.client;
  final _locationService = Get.find<LocationService>();
  
  // Rate limiting
  final RxInt panicAttemptsInWindow = 0.obs;
  final RxBool isRateLimited = false.obs;
  final RxInt panicStrikeCount = 0.obs;
  final Rx<DateTime?> lastPanicTime = Rx<DateTime?>(null);
  final RxBool isPanicActive = false.obs;
  
  // Constants
  static const int maxPanicAlertsIn5Minutes = 3;
  static const int strikesTillBan = 3;
  static const int rateLimitWindowMinutes = 5;
  
  @override
  void onInit() {
    super.onInit();
    _loadPanicStrikeCount();
    // Start location monitoring
    _locationService.init();
  }
  
  // Check if user can use panic button
  Future<Map<String, dynamic>> canUsePanicButton() async {
    // Check if user is logged in
    final user = SupabaseService.instance.currentUser;
    if (user == null) {
      return {
        'allowed': false,
        'reason': 'not_authenticated',
        'message': 'You need to sign in to use the panic button.',
      };
    }

    // Check if user is banned
    final isBanned = await _checkIfUserIsBanned();
    if (isBanned) {
      return {
        'allowed': false,
        'reason': 'banned',
        'message':
            'Your account is temporarily banned from using the panic button due to misuse.',
      };
    }
    
    // Check rate limit
    if (_isRateLimited()) {
      return {
        'allowed': false,
        'reason': 'rate_limited',
        'message':
            'You have reached the maximum number of panic alerts. Please try again later.',
        'minutesLeft': _getRateLimitMinutesLeft(),
      };
    }
    
    // Validate location
    final locationValid =
        await _locationService.validateLocationForPanicButton();
    if (locationValid['valid'] != true) {
      return {
        'allowed': false,
        'reason': locationValid['reason'],
        'message': locationValid['message'],
      };
    }
    
    return {'allowed': true, 'position': locationValid['position']};
  }
  
  // Send a panic alert
  Future<Map<String, dynamic>> sendPanicAlert({
    required String description,
    required String categoryId,
    String? evidenceUrl,
  }) async {
    try {
      // Check if panic button can be used
      final canUse = await canUsePanicButton();
      if (canUse['allowed'] != true) {
        return canUse;
      }

      final position = canUse['position'];
      final userId = SupabaseService.instance.currentUser!.id;

      // Set panic as active
      isPanicActive.value = true;

      // Create a new location entry
      final locationId = await _createLocation(
        latitude: position['latitude'],
        longitude: position['longitude'],
      );

      // Create incident log
      final incidentId = await _createIncidentLog(
        userId: userId,
        locationId: locationId,
        description: description,
        categoryId: categoryId,
      );
      
      // Create panic button log
      await _createPanicButtonLog(userId: userId, incidentId: incidentId);
      
      // Add evidence if provided
      if (evidenceUrl != null && evidenceUrl.isNotEmpty) {
        await _addEvidence(incidentId: incidentId, url: evidenceUrl);
      }
      
      // Update rate limiting
      _incrementPanicCount();
      lastPanicTime.value = DateTime.now();
      
      return {
        'success': true,
        'incidentId': incidentId,
        'message': 'Panic alert sent successfully. Help is on the way!',
      };
    } on TExceptions catch (e) {
      isPanicActive.value = false;
      return {'success': false, 'message': e.message};
    } catch (e) {
      isPanicActive.value = false;
      return {
        'success': false,
        'message': 'Failed to send panic alert: ${e.toString()}',
      };
    }
  }
  
  // Cancel active panic alert
  Future<Map<String, dynamic>> cancelPanicAlert(String incidentId) async {
    try {
      // Update the incident status
      await _supabase
          .from('incident_logs')
          .update({
            'status': 'cancelled',
            'updated_at': DateTime.now().toIso8601String(),
          })
          .eq('id', incidentId);

      isPanicActive.value = false;

      return {
        'success': true,
        'message': 'Panic alert cancelled successfully.',
      };
    } catch (e) {
      return {
        'success': false,
        'message': 'Failed to cancel panic alert: ${e.toString()}',
      };
    }
  }

  // HELPER METHODS

  Future<String> _createLocation({
    required double latitude,
    required double longitude,
  }) async {
    try {
      final response = await _supabase.rpc(
        'create_location',
        params: {
        'lat': latitude,
        'lng': longitude,
      });
      
      return response as String;
    } catch (e) {
      throw TExceptions('Failed to create location record');
    }
  }
  
  Future<String> _createIncidentLog({
    required String userId,
    required String locationId,
    required String description,
    required String categoryId,
  }) async {
    try {
      final response =
          await _supabase
              .from('incident_logs')
              .insert({
        'user_id': userId,
        'location_id': locationId,
        'category_id': categoryId,
        'description': description,
        'source': 'panic_button',
                'time': DateTime.now().toIso8601String(),
      }).select('id').single();
      
      return response['id'] as String;
    } catch (e) {
      throw TExceptions('Failed to create incident log');
    }
  }
  
  Future<void> _createPanicButtonLog({
    required String userId,
    required String incidentId,
  }) async {
    try {
      // Get officer ID if user is an officer
      String? officerId;
      final userMetadata = UserMetadataModel.fromJson(
        SupabaseService.instance.currentUser?.userMetadata,
      );

      if (userMetadata.isOfficer && userMetadata.officerData != null) {
        final officer =
            await _supabase
                .from('officers')
                .select('id')
                .eq('nrp', userMetadata.officerData!.nrp)
                .maybeSingle();
          
        if (officer != null) {
          officerId = officer['id'];
        }
      }
      
      await _supabase.from('panic_button_logs').insert({
        'user_id': userId,
        'officer_id': officerId,
        'incident_id': incidentId,
        'timestamp': DateTime.now().toIso8601String(),
      });
    } catch (e) {
      throw TExceptions('Failed to create panic button log');
    }
  }
  
  Future<void> _addEvidence({
    required String incidentId,
    required String url,
  }) async {
    try {
      await _supabase.from('evidence').insert({
        'id': 'E${DateTime.now().millisecondsSinceEpoch}',
        'incident_id': incidentId,
        'type': 'photo',
        'url': url,
        'uploaded_at': DateTime.now().toIso8601String(),
      });
    } catch (e) {
      // Don't fail the whole panic alert if evidence fails
      print('Failed to add evidence: $e');
    }
  }
  
  // Check if user is rate limited
  bool _isRateLimited() {
    if (isRateLimited.value) return true;

    if (lastPanicTime.value != null) {
      final timeSinceLastPanic = DateTime.now().difference(
        lastPanicTime.value!,
      );
      if (timeSinceLastPanic.inMinutes < rateLimitWindowMinutes) {
        if (panicAttemptsInWindow.value >= maxPanicAlertsIn5Minutes) {
          isRateLimited.value = true;
          return true;
        }
      } else {
        // Reset counter if window has passed
        panicAttemptsInWindow.value = 0;
      }
    }
    
    return false;
  }
  
  // Get minutes left in rate limit
  int _getRateLimitMinutesLeft() {
    if (lastPanicTime.value == null) return 0;
    
    final timeSinceLastPanic = DateTime.now().difference(lastPanicTime.value!);
    final minutesLeft = rateLimitWindowMinutes - timeSinceLastPanic.inMinutes;
    
    return minutesLeft > 0 ? minutesLeft : 0;
  }
  
  // Increment panic count for rate limiting
  void _incrementPanicCount() {
    panicAttemptsInWindow.value++;
    
    if (panicAttemptsInWindow.value >= maxPanicAlertsIn5Minutes) {
      isRateLimited.value = true;
      _incrementPanicStrike();
    }
  }
  
  // Increment panic strike count
  Future<void> _incrementPanicStrike() async {
    try {
      final userId = SupabaseService.instance.currentUser?.id;
      if (userId == null) return;
      
      // Check if user is an officer
      final userMetadata = UserMetadataModel.fromJson(
        SupabaseService.instance.currentUser?.userMetadata,
      );
      
      panicStrikeCount.value++;
      
      // Add strike in database based on user type
      if (userMetadata.isOfficer && userMetadata.officerData != null) {
        // Officer
        await _supabase
            .from('officers')
            .update({
              'panic_strike': panicStrikeCount.value,
              'is_banned': panicStrikeCount.value >= strikesTillBan,
              'banned_reason':
                  panicStrikeCount.value >= strikesTillBan
                      ? 'Panic button misuse'
                      : null,
              'banned_until':
                  panicStrikeCount.value >= strikesTillBan
                      ? DateTime.now()
                          .add(const Duration(days: 3))
                          .toIso8601String()
                      : null,
            })
            .eq('nrp', userMetadata.officerData!.nrp);
      } else {
        // Regular user
        await _supabase
            .from('users')
            .update({
              'panic_strike': panicStrikeCount.value,
              'is_banned': panicStrikeCount.value >= strikesTillBan,
              'banned_reason':
                  panicStrikeCount.value >= strikesTillBan
                      ? 'Panic button misuse'
                      : null,
              'banned_until':
                  panicStrikeCount.value >= strikesTillBan
                      ? DateTime.now()
                          .add(const Duration(days: 3))
                          .toIso8601String()
                      : null,
            })
            .eq('id', userId);
      }
    } catch (e) {
      print('Failed to update panic strike: $e');
    }
  }
  
  // Check if user is banned
  Future<bool> _checkIfUserIsBanned() async {
    try {
      final userId = SupabaseService.instance.currentUser?.id;
      if (userId == null) return false;
      
      // Check if user is an officer
      final userMetadata = UserMetadataModel.fromJson(
        SupabaseService.instance.currentUser?.userMetadata,
      );
      
      if (userMetadata.isOfficer && userMetadata.officerData != null) {
        // Officer
        final officer =
            await _supabase
                .from('officers')
                .select('is_banned, banned_until')
                .eq('nrp', userMetadata.officerData!.nrp)
                .maybeSingle();
          
        if (officer == null) return false;
        
        bool isBanned = officer['is_banned'] ?? false;
        if (!isBanned) return false;
        
        // Check if ban has expired
        final bannedUntil = officer['banned_until'];
        if (bannedUntil != null) {
          final banEndDate = DateTime.parse(bannedUntil);
          if (DateTime.now().isAfter(banEndDate)) {
            // Ban expired, remove it
            await _supabase
                .from('officers')
                .update({
                  'is_banned': false,
                  'banned_until': null,
                  'banned_reason': null,
                })
                .eq('nrp', userMetadata.officerData!.nrp);
            return false;
          }
        }
        
        return isBanned;
      } else {
        // Regular user
        final user =
            await _supabase
                .from('users')
                .select('is_banned, banned_until')
                .eq('id', userId)
                .single();
          
        bool isBanned = user['is_banned'] ?? false;
        if (!isBanned) return false;
        
        // Check if ban has expired
        final bannedUntil = user['banned_until'];
        if (bannedUntil != null) {
          final banEndDate = DateTime.parse(bannedUntil);
          if (DateTime.now().isAfter(banEndDate)) {
            // Ban expired, remove it
            await _supabase
                .from('users')
                .update({
                  'is_banned': false,
                  'banned_until': null,
                  'banned_reason': null,
                })
                .eq('id', userId);
            return false;
          }
        }
        
        return isBanned;
      }
    } catch (e) {
      print('Failed to check ban status: $e');
      return false;
    }
  }
  
  // Load panic strike count from database
  Future<void> _loadPanicStrikeCount() async {
    try {
      final userId = SupabaseService.instance.currentUser?.id;
      if (userId == null) return;
      
      // Check if user is an officer
      final userMetadata = UserMetadataModel.fromJson(
        SupabaseService.instance.currentUser?.userMetadata,
      );
      
      if (userMetadata.isOfficer && userMetadata.officerData != null) {
        // Officer
        final officer =
            await _supabase
                .from('officers')
                .select('panic_strike')
                .eq('nrp', userMetadata.officerData!.nrp)
                .maybeSingle();
          
        if (officer != null) {
          panicStrikeCount.value = officer['panic_strike'] ?? 0;
        }
      } else {
        // Regular user
        final user =
            await _supabase
                .from('users')
                .select('panic_strike')
                .eq('id', userId)
                .single();
          
        panicStrikeCount.value = user['panic_strike'] ?? 0;
      }
    } catch (e) {
      print('Failed to load panic strike count: $e');
    }
  }
}
