package com.xuqm.sdk.push import android.content.Context import android.util.Log import com.google.firebase.messaging.FirebaseMessaging import com.xuqm.sdk.XuqmLoginSession import com.xuqm.sdk.XuqmSDK import com.xuqm.sdk.core.ServiceEndpointRegistry import com.xuqm.sdk.network.ApiClient import com.xuqm.sdk.push.api.PushApi import com.xuqm.sdk.push.model.PushRegistrationSnapshot import com.xuqm.sdk.push.model.PushVendor import com.xuqm.sdk.push.storage.PushRegistrationStore import com.xuqm.sdk.utils.DeviceUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import java.util.concurrent.atomic.AtomicReference object PushSDK { private val api: PushApi get() = ApiClient.create(PushApi::class.java, ServiceEndpointRegistry.pushBaseUrl) private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private val registeredUserId = AtomicReference(null) fun currentRegistration(context: Context): PushRegistrationSnapshot? { XuqmSDK.requireInit() val deviceId = DeviceUtils.getDeviceId(context) return store(context).load(deviceId = deviceId, fallbackVendor = detectVendor()) } internal fun updateNativePushToken( context: Context, vendor: PushVendor, pushToken: String, ) { XuqmSDK.requireInit() val normalizedToken = pushToken.trim() require(normalizedToken.isNotBlank()) { "pushToken must not be blank" } store(context).save(vendor, normalizedToken) val sessionUserId = XuqmSDK.currentLoginSession?.userId if (sessionUserId != null) { bindImUser(context, sessionUserId) } } fun bindImUser(context: Context, userId: String) { if (!isReceivePushEnabled(context)) return ensureNativePushToken(context) registerDevice(context, userId) } fun unbindImUser(userId: String) { unregisterDevice(userId) } fun setReceivePush( context: Context, userId: String? = XuqmSDK.currentLoginSession?.userId, enabled: Boolean, ) { XuqmSDK.requireInit() store(context).setReceivePush(enabled) val resolvedUserId = userId ?: registeredUserId.get() if (resolvedUserId != null) { scope.launch { runCatching { api.setReceivePush( appId = XuqmSDK.appId, userId = resolvedUserId, enabled = enabled, ) if (enabled) { bindImUser(context, resolvedUserId) } } } } } fun registerDevice( context: Context, userId: String, ) { XuqmSDK.requireInit() val registration = currentRegistration(context) val vendor = registration?.vendor ?: detectVendor() val pushToken = registration?.pushToken?.takeIf { it.isNotBlank() } if (pushToken.isNullOrBlank()) { Log.w("XuqmPushSDK", "Native push token not ready yet, waiting for onNewToken()") ensureNativePushToken(context) return } scope.launch { runCatching { api.registerDevice( appId = XuqmSDK.appId, userId = userId, vendor = vendor.name, token = pushToken, ) registeredUserId.set(userId) store(context).updateLastUserId(userId) Log.i( "XuqmPushSDK", "Registered push device for userId=$userId vendor=${vendor.name}", ) } } } fun unregisterDevice(userId: String) { XuqmSDK.requireInit() scope.launch { runCatching { api.unregisterDevice(XuqmSDK.appId, userId) registeredUserId.compareAndSet(userId, null) store(XuqmSDK.appContext).updateLastUserId(null) } } } fun onSdkLogin(session: XuqmLoginSession) { val context = runCatching { XuqmSDK.appContext }.getOrNull() ?: return if (registeredUserId.get() == session.userId) return bindImUser(context, session.userId) } fun onSdkLogout() { val userId = registeredUserId.getAndSet(null) ?: return unregisterDevice(userId) } private fun detectVendor(): PushVendor = PushVendor.FCM private fun store(context: Context): PushRegistrationStore = PushRegistrationStore(context.applicationContext) private fun isReceivePushEnabled(context: Context): Boolean = store(context).load( deviceId = DeviceUtils.getDeviceId(context), fallbackVendor = detectVendor(), )?.receivePush ?: true private fun ensureNativePushToken(context: Context) { val vendor = detectVendor() if (vendor != PushVendor.FCM) return val registration = currentRegistration(context) if (registration?.pushToken?.isNotBlank() == true) return runCatching { FirebaseMessaging.getInstance() } .onSuccess { messaging -> messaging.token .addOnSuccessListener { token -> if (token.isNotBlank()) { store(context).save(PushVendor.FCM, token) val sessionUserId = XuqmSDK.currentLoginSession?.userId if (sessionUserId != null) { registerDevice(context, sessionUserId) } } } .addOnFailureListener { error -> Log.w("XuqmPushSDK", "Unable to fetch FCM token: ${error.message}") } } .onFailure { error -> Log.w("XuqmPushSDK", "Firebase Messaging not available: ${error.message}") } } }