Untitled

 avatar
unknown
kotlin
7 days ago
12 kB
20
Indexable
package ru.space.irminsul.models

import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.firebase.messaging.FirebaseMessaging
import com.google.gson.Gson
import io.appwrite.Client
import io.appwrite.Query
import io.appwrite.enums.OAuthProvider
import io.appwrite.services.Account
import io.appwrite.services.Databases
import io.appwrite.services.Realtime
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await
import kotlinx.coroutines.withContext
import ru.space.irminsul.screens.createUser

class AuthViewModel(private val client: Client, private val context: Context) : ViewModel() {

    private val account = Account(client)
    private val databases = Databases(client)
    private val realtime = Realtime(client)

    private val _user = MutableStateFlow<AppUser?>(null)
    val user: StateFlow<AppUser?> = _user

    private val _isAuth = MutableStateFlow(false)
    val isAuth: StateFlow<Boolean> = _isAuth

    companion object {
        private const val DATABASE_ID = "."
        private const val COLLECTION_ID = "."
        private const val PREFS_NAME = "user_prefs"
        private const val PREF_USER_KEY = "cached_user"
    }

    init {
        loadCachedUser() // Загружаем сохраненные данные
        checkAuth()
        subscribeToUserUpdates()
    }

    /**
     * Загружает кэшированные данные пользователя при старте
     */
    private fun loadCachedUser() {
        val sharedPrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
        val userJson = sharedPrefs.getString(PREF_USER_KEY, null)
        userJson?.let {
            val cachedUser = Gson().fromJson(it, AppUser::class.java)
            _user.value = cachedUser
        }
    }

    /**
     * Сохраняет данные пользователя в SharedPreferences
     */
    private fun saveUserToCache(user: AppUser) {
        val sharedPrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
        sharedPrefs.edit().putString(PREF_USER_KEY, Gson().toJson(user)).apply()
    }

    /**
     * Проверка авторизации пользователя и загрузка его данных
     */
    fun checkAuth() {
        viewModelScope.launch {
            try {
                val userData = account.get()
                val dbUser = fetchUserFromDatabase(userData.id)

                if (dbUser != null) {
                    _user.value = dbUser
                    saveUserToCache(dbUser) // Сохраняем данные
                } else {
                    val newUser =
                        AppUser(userData.id, userData.name, userData.email, "", rating = 1)
                    createUser(newUser, client)
                    _user.value = newUser
                    saveUserToCache(newUser)
                }

                _isAuth.value = true
            } catch (e: Exception) {
                _isAuth.value = false
                Log.e("AuthViewModel", "Ошибка проверки авторизации: ${e.message}")
            }
        }
    }

    /**
     * Получение данных пользователя из базы данных
     */
    private suspend fun fetchUserFromDatabase(userId: String): AppUser? {
        return try {
            Log.d("AuthViewModel", "📥 Запрос пользователя с ID: $userId")

            val response = databases.listDocuments(
                databaseId = DATABASE_ID,
                collectionId = COLLECTION_ID,
                queries = listOf(Query.equal("userId", userId))
            )

            val document = response.documents.firstOrNull()

            if (document == null) {
                Log.d("AuthViewModel", "❌ Пользователь не найден в базе")
                return null
            }

            Log.d("AuthViewModel", "✅ Найден документ: ${document.id}")
            Log.d("AuthViewModel", "📜 Данные документа: ${document.data}")

            // Проверяем, что именно приходит в "balance"
            val user = AppUser(
                id = document.data["\$id"] as String,
                name = document.data["name"]?.toString() ?: "",
                email = document.data["email"]?.toString() ?: "",
                description = document.data["description"]?.toString() ?: "",
                avatarUrl = document.data["avatarUrl"]?.toString() ?: "",
                rating = document.data["rating"]?.toString()?.toIntOrNull() ?: 1,
                balance = (document.data["balance"] as Long).toInt(),
                deviceIds = document.data["deviceIds"] as List<String>
            )

            Log.d("AuthViewModel", "✅ Итоговый объект AppUser: $user")

            withContext(Dispatchers.Main) {
                _user.value = user
            }

            user
        } catch (e: Exception) {
            Log.e("AuthViewModel", "❌ Ошибка получения данных: ${e.message}", e)
            null
        }
    }


    /**
     * Подписка на обновления данных пользователя в реальном времени
     */
    private fun subscribeToUserUpdates() {
        Log.d("AuthViewModel", "🔔 Подписка на обновления пользователя...")

        val subscription = realtime.subscribe(
            "databases.$DATABASE_ID.collections.$COLLECTION_ID.documents"
        ) { event ->
            viewModelScope.launch {
                try {
                    Log.d("AuthViewModel", "⚡ Изменения в БД: ${event.payload}")

                    val gson = Gson()
                    val jsonPayload = gson.toJson(event.payload)
                    val updatedUser = gson.fromJson(jsonPayload, AppUser::class.java)

                    if (updatedUser.id == _user.value?.id) {
                        _user.value = updatedUser
                        saveUserToCache(updatedUser)
                        Log.d("AuthViewModel", "✅ Данные пользователя обновлены!")
                    }
                } catch (e: Exception) {
                    Log.e("AuthViewModel", "❌ Ошибка при обработке обновлений: ${e.message}", e)
                }
            }
        }

        Log.d("AuthViewModel", "✅ Подписка создана: $subscription")
    }

    /**
     * Авторизация через Yandex OAuth
     */
    @SuppressLint("CommitPrefEdits")
    fun login(
        activity: ComponentActivity,
        onAuthSuccess: () -> Unit = {},
        onAuthFail: () -> Unit = {}
    ) {
        viewModelScope.launch {
            withContext(Dispatchers.IO) {
                try {
                    Log.d("AuthViewModel", "Начинаем OAuth авторизацию...")

                    // Создание OAuth2 сессии с Yandex
                    account.createOAuth2Session(activity, OAuthProvider.YANDEX)
                    Log.d("AuthViewModel", "OAuth сессия создана")

                    // Получаем данные пользователя
                    val userData = account.get()

                    // Проверяем, существует ли пользователь в базе данных
                    val dbUser = fetchUserFromDatabase(userData.id) ?: run {
                        Log.d("AuthViewModel", "Пользователь не найден в БД, создаем нового")

                        // Получаем FCM токен устройства
                        val deviceIds = try {
                            FirebaseMessaging.getInstance().token.await()
                        } catch (e: Exception) {
                            Log.e("AuthViewModel", "Ошибка получения FCM токена: ${e.message}", e)
                            "" // Возвращаем пустую строку в случае ошибки
                        }

                        val newUser = AppUser(
                            userData.id,
                            userData.name,
                            userData.email,
                            avatarUrl = "", // Здесь можно добавить URL аватара, если нужно
                            rating = 1,
                            deviceIds = listOfNotNull(deviceIds).filter { it.isNotEmpty() }
                        )

                        // Создаем пользователя в базе данных
                        createUser(newUser, client)
                        newUser
                    }
                    withContext(Dispatchers.Main){
                        // Устанавливаем пользователя в state
                        _user.value = dbUser
                    }

                    // Сохраняем пользователя в кэш
                    saveUserToCache(dbUser)
                    // Устанавливаем флаг успешной авторизации
                    withContext(Dispatchers.Main){
                        _isAuth.value = true
                        Log.d("AuthViewModel", "Авторизация успешна!")
                        // Вызов успешной авторизации
                        onAuthSuccess()
                    }

                } catch (e: Exception) {
                    Log.e("AuthViewModel", "Ошибка авторизации: ${e.message}", e)
                    // Если ошибка при авторизации, устанавливаем флаг неудачи
                    withContext(Dispatchers.Main) {
                        _isAuth.value = false
                        onAuthFail()
                    }
                }
            }
        }
    }

    fun logout(onLogoutSuccess: () -> Unit = {}, onLogoutFail: (Exception) -> Unit = {}) {
        viewModelScope.launch {
            try {
                account.deleteSession("current") // Завершаем текущую сессию
                _user.value = null
                _isAuth.value = false

                // Очистка данных из SharedPreferences
                val sharedPrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
                sharedPrefs.edit().remove(PREF_USER_KEY).apply()

                Log.d("AuthViewModel", "Выход выполнен успешно")
                onLogoutSuccess()
            } catch (e: Exception) {
                Log.e("AuthViewModel", "Ошибка при выходе: ${e.message}", e)
                onLogoutFail(e)
            }
        }
    }
}
Editor is loading...
Leave a Comment