import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:flutter/material.dart'; import 'dart:async'; import 'package:get_it/get_it.dart'; import 'package:tugas_akhir_supabase/services/user_presence_service.dart'; import 'package:tugas_akhir_supabase/services/session_manager.dart'; class AuthServices { final SupabaseClient _supabase = Supabase.instance.client; // Sign in with email and password Future signInWithEmailPassword( String email, String password, ) async { try { // Tambahkan timeout untuk mencegah permintaan menggantung final response = await _supabase.auth .signInWithPassword(email: email, password: password) .timeout( const Duration(seconds: 15), onTimeout: () { throw TimeoutException( 'Koneksi timeout. Silakan coba lagi nanti.', ); }, ); // Register and initialize UserPresenceService after successful login await _registerUserPresenceService(); // Update session manager SessionManager.setUserLoggedIn(true); return response; } catch (e) { debugPrint('Error saat login: $e'); // Re-throw exception untuk ditangani di UI rethrow; } } // Register and initialize UserPresenceService Future _registerUserPresenceService() async { try { final currentUser = _supabase.auth.currentUser; if (currentUser == null) return; // Register the service if not already registered if (!GetIt.instance.isRegistered()) { debugPrint('Registering UserPresenceService after login'); GetIt.instance.registerSingleton( UserPresenceService(), ); await GetIt.instance().initialize(); } else { // If already registered, just initialize it debugPrint('UserPresenceService already registered, initializing'); await GetIt.instance().initialize(); } } catch (e) { debugPrint('Error registering UserPresenceService: $e'); } } // Sign Up with email and password Future signUpWithEmailPassword( String email, String password, ) async { final response = await _supabase.auth.signUp( email: email, password: password, ); return response; } // Sign Out Future signOut() async { try { // Dispose UserPresenceService if registered if (GetIt.instance.isRegistered()) { debugPrint('Disposing UserPresenceService during sign out'); GetIt.instance().dispose(); // Unregister the service if (GetIt.instance.isRegistered()) { GetIt.instance.unregister(); } } // Update session manager SessionManager.setUserLoggedIn(false); // Sign out from Supabase await _supabase.auth.signOut(); } catch (e) { debugPrint('Error during sign out: $e'); // Still try to sign out from Supabase even if there was an error await _supabase.auth.signOut(); } } // Get current user ID String? getCurrentUserId() { final session = _supabase.auth.currentSession; final user = session?.user; return user?.id; } // Request password reset (sends email with OTP) Future forgotPassword(String email, {String? redirectUrl}) async { try { await _supabase.auth.resetPasswordForEmail( email, redirectTo: redirectUrl, ); } catch (e) { debugPrint('Error sending password reset email: $e'); throw Exception('Gagal mengirim email reset password: $e'); } } // Verify OTP for password reset or signup Future verifyOTP({ required String email, required String token, required OtpType type, }) async { try { final response = await _supabase.auth.verifyOTP( email: email, token: token, type: type, ); return response; } catch (e) { debugPrint('Error verifying OTP: $e'); throw Exception('Verifikasi OTP gagal: $e'); } } // Reset password (after OTP verification) Future resetPassword(String newPassword) async { try { await _supabase.auth.updateUser(UserAttributes(password: newPassword)); } catch (e) { debugPrint('Error resetting password: $e'); throw Exception('Gagal mengubah password: $e'); } } // Check if user is logged in (without checking timeout) bool isUserLoggedIn() { return _supabase.auth.currentSession != null; } // Get current authenticated user User? getCurrentUser() { return _supabase.auth.currentUser; } // Get current user email String? getCurrentUserEmail() { final session = _supabase.auth.currentSession; final user = session?.user; return user?.email; } // Get user role Future getUserRole() async { final userId = getCurrentUserId(); if (userId == null) return null; try { debugPrint('Mencoba mendapatkan role untuk user ID: $userId'); // Coba query langsung ke tabel final response = await _supabase .from('user_roles') .select('role') .eq('user_id', userId) .maybeSingle(); debugPrint('Response dari query user_roles: $response'); if (response != null) { final role = response['role'] as String?; debugPrint('Role ditemukan: $role'); return role; } else { debugPrint('Tidak ada role yang ditemukan untuk user ID: $userId'); return null; } } catch (e) { debugPrint('Error getting user role: $e'); return null; } } // Check if user is admin Future isAdmin() async { try { final userId = getCurrentUserId(); debugPrint('Checking admin status for user ID: $userId'); if (userId == null) { debugPrint('User ID is null, not an admin'); return false; } // Gunakan fungsi is_admin_no_rls untuk menghindari infinite recursion final response = await _supabase.rpc( 'is_admin_no_rls', params: {'input_user_id': userId}, ); debugPrint('Admin check direct response: $response'); // Response dari RPC akan berupa boolean final isAdmin = response == true; debugPrint('Is admin: $isAdmin'); return isAdmin; } catch (e) { debugPrint('Error checking admin status: $e'); // Fallback: cek langsung dari tabel tanpa RLS try { final userId = getCurrentUserId(); if (userId == null) return false; // Query sederhana tanpa menggunakan RPC final response = await _supabase .from('user_roles') .select('role') .eq('user_id', userId) .eq('role', 'admin') .maybeSingle(); final isAdmin = response != null; debugPrint('Fallback admin check result: $isAdmin'); return isAdmin; } catch (fallbackError) { debugPrint('Fallback error: $fallbackError'); return false; } } } // Force refresh user session Future refreshSession() async { try { final session = _supabase.auth.currentSession; if (session != null) { await _supabase.auth.refreshSession(); debugPrint('Session refreshed successfully'); } } catch (e) { debugPrint('Error refreshing session: $e'); } } // Get all admins Future>> getAllAdmins() async { try { final response = await _supabase .from('user_roles') .select('user_id') .eq('role', 'admin'); return List>.from(response); } catch (e) { debugPrint('Error getting all admins: $e'); return []; } } // Count admins Future countAdmins() async { try { final admins = await getAllAdmins(); return admins.length; } catch (e) { debugPrint('Error counting admins: $e'); return 0; } } Future signInWithEmailAndPassword( String email, String password, ) async { try { debugPrint('Attempting to sign in with email: $email'); final response = await _supabase.auth.signInWithPassword( email: email, password: password, ); debugPrint('Sign in response: ${response.session != null}'); return response; } on AuthException catch (e) { debugPrint('AuthException during sign in: ${e.message}'); rethrow; } on PostgrestException catch (e) { debugPrint('PostgrestException during sign in: ${e.message}'); // Check for infinite recursion error if (e.code == '42P17' && e.message.contains('infinite recursion')) { throw Exception( 'Terjadi masalah pada kebijakan database. Silakan hubungi admin untuk memperbaiki kebijakan users atau gunakan tombol "Perbaiki Database" jika Anda adalah admin.', ); } rethrow; } catch (e) { debugPrint('Unknown exception during sign in: $e'); // Check for infinite recursion error in generic exception if (e.toString().contains('infinite recursion') && e.toString().contains('42P17')) { throw Exception( 'Terjadi masalah pada kebijakan database. Silakan hubungi admin untuk memperbaiki kebijakan users atau gunakan tombol "Perbaiki Database" jika Anda adalah admin.', ); } rethrow; } } }