import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:logger/Logger.dart';
import 'package:sigap/src/cores/services/supabase_service.dart';
import 'package:sigap/src/features/personalization/data/models/models/roles_model.dart';
import 'package:sigap/src/features/personalization/data/models/models/user_metadata_model.dart';
import 'package:sigap/src/features/personalization/data/models/models/users_model.dart';
import 'package:sigap/src/utils/exceptions/exceptions.dart';
import 'package:sigap/src/utils/exceptions/format_exceptions.dart';
import 'package:sigap/src/utils/exceptions/platform_exceptions.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

class UserRepository extends GetxController {
  static UserRepository get instance => Get.find();

  final _supabase = SupabaseService.instance.client;
  final _logger = Get.find<Logger>();

  // Get current user ID
  String? get currentUserId => SupabaseService.instance.currentUserId;

  // Check if user is aPuthenticated
  bool get isAuthenticated => currentUserId != null;

  // Check if user is an officer
  Future<bool> isCurrentUserOfficer() async {
    try {
      if (!isAuthenticated) return false;

      final user = _supabase.auth.currentUser;
      if (user == null) return false;

      // Check if officer flag is present in user metadata
      final metadata = user.userMetadata;
      if (metadata != null && metadata.containsKey('is_officer')) {
        return metadata['is_officer'] == true;
      }

      // If no flag in metadata, check role information
      final userData =
          await _supabase
              .from('users')
              .select('role:roles(name)')
              .eq('id', currentUserId!)
              .single();

      final roleName =
          (userData['role'] as Map<String, dynamic>)['name'] as String;
      return roleName.toLowerCase() == 'officer';
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in isCurrentUserOfficer: ${error.message}');
      return false; // Default to not an officer
    } catch (e) {
      _logger.e('Exception in isCurrentUserOfficer: $e');
      return false; // Default to not an officer
    }
  }

  // Get current user data
  Future<UserModel> getCurrentUserData() async {
    try {
      if (!isAuthenticated) {
        throw 'User not authenticated';
      }

      final userData =
          await _supabase
              .from('users')
              .select('*, profiles(*), role:roles(*)')
              .eq('id', currentUserId!)
              .single();

      return UserModel.fromJson(userData);
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in getCurrentUserData: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } on FormatException catch (_) {
      throw const TFormatException();
    } on PlatformException catch (e) {
      throw TPlatformException(e.code).message;
    } catch (e) {
      _logger.e('Exception in getCurrentUserData: $e');
      throw 'Failed to fetch user data: ${e.toString()}';
    }
  }

  Future<UserModel?> updateUser(UserModel user) async {
    try {
      if (!isAuthenticated) {
        throw 'User not authenticated';
      }

      final userData = user.toJson();

      // Update user data in database
      await _supabase.from('users').update(userData).eq('id', user.id);

      // Return updated user model
      return UserModel.fromJson(userData);
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in updateUser: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } on FormatException catch (_) {
      throw const TFormatException();
    } on PlatformException catch (e) {
      throw TPlatformException(e.code).message;
    } catch (e) {
      _logger.e('Exception in updateUser: $e');
      throw 'Failed to update user data: ${e.toString()}';
    }
  }

  // Update user metadata
  Future<void> updateUserMetadata(Map<String, dynamic> metadata) async {
    try {
      if (!isAuthenticated) {
        throw 'User not authenticated';
      }
      Logger().i('Updating user metadata: $metadata');

      final updatedMetadata = await _supabase.auth.updateUser(
        UserAttributes(data: metadata),
      );

      Logger().i('User metadata updated successfully: $updatedMetadata');
    } on AuthException catch (e) {
      _logger.e('AuthException in updateUserMetadata: ${e.message}');
      throw TExceptions(e.message);
    } catch (e) {
      _logger.e('Exception in updateUserMetadata: $e');
      throw 'Failed to update user metadata: ${e.toString()}';
    }
  }

  // update profile status in user metadata
  Future<void> updateProfileStatus(String status) async {
    try {
      if (!isAuthenticated) {
        throw 'User not authenticated';
      }

      final metadata = {
        'profile_status': status,
      };

      await updateUserMetadata(metadata);
    } on AuthException catch (e) {
      _logger.e('AuthException in updateProfileStatus: ${e.message}');
      throw TExceptions(e.message);
    } catch (e) {
      _logger.e('Exception in updateProfileStatus: $e');
      throw 'Failed to update profile status: ${e.toString()}';
    }
  }

  // Update user email
  Future<void> updateUserEmail(String newEmail) async {
    try {
      if (!isAuthenticated) {
        throw 'User not authenticated';
      }

      await _supabase.auth.updateUser(UserAttributes(email: newEmail));

      // Also update in users table
      await _supabase
          .from('users')
          .update({'email': newEmail})
          .eq('id', currentUserId!);
    } on AuthException catch (e) {
      _logger.e('AuthException in updateUserEmail: ${e.message}');
      throw TExceptions(e.message);
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in updateUserEmail: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in updateUserEmail: $e');
      throw 'Failed to update user email: ${e.toString()}';
    }
  }

  // Update user phone
  Future<void> updateUserPhone(String newPhone) async {
    try {
      if (!isAuthenticated) {
        throw 'User not authenticated';
      }

      await _supabase.auth.updateUser(UserAttributes(phone: newPhone));

      // Also update in users table
      await _supabase
          .from('users')
          .update({'phone': newPhone})
          .eq('id', currentUserId!);
    } on AuthException catch (e) {
      _logger.e('AuthException in updateUserPhone: ${e.message}');
      throw TExceptions(e.message);
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in updateUserPhone: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in updateUserPhone: $e');
      throw 'Failed to update user phone: ${e.toString()}';
    }
  }

  // Update user password
  Future<void> updateUserPassword(String newPassword) async {
    try {
      if (!isAuthenticated) {
        throw 'User not authenticated';
      }

      await _supabase.auth.updateUser(UserAttributes(password: newPassword));
    } on AuthException catch (e) {
      _logger.e('AuthException in updateUserPassword: ${e.message}');
      throw TExceptions(e.message);
    } catch (e) {
      _logger.e('Exception in updateUserPassword: $e');
      throw 'Failed to update user password: ${e.toString()}';
    }
  }

  // Update user role
  Future<void> updateUserRole(String roleId) async {
    try {
      if (!isAuthenticated) {
        throw 'User not authenticated';
      }

      // Update in users table
      await _supabase
          .from('users')
          .update({'roles_id': roleId})
          .eq('id', currentUserId!);

      // Get role details
      final roleData =
          await _supabase.from('roles').select().eq('id', roleId).single();

      final role = RoleModel.fromJson(roleData);

      final metadata =
          UserMetadataModel(
            isOfficer: role.isOfficer,
            roleId: role.id,
          ).toAuthMetadataJson();
      await updateUserMetadata(metadata);
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in updateUserRole: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in updateUserRole: $e');
      throw 'Failed to update user role: ${e.toString()}';
    }
  }

  // Get users by role ID
  Future<List<UserModel>> getUsersByRoleId(String roleId) async {
    try {
      final users = await _supabase
          .from('users')
          .select('*, profiles(*), role:roles(*)')
          .eq('roles_id', roleId);

      return (users as List).map((user) => UserModel.fromJson(user)).toList();
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in getUsersByRoleId: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in getUsersByRoleId: $e');
      throw 'Failed to fetch users by role: ${e.toString()}';
    }
  }

  // Get user by ID
  Future<UserModel> getUserById(String userId) async {
    try {
      final userData =
          await _supabase
              .from('users')
              .select('*, profiles(*), role:roles(*)')
              .eq('id', userId)
              .single();

      return UserModel.fromJson(userData);
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in getUserById: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in getUserById: $e');
      throw 'Failed to fetch user data: ${e.toString()}';
    }
  }

  // Get user by email
  Future<UserModel?> getUserByEmail(String email) async {
    try {
      final userData =
          await _supabase
              .from('users')
              .select('*, profiles(*), role:roles(*)')
              .eq('email', email)
              .single();

      return UserModel.fromJson(userData);
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in getUserByEmail: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in getUserByEmail: $e');
      return null; // Return null if user not found
    }
  }

  // Get user by nik
  Future<UserModel?> getUserByNik(String nik) async {
    try {
      final userData =
          await _supabase
              .from('users')
              .select('*, profiles(*), role:roles(*)')
              .eq('profiles.nik', nik)
              .maybeSingle();

      if (userData == null) return null;

      return UserModel.fromJson(userData);
    } catch (e) {
      _logger.e('Exception in getUserByNik: $e');
      return null;
    }
  }

  Future<bool> isNikExists(String nik) async {
    try {
      final result = await _supabase
          .from('profiles')
          .select('id')
          .eq('nik', nik)
          .limit(1);

      return result.isNotEmpty;
    } catch (e) {
      _logger.e('Exception in isNikExists: $e');
      return false; // Assume NIK doesn't exist if error
    }
  }

  // Chekch if email is already in use
  Future<bool> isEmailInUse(String email) async {
    try {
      final userData =
          await _supabase
              .from('users')
              .select('id')
              .eq('email', email)
              .maybeSingle(); // ⬅️ Ganti single() dengan maybeSingle()

      return userData != null;
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in isEmailInUse: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in isEmailInUse: $e');
      return false; // Default: anggap belum digunakan jika gagal
    }
  }

  // Search users by name/username/email
  Future<List<UserModel>> searchUsers(String query, {int limit = 20}) async {
    try {
      final users = await _supabase
          .from('users')
          .select('*, profiles(*), role:roles(*)')
          .or(
            'email.ilike.%$query%, profiles.first_name.ilike.%$query%, profiles.last_name.ilike.%$query%, profiles.username.ilike.%$query%',
          )
          .limit(limit);

      return (users as List).map((user) => UserModel.fromJson(user)).toList();
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in searchUsers: ${error.message}');
      throw TExceptions.fromCode(error.code ?? 'unknown-error');
    } catch (e) {
      _logger.e('Exception in searchUsers: $e');
      throw 'Failed to search users: ${e.toString()}';
    }
  }

  // Check if user is banned
  Future<bool> isUserBanned({String? userId}) async {
    try {
      final id = userId ?? currentUserId;
      if (id == null) {
        throw 'User ID is required';
      }

      final userData =
          await _supabase
              .from('users')
              .select('is_banned, banned_until')
              .eq('id', id)
              .single();

      // Check if explicitly banned
      bool isBanned = userData['is_banned'] == true;

      // Check if temporary ban is still active
      if (!isBanned && userData['banned_until'] != null) {
        final bannedUntil = DateTime.parse(userData['banned_until']);
        if (bannedUntil.isAfter(DateTime.now())) {
          isBanned = true;
        }
      }

      return isBanned;
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in isUserBanned: ${error.message}');
      return false; // Default to not banned
    } catch (e) {
      _logger.e('Exception in isUserBanned: $e');
      return false; // Default to not banned
    }
  }

  // Update user profile with registration data
  Future<bool> updateUserProfile(Map<String, dynamic> data) async {
    try {
      if (!isAuthenticated) {
        throw 'User not authenticated';
      }

      final userId = data['user_id'] ?? currentUserId;
      if (userId == null) {
        throw 'User ID is required';
      }

      // Update user metadata
      final authData = {
        'email': data['email'],
        'phone': data['phone'],
        'is_officer': data['is_officer'] ?? false,
        'role_id': data['role_id'],
        'profile_status': data['profile_status'] ?? 'pending_approval',
      };

      // Add ID card and verification data to user metadata
      if (data['id_card'] != null) {
        authData['id_card'] = data['id_card'];
      }

      if (data['face_verification'] != null) {
        authData['face_verification'] = data['face_verification'];
      }

      // Add officer-specific data if user is an officer
      if (data['is_officer'] == true && data['officer'] != null) {
        authData['officer_data'] = data['officer'];
      }

      // Update user metadata in auth
      await _supabase.auth.updateUser(UserAttributes(data: authData));

      // Update the database tables based on user type
      if (data['is_officer'] == true) {
        // Handle officer data
        if (data['officer'] != null) {
          final officerData = Map<String, dynamic>.from(data['officer']);

          // Check if officer exists
          final existingOfficer =
              await _supabase
                  .from('officers')
                  .select('id')
                  .eq('id', userId)
                  .maybeSingle();

          if (existingOfficer != null) {
            // Update existing officer
            await _supabase
                .from('officers')
                .update(officerData)
                .eq('id', userId);
          } else {
            // Create new officer
            await _supabase.from('officers').insert(officerData);
          }
        }
      } else {
        // Handle regular user data
        if (data['profile'] != null) {
          final profileData = Map<String, dynamic>.from(data['profile']);

          // Check if profile exists
          final existingProfile =
              await _supabase
                  .from('profiles')
                  .select('id')
                  .eq('user_id', userId)
                  .maybeSingle();

          if (existingProfile != null) {
            // Update existing profile
            await _supabase
                .from('profiles')
                .update(profileData)
                .eq('user_id', userId);
          } else {
            // Create new profile
            await _supabase.from('profiles').insert(profileData);
          }
        }
      }

      // Update users table with common data
      await _supabase
          .from('users')
          .update({
            'phone': data['phone'],
            'roles_id': data['role_id'],
            'updated_at': DateTime.now().toIso8601String(),
          })
          .eq('id', userId);

      _logger.d('User profile updated successfully');
      return true;
    } on PostgrestException catch (error) {
      _logger.e('PostgrestException in updateUserProfile: ${error.message}');
      return false;
    } on AuthException catch (e) {
      _logger.e('AuthException in updateUserProfile: ${e.message}');
      return false;
    } catch (e) {
      _logger.e('Exception in updateUserProfile: $e');
      return false;
    }
  }
}
