import 'package:supabase_flutter/supabase_flutter.dart' as supabase; import 'package:dio/dio.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../models/user_model.dart'; import '../core/api_config.dart'; import 'auth_service.dart'; /// Implementasi [AuthService] menggunakan layanan **Supabase Auth** untuk autentikasi user /// dan **SharedPreferences** untuk menyimpan cache lokal data `device_id` IoT. class SupabaseAuthService implements AuthService { final supabase.SupabaseClient _client = supabase.Supabase.instance.client; final Dio _dio = Dio(); @override Future login(String email, String password) async { try { // 1. Melakukan sign in ke Supabase Auth final response = await _client.auth.signInWithPassword( email: email, password: password, ); final user = response.user; if (user == null) { throw Exception('Login gagal: User tidak ditemukan'); } // 2. Mengambil informasi device_id yang terhubung ke pengguna dari database backend String? deviceId; try { final deviceRes = await _dio.get('${ApiConfig.baseUrl}/device/my-device/${user.id}'); if (deviceRes.statusCode == 200) { deviceId = deviceRes.data['device_id']; } } catch (e) { print('Gagal mengambil info device: $e'); // Tidak melempar error agar login tetap sukses meskipun user belum menghubungkan device } // 3. Menyimpan device_id ke cache lokal (SharedPreferences) agar auto-login selanjutnya lebih cepat dan offline-safe if (deviceId != null) { final prefs = await SharedPreferences.getInstance(); await prefs.setString('cached_device_id', deviceId); } return UserModel( id: user.id, name: user.userMetadata?['name'] ?? user.email?.split('@').first ?? 'User', email: user.email ?? email, token: response.session?.accessToken ?? '', deviceId: deviceId, ); } on supabase.AuthException catch (e) { // Pemetaan pesan error dari Supabase ke bahasa Indonesia yang ramah pengguna if (e.message.contains('Invalid login credentials')) { throw Exception('Email atau password salah.'); } else if (e.message.contains('Email not confirmed')) { throw Exception('Email belum diverifikasi.'); } throw Exception(e.message); } catch (e) { throw Exception('Terjadi kesalahan koneksi atau server.'); } } @override Future register(String name, String email, String password) async { try { // Melakukan pendaftaran akun ke Supabase Auth beserta meta data nama lengkap final response = await _client.auth.signUp( email: email, password: password, data: {'name': name}, ); final user = response.user; if (user == null) { throw Exception('Registrasi gagal'); } return UserModel( id: user.id, name: name, email: email, token: response.session?.accessToken ?? '', deviceId: null, ); } on supabase.AuthException catch (e) { if (e.message.contains('already registered')) { throw Exception('Email sudah terdaftar.'); } else if (e.message.contains('weak')) { throw Exception('Password terlalu lemah.'); } throw Exception(e.message); } catch (e) { throw Exception('Terjadi kesalahan koneksi atau server.'); } } @override Future claimDevice(String userId, String claimCode) async { try { // Mengirim request klaim alat ke REST API backend final claimRes = await _dio.post('${ApiConfig.baseUrl}/device/claim', data: { 'claim_code': claimCode.trim(), 'user_id': userId, }); if (claimRes.statusCode == 200) { final deviceId = claimRes.data['device']['device_id'] as String; // Menyimpan ID perangkat yang berhasil diklaim ke SharedPreferences final prefs = await SharedPreferences.getInstance(); await prefs.setString('cached_device_id', deviceId); return deviceId; } else { throw Exception('Gagal klaim perangkat: ${claimRes.statusCode}'); } } catch (e) { String errorMsg = 'Gagal klaim perangkat.'; if (e is DioException && e.response?.data != null) { errorMsg = e.response?.data['message'] ?? errorMsg; } throw Exception(errorMsg); } } @override Future logout() async { // 1. Membersihkan sesi Supabase Auth di server await _client.auth.signOut(); // 2. Menghapus cache lokal device_id agar bersih saat login pengguna lain final prefs = await SharedPreferences.getInstance(); await prefs.remove('cached_device_id'); } /// Memulihkan sesi login yang tersimpan di internal storage untuk Auto-Login. /// Mengambil data user dari sesi aktif Supabase, dan device_id dari SharedPreferences. @override Future checkSession() async { final session = _client.auth.currentSession; if (session != null) { final user = session.user; // Mengambil device_id dari cache lokal (cepat & aman saat offline) String? deviceId; try { final prefs = await SharedPreferences.getInstance(); deviceId = prefs.getString('cached_device_id'); } catch (e) { print('Gagal membaca cache device_id: $e'); } return UserModel( id: user.id, name: user.userMetadata?['name'] ?? user.email?.split('@').first ?? 'User', email: user.email ?? '', token: session.accessToken, deviceId: deviceId, ); } return null; } }