package com.notifyhub.viewer.data import com.google.gson.Gson import com.notifyhub.viewer.data.prefs.SecurePreferences import com.notifyhub.viewer.data.remote.ApiErrorParser import com.notifyhub.viewer.data.remote.LoginRequest import com.notifyhub.viewer.data.remote.LoginResponse import com.notifyhub.viewer.data.remote.NotizApi import com.notifyhub.viewer.data.remote.Permissions import com.notifyhub.viewer.data.remote.ProfileResponse import com.notifyhub.viewer.data.remote.PushTokenRequest import com.notifyhub.viewer.data.remote.TwoFactorResendRequest import com.notifyhub.viewer.data.remote.TwoFactorVerifyRequest import retrofit2.Response /** * Wraps API calls and persists session state into SecurePreferences. Exposes a * small set of sealed results so the UI can branch on 2FA, maintenance, and * access-denied states without touching Retrofit directly. */ class AuthRepository( private val api: NotizApi, private val prefs: SecurePreferences, private val deviceName: String, ) { private val gson = Gson() sealed class LoginOutcome { data class Success(val profile: ProfileResponse) : LoginOutcome() data class TwoFactorRequired(val challengeId: String, val message: String) : LoginOutcome() data class Failed(val message: String, val code: String? = null) : LoginOutcome() } sealed class VerifyOutcome { data class Success(val profile: ProfileResponse) : VerifyOutcome() data class Failed(val message: String) : VerifyOutcome() } suspend fun login(login: String, password: String, companyNumber: String): LoginOutcome { val response = api.login(LoginRequest(login.trim(), password, companyNumber.trim(), deviceName)) return handleLogin(response) } suspend fun verify(challengeId: String, code: String): VerifyOutcome { val response = api.verifyTwoFactor(TwoFactorVerifyRequest(challengeId, code.trim(), deviceName)) if (response.isSuccessful) { val body = response.body()!! storeSession(body) return VerifyOutcome.Success(body.profile!!) } return VerifyOutcome.Failed(ApiErrorParser.message(response)) } suspend fun resend(challengeId: String): String? { val response = api.resendTwoFactor(TwoFactorResendRequest(challengeId)) if (!response.isSuccessful) return ApiErrorParser.message(response) return null } suspend fun logout() { runCatching { api.unregisterPushToken(prefs.pushToken ?: "") } runCatching { api.logout() } prefs.clearSession() } suspend fun registerPushToken(token: String): Boolean { prefs.pushToken = token if (!prefs.isLoggedIn()) return false val response = api.registerPushToken(PushTokenRequest("android", token)) return response.isSuccessful } suspend fun refreshProfile(): ProfileResponse? { val response = api.profile() if (response.isSuccessful) { val profile = response.body()!! storeProfile(profile) return profile } return null } private fun handleLogin(response: Response): LoginOutcome { if (response.isSuccessful) { val body = response.body()!! if (body.code == "two_factor_required" && body.challengeId != null) { return LoginOutcome.TwoFactorRequired(body.challengeId, body.message ?: "Enter the code sent to your email.") } storeSession(body) return LoginOutcome.Success(body.profile!!) } val code = ApiErrorParser.code(response) val message = ApiErrorParser.message(response) return LoginOutcome.Failed(message, code) } private fun storeSession(body: LoginResponse) { prefs.apiToken = body.token body.profile?.let { storeProfile(it) } } private fun storeProfile(profile: ProfileResponse) { prefs.profileJson = gson.toJson(profile) prefs.permissionsJson = gson.toJson(profile.permissions) prefs.applyBranding(profile.branding) prefs.applyCompany(profile.company) } }