import 'package:posyandu/services/api_service.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:intl/intl.dart'; import 'package:posyandu/services/auth_service.dart'; import 'dart:convert'; import 'dart:math'; class AnakService { final ApiService _apiService = ApiService(); // Singleton pattern static final AnakService _instance = AnakService._internal(); factory AnakService() { return _instance; } AnakService._internal(); /// Mendapatkan daftar anak milik pengguna yang sedang login Future> getAnakList({bool useLocalOnly = false, bool skipLocal = false}) async { try { // Dapatkan user_id dari SharedPreferences final prefs = await SharedPreferences.getInstance(); final userId = prefs.getInt('user_id'); final nik = prefs.getString('nik'); final token = prefs.getString('token'); print('Mencoba mendapatkan data anak: user_id=$userId, nik=$nik, token=${token != null ? "ada" : "tidak ada"}'); if (userId == null) { throw Exception('User ID tidak ditemukan. Silakan login terlebih dahulu.'); } List anakList = []; bool success = false; // Dapatkan data lokal hanya jika tidak skip List localAnakList = skipLocal ? [] : await getLocalAnakList(); if (!skipLocal) { print('Data anak lokal: ${localAnakList.length} item'); // Jika ada data lokal, tampilkan info debug if (localAnakList.isNotEmpty) { print('Contoh data lokal pertama: ${localAnakList[0]['nama_anak']} (${localAnakList[0]['tanggal_lahir']})'); } } // Jika diminta hanya menggunakan data lokal if (useLocalOnly) { print('Hanya menggunakan data lokal sesuai permintaan'); return localAnakList; } // Jika tidak ada koneksi atau token, gunakan data lokal saja if (token == null) { print('Tidak ada token, menggunakan data lokal saja'); return localAnakList; } // Opsi 1: Coba endpoint standard try { print('Mencoba endpoint standard: anak'); final response = await _apiService.get('anak'); if (response['status'] == 'success' || response['success'] == true) { anakList = response['data'] ?? []; print('Berhasil mendapatkan ${anakList.length} data anak dari endpoint standard'); // Tampilkan contoh data dari API if (anakList.isNotEmpty) { print('Contoh data API pertama: ${anakList[0]['nama_anak']} (${anakList[0]['tanggal_lahir']})'); } success = true; } } catch (e) { print('Endpoint standard gagal: $e'); } // Opsi 2: Jika opsi 1 gagal, coba endpoint mobile if (!success) { try { print('Mencoba endpoint mobile: mobile/anak/pengguna/$userId'); final response = await _apiService.get('mobile/anak/pengguna/$userId'); if (response['status'] == 'success' || response['success'] == true) { anakList = response['data'] ?? []; print('Berhasil mendapatkan ${anakList.length} data anak dari endpoint mobile'); success = true; } } catch (e) { print('Endpoint mobile gagal: $e'); } } // Opsi 3: Jika semua gagal dan kita memiliki NIK, coba endpoint berdasarkan NIK if (!success && nik != null) { try { print('Mencoba endpoint berdasarkan NIK: anak/pengguna/nik/$nik'); final response = await _apiService.get('anak/pengguna/nik/$nik'); if (response['status'] == 'success' || response['success'] == true) { anakList = response['data'] ?? []; print('Berhasil mendapatkan ${anakList.length} data anak dari endpoint NIK'); success = true; } } catch (e) { print('Endpoint NIK gagal: $e'); } } // Jika berhasil mendapatkan data dari API dan skipLocal=true, langsung kembalikan data API if (success && anakList.isNotEmpty && skipLocal) { print('Mengembalikan hanya data dari API: ${anakList.length} data'); return anakList; } // Gabungkan data dari API dan lokal jika API berhasil dan tidak skip lokal if (success && anakList.isNotEmpty && !skipLocal) { print('Menggabungkan ${anakList.length} data dari API dengan ${localAnakList.length} data lokal'); final combinedList = await _combineAnakLists(anakList, localAnakList); print('Setelah penggabungan: ${combinedList.length} data anak'); // Debug: tampilkan beberapa data setelah penggabungan if (combinedList.isNotEmpty) { for (int i = 0; i < min(3, combinedList.length); i++) { print('Data #${i+1}: ${combinedList[i]['nama_anak']} (${combinedList[i]['tanggal_lahir']})'); } } // Jika ada data dari API, tambahkan informasi pengguna if (anakList.isNotEmpty) { print('Sample anak data API: ${anakList[0]}'); if (anakList[0]['pengguna'] != null) { print('Pengguna data available: ${anakList[0]['pengguna']}'); } else { print('Pengguna data not available in response'); // Coba ambil data parent secara terpisah try { final penggunaData = await getPenggunaById(userId); print('Berhasil mendapatkan data parent: $penggunaData'); // Simpan ke SharedPreferences untuk digunakan di seluruh aplikasi if (penggunaData != null) { await prefs.setString('nik', penggunaData['nik'] ?? ''); await prefs.setString('nama_ibu', penggunaData['nama'] ?? ''); print('Data parent disimpan ke SharedPreferences'); } } catch (e) { print('Gagal mendapatkan data parent: $e'); } } } return combinedList; } // Jika semua endpoint gagal, gunakan data lokal if (!success) { print('Semua endpoint API gagal, menggunakan data lokal: ${localAnakList.length} item'); return localAnakList; } return anakList; } catch (e) { print('Error saat mengambil data anak: $e'); // Jika terjadi error, coba gunakan data lokal try { final localAnakList = await getLocalAnakList(); print('Menggunakan data lokal karena error: ${localAnakList.length} item'); return localAnakList; } catch (localError) { print('Error saat mengakses data lokal: $localError'); return []; } } } /// Mendapatkan detail data anak berdasarkan ID Future> getAnakDetail(int anakId) async { try { // Endpoint sudah menggunakan with('pengguna') di Laravel final response = await _apiService.get('anak/$anakId'); if (response['status'] == 'success' || response['success'] == true) { final anakData = response['data']; print('Detail anak: $anakData'); return anakData; } else { throw Exception(response['message'] ?? 'Gagal mendapatkan detail anak'); } } catch (e) { throw Exception('Error: $e'); } } /// Menambahkan data anak baru Future> createAnak({ required String namaAnak, required String tempatLahir, required DateTime tanggalLahir, required String jenisKelamin, }) async { try { // Dapatkan ID pengguna dari SharedPreferences final prefs = await SharedPreferences.getInstance(); final userId = prefs.getInt('user_id'); // Pastikan menyimpan sebagai int print('Menyimpan data anak...'); print('UserId from SharedPreferences: $userId'); int? penggunaId; // Pastikan user_id tersedia if (userId == null) { print('User ID tidak ditemukan di SharedPreferences - mencoba get user data'); try { // Menggunakan /api/user endpoint untuk mendapatkan data user yang terautentikasi final userData = await _apiService.get('user'); print('User data response: $userData'); if ((userData['status'] == 'success' || userData['success'] == true) && userData['pengguna'] != null) { penggunaId = userData['pengguna']['id']; // Simpan user_id untuk penggunaan selanjutnya await prefs.setInt('user_id', penggunaId!); print('ID pengguna berhasil didapatkan dari API: $penggunaId'); } else { throw Exception('Gagal mendapatkan data pengguna dari API'); } } catch (e) { print('Gagal mendapatkan data pengguna: $e'); // Jika masih gagal, coba tambahkan alternatif login: print('Mencoba login ulang secara otomatis'); try { final nik = prefs.getString('nik'); final authService = AuthService(); if (nik != null) { print('Mencoba login otomatis dengan NIK: $nik'); // Coba login ulang dengan password default (misal 'password') // CATATAN: Ini hanya solusi sementara dan tidak aman untuk produksi // Alternatif lain adalah mengarahkan pengguna ke layar login // await authService.login(nik: nik, password: 'password'); throw Exception('Silakan login kembali'); } else { throw Exception('Data login tidak tersedia'); } } catch (loginError) { throw Exception('Gagal mendapatkan ID pengguna, silakan login kembali'); } } } else { penggunaId = userId; } // Hitung usia dalam bulan dan hari String usia = _calculateAge(tanggalLahir); // Format data untuk API final Map data = { 'pengguna_id': penggunaId, 'nama_anak': namaAnak, 'tempat_lahir': tempatLahir, 'tanggal_lahir': DateFormat('yyyy-MM-dd').format(tanggalLahir), 'jenis_kelamin': jenisKelamin, 'usia': usia, }; print('Mengirim data anak: $data'); try { // Gunakan metode post khusus dengan header tambahan final response = await _apiService.post('anak', data); // Debug informasi respons print('Response full: $response'); print('Response keys: ${response.keys.toList()}'); // API bisa menggunakan 'status' atau 'success' if (response['status'] == 'success' || response['success'] == true) { // Jika API berhasil, JANGAN simpan data lokal, karena nanti saat getAnakList // data ini sudah akan diambil dari server // HAPUS: await _saveAnakToLocalStorage(data); // Hapus data duplikat dengan nama dan tanggal lahir yang sama await _removeDuplicateLocalAnak(data['nama_anak'], data['tanggal_lahir']); return response; } else if (response['status'] == 'error' && response['message'] == 'Gagal autentikasi untuk endpoint anak') { // Jika autentikasi gagal tapi data valid, simpan secara lokal dan beri tahu user print('Menyimpan data anak secara lokal karena autentikasi gagal'); await _saveAnakToLocalStorage(data); // Kembalikan respons berhasil (simulasi) dengan informasi tambahan return { 'status': 'success', 'message': 'Data disimpan secara lokal, akan tersinkronisasi saat terhubung ke server', 'data': data, 'is_local_only': true, }; } else { throw Exception(response['message'] ?? 'Gagal menyimpan data anak'); } } catch (apiError) { print('Error dari API: $apiError'); // Jika API error tapi data valid, simpan secara lokal await _saveAnakToLocalStorage(data); return { 'status': 'success', 'message': 'Data disimpan secara lokal, akan tersinkronisasi saat terhubung ke server', 'data': data, 'is_local_only': true, }; } } catch (e) { print('Error dari API: $e'); throw Exception('Gagal mengirim data ke server: $e'); } } /// Menghapus data anak yang duplikat dari storage lokal berdasarkan nama dan tanggal lahir Future _removeDuplicateLocalAnak(String namaAnak, String tanggalLahir) async { try { final prefs = await SharedPreferences.getInstance(); final anakListJson = prefs.getString('local_anak_list') ?? '[]'; List anakList = jsonDecode(anakListJson); // Filter out anak dengan nama dan tanggal lahir yang sama final filteredList = anakList.where((anak) => !(anak['nama_anak'] == namaAnak && anak['tanggal_lahir'].toString().startsWith(tanggalLahir))).toList(); if (anakList.length != filteredList.length) { print('Menghapus ${anakList.length - filteredList.length} data duplikat dari penyimpanan lokal'); await prefs.setString('local_anak_list', jsonEncode(filteredList)); } } catch (e) { print('Error saat menghapus data duplikat: $e'); } } /// Menyimpan data anak ke storage lokal sebagai fallback Future _saveAnakToLocalStorage(Map anakData) async { try { final prefs = await SharedPreferences.getInstance(); final anakListJson = prefs.getString('local_anak_list') ?? '[]'; List anakList = jsonDecode(anakListJson); // Generate ID lokal jika tidak ada if (!anakData.containsKey('id')) { anakData['id'] = DateTime.now().millisecondsSinceEpoch; } // Tandai sebagai disimpan secara lokal anakData['is_local'] = true; anakData['created_at'] = DateTime.now().toIso8601String(); // Periksa apakah data sudah ada (berdasarkan nama dan tanggal lahir) bool isDuplicate = anakList.any((anak) => anak['nama_anak'] == anakData['nama_anak'] && anak['tanggal_lahir'].toString().startsWith(anakData['tanggal_lahir'].toString())); if (isDuplicate) { print('Data dengan nama "${anakData['nama_anak']}" dan tanggal lahir "${anakData['tanggal_lahir']}" sudah ada, tidak disimpan ulang'); return; } // Tambahkan ke list anakList.add(anakData); // Simpan kembali ke SharedPreferences await prefs.setString('local_anak_list', jsonEncode(anakList)); print('Data anak berhasil disimpan secara lokal: ${anakData['nama_anak']}'); } catch (e) { print('Error menyimpan data anak secara lokal: $e'); } } /// Mendapatkan daftar anak dari storage lokal Future> getLocalAnakList() async { try { final prefs = await SharedPreferences.getInstance(); final anakListJson = prefs.getString('local_anak_list') ?? '[]'; List anakList = jsonDecode(anakListJson); print('Mendapatkan ${anakList.length} data anak dari penyimpanan lokal'); return anakList; } catch (e) { print('Error mendapatkan data anak lokal: $e'); return []; } } /// Menggabungkan data anak dari API dan local storage Future> _combineAnakLists(List apiList, List localList) async { // Gabungkan kedua list, prioritaskan data dari API final Map combinedMap = {}; // Tambahkan data dari API terlebih dahulu (prioritas utama) for (var anak in apiList) { // Gunakan ID sebagai kunci untuk menghindari duplikasi if (anak['id'] != null) { String key = 'id_${anak['id']}'; combinedMap[key] = anak; } else { // Jika tidak ada ID, gunakan kombinasi nama dan tanggal String tanggalKey = anak['tanggal_lahir'].toString().split('T')[0]; // Ambil hanya bagian tanggal String key = 'name_${anak['nama_anak']}_${tanggalKey}'; combinedMap[key] = anak; } } // Tambahkan data lokal HANYA jika belum ada data yang sama dari API for (var anak in localList) { // Periksa apakah anak ini sudah ada di API berdasarkan nama dan tanggal bool alreadyExists = false; // Jika ada ID di data lokal (mungkin dari sinkronisasi sebelumnya) if (anak['id'] != null) { String idKey = 'id_${anak['id']}'; if (combinedMap.containsKey(idKey)) { alreadyExists = true; } } // Jika belum ada, periksa berdasarkan nama dan tanggal if (!alreadyExists) { String tanggalKey = anak['tanggal_lahir'].toString().split('T')[0]; bool found = false; for (var key in combinedMap.keys) { var existingAnak = combinedMap[key]; String existingTanggal = existingAnak['tanggal_lahir'].toString().split('T')[0]; if (existingAnak['nama_anak'] == anak['nama_anak'] && existingTanggal == tanggalKey) { found = true; break; } } // Jika benar-benar tidak ada di API, tambahkan dari lokal if (!found) { String key = 'local_${DateTime.now().millisecondsSinceEpoch}_${anak['nama_anak']}'; combinedMap[key] = anak; } } } print('Total data setelah penggabungan: ${combinedMap.length}'); return combinedMap.values.toList(); } /// Memperbarui data anak yang sudah ada Future> updateAnak({ required int anakId, required String namaAnak, required String tempatLahir, required DateTime tanggalLahir, required String jenisKelamin, }) async { try { // Dapatkan ID pengguna dari SharedPreferences untuk logging final prefs = await SharedPreferences.getInstance(); final userId = prefs.getInt('user_id'); print('Updating anak with ID: $anakId by user ID: $userId'); // Hitung usia dalam bulan dan hari String usia = _calculateAge(tanggalLahir); // Format data untuk API final Map data = { 'nama_anak': namaAnak, 'tempat_lahir': tempatLahir, 'tanggal_lahir': DateFormat('yyyy-MM-dd').format(tanggalLahir), 'jenis_kelamin': jenisKelamin, 'usia': usia, }; final response = await _apiService.put('anak/$anakId', data); // API menggunakan 'status' bukan 'success' if (response['status'] == 'success' || response['success'] == true) { return response; } else { throw Exception(response['message'] ?? 'Gagal memperbarui data anak'); } } catch (e) { throw Exception('Error: $e'); } } /// Menghapus data anak Future deleteAnak(int anakId) async { try { final response = await _apiService.delete('anak/$anakId'); // API menggunakan 'status' bukan 'success' if (response['status'] == 'success' || response['success'] == true) { return true; } else { throw Exception(response['message'] ?? 'Gagal menghapus data anak'); } } catch (e) { throw Exception('Error: $e'); } } /// Mengaitkan anak dengan orang tua berdasarkan NIK Future> linkAnakToParent({ required int anakId, required String nik, }) async { try { final Map data = { 'anak_id': anakId, 'nik': nik, }; final response = await _apiService.post('anak/link-to-parent', data); if (response['status'] == 'success' || response['success'] == true) { return response; } else { throw Exception(response['message'] ?? 'Gagal mengaitkan anak dengan orang tua'); } } catch (e) { throw Exception('Error: $e'); } } /// Fungsi untuk menghitung usia dalam bulan dari tanggal lahir String _calculateAge(DateTime birthDate) { DateTime currentDate = DateTime.now(); // Hitung total usia dalam hari int totalDays = currentDate.difference(birthDate).inDays; // Konversi ke bulan dan hari int months = totalDays ~/ 30; // Aproximasi 1 bulan = 30 hari int days = totalDays % 30; return '$months bulan $days hari'; // Return format bulan dan hari } /// Memperbaiki data ibu yang kosong Future fixParentData() async { try { final prefs = await SharedPreferences.getInstance(); final nik = prefs.getString('nik'); final namaIbu = prefs.getString('nama_ibu'); // Jika nama ibu kosong tapi NIK ada, coba dapatkan dari API if ((namaIbu == null || namaIbu.isEmpty) && nik != null && nik.isNotEmpty) { print('Nama ibu kosong, mencoba dapatkan dari API berdasarkan NIK: $nik'); try { // Coba cari pengguna berdasarkan NIK final response = await _apiService.get('user'); if (response['success'] == true || response['status'] == 'success') { final pengguna = response['pengguna'] ?? response['data']; if (pengguna != null) { // Nama bisa berupa nama atau nama_ibu final nama = pengguna['nama'] ?? pengguna['nama_ibu'] ?? 'Ibu'; // Update SharedPreferences await prefs.setString('nama_ibu', nama); print('Berhasil update nama ibu: $nama'); // Juga update NIK jika kosong if (nik.isEmpty && pengguna.containsKey('nik')) { await prefs.setString('nik', pengguna['nik']); } // Juga update user_id jika belum ada if (pengguna.containsKey('id') && prefs.getInt('user_id') == null) { await prefs.setInt('user_id', pengguna['id']); } } } } catch (e) { print('Gagal mendapatkan data ibu dari API: $e'); // Fallback: gunakan default jika masih kosong if (namaIbu == null || namaIbu.isEmpty) { await prefs.setString('nama_ibu', 'Ibu'); print('Menggunakan nama default: Ibu'); } } } else if (namaIbu == null || namaIbu.isEmpty) { // Jika nama ibu masih kosong, tetapkan ke "Ibu" await prefs.setString('nama_ibu', 'Ibu'); print('Menggunakan nama default: Ibu karena nama_ibu kosong'); } } catch (e) { print('Error saat memperbaiki data ibu: $e'); } } /// Mendapatkan data pengguna (parent) dari SharedPreferences Future> getParentFromPrefs() async { try { // Coba perbaiki data yang kosong terlebih dahulu await fixParentData(); final prefs = await SharedPreferences.getInstance(); final userId = prefs.getInt('user_id'); final nik = prefs.getString('nik') ?? 'Tidak tersedia'; final namaIbu = prefs.getString('nama_ibu') ?? 'Ibu'; print('Data parent dari SharedPreferences setelah perbaikan: userId=$userId, nik=$nik, nama=$namaIbu'); // Pastikan namaIbu tidak kosong if (namaIbu.isEmpty) { await prefs.setString('nama_ibu', 'Ibu'); } return { 'id': userId ?? 0, 'nik': nik, 'nama': namaIbu.isEmpty ? 'Ibu' : namaIbu, }; } catch (e) { print('Error mendapatkan data parent dari SharedPreferences: $e'); return { 'id': 0, 'nik': 'Tidak tersedia', 'nama': 'Ibu', }; } } /// Mendapatkan data pengguna berdasarkan ID Future?> getPenggunaById(int penggunaId) async { try { // Ubah endpoint dari pengguna/{id} menjadi pengguna/detail/{id} // Sesuaikan dengan path yang benar dari Laravel API print('Mencoba mendapatkan data pengguna dengan ID: $penggunaId'); // Coba langsung endpoint user yang sudah terbukti berfungsi try { print('Menggunakan endpoint user untuk mendapatkan data pengguna'); final response = await _apiService.get('user'); if (response['success'] == true || response['status'] == 'success') { // Data pengguna mungkin berada di 'pengguna' atau 'data' final penggunaData = response['pengguna'] ?? response['data']; if (penggunaData != null) { print('Response pengguna dari endpoint user: $penggunaData'); // Normalkan nama field yang berbeda-beda // Field nama bisa berupa 'nama' atau 'nama_ibu' final Map normalizedData = { 'id': penggunaData['id'], 'nik': penggunaData['nik'], 'nama': penggunaData['nama'] ?? penggunaData['nama_ibu'] ?? 'Ibu', }; print('Data pengguna yang dinormalisasi: $normalizedData'); return normalizedData; } } } catch (e) { print('Error saat mengakses endpoint user: $e'); } // Endpoint alternatif jika user tidak berhasil print('Tidak bisa mendapatkan data pengguna dari endpoint utama, mencoba alternatif'); // Coba gunakan beberapa alternatif endpoint final endpoints = [ 'pengguna/detail/$penggunaId', 'user/profile', 'profile' ]; for (String endpoint in endpoints) { try { print('Mencoba endpoint: $endpoint'); final response = await _apiService.get(endpoint); if (response['success'] == true || response['status'] == 'success') { // Data pengguna mungkin berada di 'pengguna' atau 'data' final penggunaData = response['pengguna'] ?? response['data']; if (penggunaData != null) { print('Berhasil mendapatkan data pengguna dari endpoint $endpoint: $penggunaData'); // Normalkan nama field final Map normalizedData = { 'id': penggunaData['id'], 'nik': penggunaData['nik'], 'nama': penggunaData['nama'] ?? penggunaData['nama_ibu'] ?? 'Ibu', }; return normalizedData; } } } catch (e) { print('Endpoint $endpoint error: $e'); continue; } } print('Semua endpoint gagal, gunakan default data'); // Return default data jika tidak bisa mendapatkan dari server return { 'id': penggunaId, 'nik': 'Data tidak tersedia', 'nama': 'Ibu', // Default ke 'Ibu' bukan 'Data tidak tersedia' }; } catch (e) { print('Error mendapatkan data pengguna: $e'); return { 'id': penggunaId, 'nik': 'Data tidak tersedia', 'nama': 'Ibu', // Default ke 'Ibu' }; } } /// Mendapatkan detail pengguna berdasarkan ID Future> getPenggunaDetail(int penggunaId) async { try { // Coba gunakan method getPenggunaById yang lebih robust Map? data = await getPenggunaById(penggunaId); if (data != null) { return data; } // Fallback jika gagal return { 'id': penggunaId, 'nama': 'Data tidak tersedia', 'nik': 'Data tidak tersedia', }; } catch (e) { print('Error mendapatkan detail pengguna: $e'); // Return minimal data jika gagal return { 'id': penggunaId, 'nama': 'Data tidak tersedia', 'nik': 'Data tidak tersedia', }; } } // Tambahkan fungsi untuk menghapus semua data anak lokal Future clearAllLocalAnakData() async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setString('local_anak_list', '[]'); print('Semua data anak lokal telah dihapus'); return true; } catch (e) { print('Error saat menghapus data lokal: $e'); return false; } } }