package com.example.util import android.util.Log import com.example.data.model.AdminSettings import com.example.data.model.CustomerUser import com.example.data.model.FoodItem import com.example.data.model.Order import com.example.data.model.OrderItemSummary import com.example.data.model.OrderStatus import com.google.android.gms.tasks.Task import com.google.firebase.FirebaseApp import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.Query import com.squareup.moshi.Moshi import com.squareup.moshi.Types import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException object CloudSyncManager { private const val TAG = "CloudSyncManager" const val DATABASE_ID = "ai-studio-android-lovezone-536461f3-30bc-4e6e-934e-fffa810f49f8" private fun getFirestore(): FirebaseFirestore { return try { FirebaseFirestore.getInstance(DATABASE_ID) } catch (e: Exception) { Log.w(TAG, "Custom databaseId lookup fallback: ${e.message}") try { val app = FirebaseApp.getInstance() FirebaseFirestore.getInstance(app, DATABASE_ID) } catch (ex: Exception) { FirebaseFirestore.getInstance() } } } private val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) .build() private val itemsListType = Types.newParameterizedType( List::class.java, OrderItemSummary::class.java ) private val itemsAdapter = moshi.adapter>(itemsListType) private suspend fun Task.awaitResult(): T = suspendCancellableCoroutine { continuation -> addOnSuccessListener { result -> if (continuation.isActive) continuation.resume(result) } addOnFailureListener { exception -> if (continuation.isActive) continuation.resumeWithException(exception) } addOnCanceledListener { if (continuation.isActive) continuation.cancel() } } /** * Checks if a phone number is registered centrally in Firestore across all devices globally. */ suspend fun isPhoneRegisteredCentrally(phoneDigits: String): Boolean = withContext(Dispatchers.IO) { val digits = phoneDigits.filter { it.isDigit() }.let { if (it.length > 10) it.takeLast(10) else it } if (digits.length < 10) return@withContext false try { val doc = getFirestore().collection("users").document(digits).get().awaitResult() val exists = doc.exists() Log.d(TAG, "Central check for $digits: exists=$exists") return@withContext exists } catch (e: Exception) { Log.e(TAG, "Failed to check central phone registration: ${e.message}", e) } return@withContext false } /** * Registers or updates a user profile centrally in Firestore so all devices see the registered number. */ suspend fun registerUserCentrally(user: CustomerUser): Boolean = withContext(Dispatchers.IO) { val digits = user.phoneDigits if (digits.length < 10) return@withContext false try { val data = hashMapOf( "phone" to user.phone, "phoneDigits" to digits, "name" to user.name, "email" to user.email, "alternatePhone" to user.alternatePhone, "houseNo" to user.houseNo, "streetArea" to user.streetArea, "landmark" to user.landmark, "zone" to user.zone, "pincode" to user.pincode, "address" to user.address, "dietaryPreference" to user.dietaryPreference, "dateOfBirth" to user.dateOfBirth, "deliveryNote" to user.deliveryNote, "walletBalance" to user.walletBalance, "profileImageUri" to user.profileImageUri, "password" to user.password, "registeredAt" to System.currentTimeMillis() ) getFirestore().collection("users").document(digits).set(data).awaitResult() Log.d(TAG, "Successfully registered user $digits centrally in Firestore") return@withContext true } catch (e: Exception) { Log.e(TAG, "Failed to register user centrally: ${e.message}", e) return@withContext false } } /** * Fetches a registered user profile centrally from Firestore by phone digits. */ suspend fun fetchUserCentrally(phoneDigits: String): CustomerUser? = withContext(Dispatchers.IO) { val digits = phoneDigits.filter { it.isDigit() }.let { if (it.length > 10) it.takeLast(10) else it } if (digits.length < 10) return@withContext null try { val doc = getFirestore().collection("users").document(digits).get().awaitResult() if (doc.exists()) { return@withContext CustomerUser( phone = doc.getString("phone") ?: digits, name = doc.getString("name") ?: "Customer $digits", email = doc.getString("email") ?: "", alternatePhone = doc.getString("alternatePhone") ?: "", houseNo = doc.getString("houseNo") ?: "House #12", streetArea = doc.getString("streetArea") ?: "Halaimura", landmark = doc.getString("landmark") ?: "Near Bhairav Thali", zone = doc.getString("zone") ?: "Kumarghat", pincode = doc.getString("pincode") ?: "799290", address = doc.getString("address") ?: "Kumarghat, Tripura 799290", dietaryPreference = doc.getString("dietaryPreference") ?: "Both", dateOfBirth = doc.getString("dateOfBirth") ?: "", deliveryNote = doc.getString("deliveryNote") ?: "", walletBalance = doc.getDouble("walletBalance") ?: 0.0, profileImageUri = doc.getString("profileImageUri") ?: "", password = doc.getString("password") ?: "", isLoggedIn = true ) } } catch (e: Exception) { Log.e(TAG, "Failed to fetch user centrally: ${e.message}", e) } return@withContext null } /** * Fetches all registered users centrally across all customer devices. */ suspend fun fetchAllUsersCentrally(): List = withContext(Dispatchers.IO) { try { val querySnapshot = getFirestore().collection("users").get().awaitResult() val list = mutableListOf() for (doc in querySnapshot.documents) { val digits = doc.getString("phoneDigits") ?: doc.id list.add( CustomerUser( phone = doc.getString("phone") ?: digits, name = doc.getString("name") ?: "Customer $digits", email = doc.getString("email") ?: "", alternatePhone = doc.getString("alternatePhone") ?: "", houseNo = doc.getString("houseNo") ?: "House #12", streetArea = doc.getString("streetArea") ?: "Halaimura", landmark = doc.getString("landmark") ?: "Near Bhairav Thali", zone = doc.getString("zone") ?: "Kumarghat", pincode = doc.getString("pincode") ?: "799290", address = doc.getString("address") ?: "Kumarghat, Tripura 799290", dietaryPreference = doc.getString("dietaryPreference") ?: "Both", dateOfBirth = doc.getString("dateOfBirth") ?: "", deliveryNote = doc.getString("deliveryNote") ?: "", walletBalance = doc.getDouble("walletBalance") ?: 0.0, profileImageUri = doc.getString("profileImageUri") ?: "", isLoggedIn = false ) ) } return@withContext list } catch (e: Exception) { Log.e(TAG, "Failed to fetch all users centrally: ${e.message}") } return@withContext emptyList() } /** * Pushes a customer order to Firestore in real time. */ suspend fun pushOrderCentrally(order: Order): Boolean = withContext(Dispatchers.IO) { try { val sanitizeId = order.orderId.replace("#", "").replace("/", "_") val itemsJson = itemsAdapter.toJson(order.items) val data = hashMapOf( "orderId" to order.orderId, "customerName" to order.customerName, "customerPhone" to order.customerPhone, "deliveryAddress" to order.deliveryAddress, "itemsJson" to itemsJson, "itemTotal" to order.itemTotal, "deliveryFee" to order.deliveryFee, "discount" to order.discount, "grandTotal" to order.grandTotal, "orderStatus" to order.orderStatus.name, "paymentMethod" to order.paymentMethod, "paymentStatus" to order.paymentStatus, "upiTransactionId" to order.upiTransactionId, "assignedRiderName" to order.assignedRiderName, "assignedRiderPhone" to order.assignedRiderPhone, "assignedRiderVehicle" to order.assignedRiderVehicle, "timestamp" to order.timestamp ) getFirestore().collection("orders").document(sanitizeId).set(data).awaitResult() Log.d(TAG, "Pushed order ${order.orderId} centrally to Firestore") return@withContext true } catch (e: Exception) { Log.e(TAG, "Failed to push order centrally: ${e.message}", e) return@withContext false } } /** * Fetches all live orders from Firestore. */ suspend fun fetchAllOrdersCentrally(): List = withContext(Dispatchers.IO) { try { val snapshot = getFirestore().collection("orders") .get() .awaitResult() val list = mutableListOf() for (doc in snapshot.documents) { val orderId = doc.getString("orderId") ?: doc.id val itemsJson = doc.getString("itemsJson") ?: "[]" val items = try { itemsAdapter.fromJson(itemsJson) ?: emptyList() } catch (e: Exception) { emptyList() } val statusName = doc.getString("orderStatus") ?: "PLACED" val status = try { OrderStatus.valueOf(statusName) } catch (e: Exception) { OrderStatus.PLACED } list.add( Order( orderId = orderId, customerName = doc.getString("customerName") ?: "Customer", customerPhone = doc.getString("customerPhone") ?: "", deliveryAddress = doc.getString("deliveryAddress") ?: "", items = items, itemTotal = doc.getDouble("itemTotal") ?: 0.0, deliveryFee = doc.getDouble("deliveryFee") ?: 0.0, discount = doc.getDouble("discount") ?: 0.0, grandTotal = doc.getDouble("grandTotal") ?: 0.0, orderStatus = status, paymentMethod = doc.getString("paymentMethod") ?: "ONLINE_UPI", paymentStatus = doc.getString("paymentStatus") ?: "RECEIVED_VERIFIED", upiTransactionId = doc.getString("upiTransactionId") ?: "", assignedRiderName = doc.getString("assignedRiderName") ?: "", assignedRiderPhone = doc.getString("assignedRiderPhone") ?: "", assignedRiderVehicle = doc.getString("assignedRiderVehicle") ?: "", timestamp = doc.getLong("timestamp") ?: System.currentTimeMillis() ) ) } return@withContext list.sortedByDescending { it.timestamp } } catch (e: Exception) { Log.e(TAG, "Failed to fetch all orders centrally: ${e.message}", e) } return@withContext emptyList() } /** * Updates an order status centrally (e.g. PREPARING, OUT_FOR_DELIVERY, DELIVERED). */ suspend fun updateOrderStatusCentrally(orderId: String, newStatus: OrderStatus): Boolean = withContext(Dispatchers.IO) { try { val sanitizeId = orderId.replace("#", "").replace("/", "_") getFirestore().collection("orders").document(sanitizeId) .update("orderStatus", newStatus.name) .awaitResult() return@withContext true } catch (e: Exception) { Log.e(TAG, "Failed to update order status centrally: ${e.message}", e) return@withContext false } } /** * Updates rider assignment centrally on Firestore. */ suspend fun assignRiderCentrally(orderId: String, riderName: String, riderPhone: String, vehicle: String): Boolean = withContext(Dispatchers.IO) { try { val sanitizeId = orderId.replace("#", "").replace("/", "_") val updates = mapOf( "orderStatus" to OrderStatus.OUT_FOR_DELIVERY.name, "assignedRiderName" to riderName, "assignedRiderPhone" to riderPhone, "assignedRiderVehicle" to vehicle ) getFirestore().collection("orders").document(sanitizeId).update(updates).awaitResult() return@withContext true } catch (e: Exception) { Log.e(TAG, "Failed to assign rider centrally: ${e.message}", e) return@withContext false } } /** * Pushes a menu item with updated price to Firestore. */ suspend fun pushMenuItemCentrally(item: FoodItem): Boolean = withContext(Dispatchers.IO) { try { val data = hashMapOf( "id" to item.id, "name" to item.name, "category" to item.category, "price" to item.price, "originalPrice" to item.originalPrice, "imageUri" to item.imageUri, "isAvailable" to item.isAvailable, "isVeg" to item.isVeg, "rating" to item.rating.toDouble(), "reviewCount" to item.reviewCount, "description" to item.description, "preparationTimeMins" to item.preparationTimeMins, "isBestSeller" to item.isBestSeller ) getFirestore().collection("menu_items").document(item.id.toString()).set(data).awaitResult() return@withContext true } catch (e: Exception) { Log.e(TAG, "Failed to push menu item centrally: ${e.message}", e) return@withContext false } } /** * Fetches all menu items from Firestore to ensure price updates reflect on every customer's device. */ suspend fun fetchAllMenuItemsCentrally(): List = withContext(Dispatchers.IO) { try { val snapshot = getFirestore().collection("menu_items").get().awaitResult() val list = mutableListOf() for (doc in snapshot.documents) { val id = doc.getLong("id") ?: continue list.add( FoodItem( id = id, name = doc.getString("name") ?: "", category = doc.getString("category") ?: "Snacks", price = doc.getDouble("price") ?: 0.0, originalPrice = doc.getDouble("originalPrice") ?: 0.0, imageUri = doc.getString("imageUri") ?: "", isAvailable = doc.getBoolean("isAvailable") ?: true, isVeg = doc.getBoolean("isVeg") ?: false, rating = doc.getDouble("rating")?.toFloat() ?: 4.8f, reviewCount = doc.getLong("reviewCount")?.toInt() ?: 10, description = doc.getString("description") ?: "", preparationTimeMins = doc.getLong("preparationTimeMins")?.toInt() ?: 25, isBestSeller = doc.getBoolean("isBestSeller") ?: false ) ) } return@withContext list } catch (e: Exception) { Log.e(TAG, "Failed to fetch menu items centrally: ${e.message}", e) } return@withContext emptyList() } /** * Pushes admin settings (including custom domain like lovezonerasturant.shop) centrally. */ suspend fun pushAdminSettingsCentrally(settings: AdminSettings): Boolean = withContext(Dispatchers.IO) { try { val data = hashMapOf( "restaurantName" to settings.restaurantName, "upiId" to settings.upiId, "qrImageUri" to settings.qrImageUri, "isOpen" to settings.isOpen, "freeDeliveryThreshold" to settings.freeDeliveryThreshold, "deliveryFee" to settings.deliveryFee, "announcement" to settings.announcement, "customDomain" to settings.customDomain ) getFirestore().collection("settings").document("general").set(data).awaitResult() return@withContext true } catch (e: Exception) { Log.e(TAG, "Failed to push admin settings centrally: ${e.message}", e) return@withContext false } } /** * Fetches admin settings from Firestore. */ suspend fun fetchAdminSettingsCentrally(): AdminSettings? = withContext(Dispatchers.IO) { try { val doc = getFirestore().collection("settings").document("general").get().awaitResult() if (doc.exists()) { return@withContext AdminSettings( restaurantName = doc.getString("restaurantName") ?: "Love Zone Restaurant", upiId = doc.getString("upiId") ?: "lovezonerestaurant@okaxis", qrImageUri = doc.getString("qrImageUri") ?: "", isOpen = doc.getBoolean("isOpen") ?: true, freeDeliveryThreshold = doc.getDouble("freeDeliveryThreshold") ?: 199.0, deliveryFee = doc.getDouble("deliveryFee") ?: 30.0, announcement = doc.getString("announcement") ?: "Welcome to Love Zone Restaurant! Order delicious meals now.", customDomain = doc.getString("customDomain") ?: "https://lovezonerasturant.shop" ) } } catch (e: Exception) { Log.e(TAG, "Failed to fetch admin settings centrally: ${e.message}", e) } return@withContext null } }