import 'dart:io'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:uuid/uuid.dart'; import '../models/pet_model.dart'; import '../models/schedule_model.dart'; import '../utils/log_manager.dart'; class FirestoreService { final FirebaseFirestore _db = FirebaseFirestore.instance; final FirebaseStorage _storage = FirebaseStorage.instance; final FirebaseAuth _auth = FirebaseAuth.instance; // 반려동물 등록 Future registerPet(Pet pet, File? imageFile) async { try { String? imageUrl; // 1. 이미지 업로드 (이미지가 있는 경우) if (imageFile != null) { final String fileName = '${pet.id}_${DateTime.now().millisecondsSinceEpoch}.jpg'; final Reference storageRef = _storage .ref() .child('pet_images') .child(fileName); LogManager().addLog( '[Storage] Starting upload to ${storageRef.fullPath}', ); LogManager().addLog('[Storage] Bucket: ${_storage.bucket}'); final TaskSnapshot snapshot = await storageRef.putFile(imageFile); if (snapshot.state == TaskState.success) { LogManager().addLog( '[Storage] Upload success. Validating metadata...', ); // 잠시 대기 (consistency 이슈 방지) await Future.delayed(const Duration(milliseconds: 500)); imageUrl = await storageRef.getDownloadURL(); LogManager().addLog('[Storage] Download URL retrieved: $imageUrl'); } else { throw Exception('이미지 업로드 실패 (상태: ${snapshot.state})'); } } // 2. Pet 객체에 이미지 URL 업데이트 (새로운 객체 생성) Pet petWithImage = Pet( id: pet.id, ownerId: pet.ownerId, name: pet.name, species: pet.species, breed: pet.breed, gender: pet.gender, isNeutered: pet.isNeutered, birthDate: pet.birthDate, isDateUnknown: pet.isDateUnknown, registrationNumber: pet.registrationNumber, profileImageUrl: imageUrl, // 이미지 URL 설정 weight: pet.weight, // 체중 추가 diseases: pet.diseases, pastDiseases: pet.pastDiseases, healthConcerns: pet.healthConcerns, createdAt: pet.createdAt, ); // 3. Firestore에 저장 await _db.collection('pets').doc(pet.id).set(petWithImage.toMap()); LogManager().addLog('[Firestore] Pet registered successfully: ${pet.id}'); } catch (e) { LogManager().addLog('[Firestore] Error registering pet: $e'); throw Exception('반려동물 등록 실패: $e'); } } // 반려동물 정보 수정 Future updatePet(Pet pet, File? newImageFile) async { try { String? imageUrl = pet.profileImageUrl; // 1. 새 이미지가 있다면 업로드 및 기존 이미지 삭제(선택사항) if (newImageFile != null) { // 기존 이미지가 있다면 삭제할 수도 있겠지만, 여기선 덮어쓰거나 새로 올림 // 간단히 새로 올리고 URL 교체 final String fileName = '${pet.id}_${DateTime.now().millisecondsSinceEpoch}.jpg'; final Reference storageRef = _storage .ref() .child('pet_images') .child(fileName); LogManager().addLog('[Storage] Uploading new image...'); await storageRef.putFile(newImageFile); imageUrl = await storageRef.getDownloadURL(); } // 2. Pet 객체 업데이트 Pet updatedPet = Pet( id: pet.id, // ID 유지 ownerId: pet.ownerId, // Owner ID 유지 name: pet.name, species: pet.species, breed: pet.breed, gender: pet.gender, isNeutered: pet.isNeutered, birthDate: pet.birthDate, isDateUnknown: pet.isDateUnknown, registrationNumber: pet.registrationNumber, profileImageUrl: imageUrl, weight: pet.weight, diseases: pet.diseases, pastDiseases: pet.pastDiseases, healthConcerns: pet.healthConcerns, createdAt: pet.createdAt, // 생성일 유지 ); // 3. Firestore 업데이트 await _db.collection('pets').doc(pet.id).set(updatedPet.toMap()); LogManager().addLog('[Firestore] Pet updated successfully: ${pet.id}'); } catch (e) { LogManager().addLog('[Firestore] Error updating pet: $e'); throw Exception('반려동물 수정 실패: $e'); } } // 현재 로그인한 사용자의 ID 가져오기 String? getCurrentUserId() { return _auth.currentUser?.uid; } // 반려동물 리스트 스트림 가져오기 Stream> getPets(String userId) { return _db .collection('pets') .where('ownerId', isEqualTo: userId) .orderBy('createdAt', descending: true) // 최신 등록순 .snapshots() .map((snapshot) { return snapshot.docs.map((doc) => Pet.fromMap(doc.data())).toList(); }); } // 새 Pet ID 생성 String generatePetId() { return const Uuid().v4(); } // 일정 추가 Future addSchedule(Schedule schedule) async { try { await _db.collection('schedules').doc(schedule.id).set(schedule.toMap()); } catch (e) { LogManager().addLog('[Firestore] Error adding schedule: $e'); throw Exception('일정 추가 실패: $e'); } } // 일정 조회 (특정 반려동물, 특정 날짜) // 날짜는 년,월,일 만 비교를 위해 range query 사용 (startOfDay ~ endOfDay) Stream> getSchedules(String petId, DateTime date) { DateTime startOfDay = DateTime(date.year, date.month, date.day); DateTime endOfDay = DateTime(date.year, date.month, date.day, 23, 59, 59); return _db .collection('schedules') .where('petId', isEqualTo: petId) .where('date', isGreaterThanOrEqualTo: Timestamp.fromDate(startOfDay)) .where('date', isLessThanOrEqualTo: Timestamp.fromDate(endOfDay)) .snapshots() .map((snapshot) { return snapshot.docs .map((doc) => Schedule.fromMap(doc.data())) .toList(); }); } // 월간 일정 조회 (도장 모드용) - 해당 월의 모든 일정 가져오기 Stream> getMonthlySchedules(String petId, DateTime month) { DateTime startOfMonth = DateTime(month.year, month.month, 1); DateTime endOfMonth = DateTime(month.year, month.month + 1, 0, 23, 59, 59); return _db .collection('schedules') .where('petId', isEqualTo: petId) .where('date', isGreaterThanOrEqualTo: Timestamp.fromDate(startOfMonth)) .where('date', isLessThanOrEqualTo: Timestamp.fromDate(endOfMonth)) .snapshots() .map((snapshot) { return snapshot.docs .map((doc) => Schedule.fromMap(doc.data())) .toList(); }); } // 일정 수정 Future updateSchedule(Schedule schedule) async { try { await _db .collection('schedules') .doc(schedule.id) .update(schedule.toMap()); } catch (e) { LogManager().addLog('[Firestore] Error updating schedule: $e'); throw Exception('일정 수정 실패: $e'); } } // 일정 삭제 Future deleteSchedule(String scheduleId) async { try { await _db.collection('schedules').doc(scheduleId).delete(); } catch (e) { LogManager().addLog('[Firestore] Error deleting schedule: $e'); throw Exception('일정 삭제 실패: $e'); } } // ID 생성 String generateScheduleId() { return const Uuid().v4(); } }