package com.aistudio.bonjuteri.jwlry.data import android.content.Context import android.util.Log import com.aistudio.bonjuteri.jwlry.model.Address import com.aistudio.bonjuteri.jwlry.model.CartItem import com.aistudio.bonjuteri.jwlry.model.Order import com.aistudio.bonjuteri.jwlry.model.OrderItem import com.aistudio.bonjuteri.jwlry.model.OrderStatus import com.aistudio.bonjuteri.jwlry.model.PaymentStatus import com.aistudio.bonjuteri.jwlry.model.Product import com.google.firebase.Timestamp import com.google.firebase.auth.FirebaseAuth import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.Query import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.tasks.await import java.text.SimpleDateFormat import java.util.Date import java.util.Locale import kotlin.random.Random class OrderRepository( private val firestore: FirebaseFirestore, private val auth: FirebaseAuth ) { companion object { private const val TAG = "OrderRepository" const val ORDERS_COLLECTION = "orders" @Volatile private var INSTANCE: OrderRepository? = null fun getInstance(context: Context): OrderRepository { return INSTANCE ?: synchronized(this) { INSTANCE ?: OrderRepository( FirestoreHelper.getFirestore(context), FirebaseAuth.getInstance() ).also { INSTANCE = it } } } } private fun requireUserId(): String { return auth.currentUser?.uid ?: throw IllegalStateException("Sipariş oluşturmak için lütfen oturum açınız.") } fun observeUserOrders(): Flow> = callbackFlow { val uid = auth.currentUser?.uid if (uid == null) { trySend(emptyList()) close() return@callbackFlow } val query = firestore.collection(ORDERS_COLLECTION) .whereEqualTo("userId", uid) val listener = query.addSnapshotListener { snapshot, error -> if (error != null) { FirestoreHelper.handleFirestoreError(error, "observeUserOrders", ORDERS_COLLECTION) trySend(emptyList()) return@addSnapshotListener } val orders = snapshot?.documents?.mapNotNull { it.toObject(Order::class.java) } ?.sortedByDescending { it.createdAt?.seconds ?: 0L } ?: emptyList() trySend(orders) } awaitClose { listener.remove() } } fun observeAllOrders(): Flow> = callbackFlow { val query = firestore.collection(ORDERS_COLLECTION) val listener = query.addSnapshotListener { snapshot, error -> if (error != null) { FirestoreHelper.handleFirestoreError(error, "observeAllOrders", ORDERS_COLLECTION) trySend(emptyList()) return@addSnapshotListener } val orders = snapshot?.documents?.mapNotNull { it.toObject(Order::class.java) } ?.sortedByDescending { it.createdAt?.seconds ?: 0L } ?: emptyList() trySend(orders) } awaitClose { listener.remove() } } suspend fun createOrder( cartItems: List, deliveryAddress: Address, paymentMethod: String = "Havale / EFT & Kapıda Ödeme" ): Result { if (cartItems.isEmpty()) { return Result.failure(IllegalArgumentException("Sepetinizde ürün bulunmuyor.")) } if (deliveryAddress.fullAddress.isBlank() || deliveryAddress.city.isBlank()) { return Result.failure(IllegalArgumentException("Lütfen geçerli bir teslimat adresi seçiniz.")) } return try { val uid = requireUserId() val userEmail = auth.currentUser?.email ?: "" // Price & stock server-side verification: Re-fetch every item from Firestore products collection! var calculatedSubtotal = 0.0 val verifiedOrderItems = mutableListOf() for (cartItem in cartItems) { val prodDoc = firestore.collection(ProductRepository.PRODUCTS_COLLECTION) .document(cartItem.productId) .get() .await() val liveProd = prodDoc.toObject(Product::class.java) ?: return Result.failure(IllegalStateException("${cartItem.title} sistemde bulunamadı.")) if (liveProd.stock < cartItem.quantity) { return Result.failure(IllegalStateException("${liveProd.title} için yetersiz stok! Mevcut stok: ${liveProd.stock} adet.")) } val itemTotal = liveProd.price * cartItem.quantity calculatedSubtotal += itemTotal verifiedOrderItems.add( OrderItem( productId = liveProd.id, title = liveProd.title, imageUrl = liveProd.imageUrl, price = liveProd.price, quantity = cartItem.quantity ) ) } val shippingCost = if (calculatedSubtotal >= CartRepository.FREE_SHIPPING_THRESHOLD) 0.0 else CartRepository.STANDARD_SHIPPING_COST val totalAmount = calculatedSubtotal + shippingCost val dateStr = SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(Date()) val randomSuffix = Random.nextInt(1000, 9999) val uniqueOrderNumber = "BNJ-$dateStr-$randomSuffix" val docRef = firestore.collection(ORDERS_COLLECTION).document() val newOrder = Order( id = docRef.id, orderNumber = uniqueOrderNumber, userId = uid, userEmail = userEmail, items = verifiedOrderItems, deliveryAddress = deliveryAddress, subtotal = calculatedSubtotal, discount = 0.0, shippingCost = shippingCost, totalAmount = totalAmount, orderStatus = OrderStatus.PREPARING.name, // Compliance rule: Real payment integration is pending, so status is strictly PENDING paymentStatus = PaymentStatus.PENDING.name, paymentMethod = paymentMethod, cargoCompany = "Hazırlanıyor", trackingNumber = "", adminNotes = "Sipariş alındı. Ödeme onayı bekleniyor.", createdAt = Timestamp.now(), updatedAt = Timestamp.now() ) // Atomic batch: decrement product stocks and save order val batch = firestore.batch() batch.set(docRef, newOrder) for (orderItem in verifiedOrderItems) { val prodRef = firestore.collection(ProductRepository.PRODUCTS_COLLECTION).document(orderItem.productId) val liveDoc = prodRef.get().await() val currentStock = liveDoc.getLong("stock") ?: 0L val newStock = (currentStock - orderItem.quantity).coerceAtLeast(0L) batch.update(prodRef, "stock", newStock) } // Clear user's cart val cartRef = firestore.collection("users").document(uid).collection("cart") val cartSnapshots = cartRef.get().await() for (doc in cartSnapshots.documents) { batch.delete(doc.reference) } batch.commit().await() Log.d(TAG, "Sipariş başarıyla oluşturuldu: ${newOrder.orderNumber}") Result.success(newOrder) } catch (e: Throwable) { val msg = FirestoreHelper.handleFirestoreError(e, "createOrder", ORDERS_COLLECTION) Log.e(TAG, "Sipariş oluşturma hatası: $msg", e) Result.failure(Exception(msg)) } } suspend fun updateOrderStatus(orderId: String, newStatus: OrderStatus): Result { return try { firestore.collection(ORDERS_COLLECTION).document(orderId) .update( mapOf( "orderStatus" to newStatus.name, "updatedAt" to Timestamp.now() ) ).await() Result.success(Unit) } catch (e: Throwable) { val msg = FirestoreHelper.handleFirestoreError(e, "updateOrderStatus", orderId) Result.failure(Exception(msg)) } } suspend fun updateCargoInfo(orderId: String, cargoCompany: String, trackingNumber: String): Result { return try { firestore.collection(ORDERS_COLLECTION).document(orderId) .update( mapOf( "cargoCompany" to cargoCompany.trim(), "trackingNumber" to trackingNumber.trim(), "updatedAt" to Timestamp.now() ) ).await() Result.success(Unit) } catch (e: Throwable) { val msg = FirestoreHelper.handleFirestoreError(e, "updateCargoInfo", orderId) Result.failure(Exception(msg)) } } suspend fun updatePaymentStatus(orderId: String, newPaymentStatus: PaymentStatus): Result { return try { firestore.collection(ORDERS_COLLECTION).document(orderId) .update( mapOf( "paymentStatus" to newPaymentStatus.name, "updatedAt" to Timestamp.now() ) ).await() Result.success(Unit) } catch (e: Throwable) { val msg = FirestoreHelper.handleFirestoreError(e, "updatePaymentStatus", orderId) Result.failure(Exception(msg)) } } }