package sh.sar.basedbank import android.app.Application import androidx.appcompat.app.AppCompatDelegate import com.google.android.material.color.DynamicColors import kotlinx.coroutines.sync.Mutex import sh.sar.basedbank.api.bml.BmlLoginFlow import sh.sar.basedbank.api.bml.BmlProfile import sh.sar.basedbank.api.bml.BmlSession import sh.sar.basedbank.api.fahipay.FahipaySession import sh.sar.basedbank.api.models.BankAccount import sh.sar.basedbank.api.mib.MibLoginFlow import sh.sar.basedbank.api.mib.MibProfile import sh.sar.basedbank.api.mib.MibSession import sh.sar.basedbank.util.CredentialStore class BasedBankApp : Application() { // Held in memory after successful login; cleared on logout var accounts: List = emptyList() var fullName: String = "" /** Active MIB sessions keyed by loginId (= MIB username). */ val mibSessions: MutableMap = mutableMapOf() val mibProfilesMap: MutableMap> = mutableMapOf() val mibLoginFlows: MutableMap = mutableMapOf() var mibAccounts: List = emptyList() /** * Active BML sessions keyed by profileId (a globally unique GUID per BML profile). * Use [bmlSessionFor] to look up the session for an account. */ val bmlSessions: MutableMap = mutableMapOf() /** BML profiles per loginId (= BML username). */ val bmlProfilesMap: MutableMap> = mutableMapOf() /** BML login flows per loginId — hold the web session (cookies) needed for profile activation. */ val bmlLoginFlows: MutableMap = mutableMapOf() var bmlAccounts: List = emptyList() /** Active Fahipay sessions keyed by loginId (= profileId). */ val fahipaySessions: MutableMap = mutableMapOf() var fahipayAccounts: List = emptyList() // ─── MIB helpers ────────────────────────────────────────────────────────── /** Returns the MIB session for the given account (matched via loginTag). */ fun mibSessionFor(account: BankAccount): MibSession? = mibSessions[account.loginTag.removePrefix("mib_")] /** Returns any available MIB session. */ fun anyMibSession(): MibSession? = mibSessions.values.firstOrNull() /** Returns all MIB profiles across all logins. */ fun allMibProfiles(): List = mibProfilesMap.values.flatten() /** Returns the MibLoginFlow for a given loginId, creating and caching it if needed. */ fun mibFlowFor(loginId: String): MibLoginFlow = mibLoginFlows.getOrPut(loginId) { MibLoginFlow(CredentialStore(this)).also { flow -> flow.onSessionRefreshed = { session, profiles -> mibSessions[loginId] = session mibProfilesMap[loginId] = profiles } } } /** Returns any available MibLoginFlow. */ fun anyMibFlow(): MibLoginFlow? = mibLoginFlows.values.firstOrNull() // ─── BML helpers ────────────────────────────────────────────────────────── /** * Returns the BML session for the given account. * Looks up by profileId first (multi-profile), falls back to loginId (legacy single-profile). */ fun bmlSessionFor(account: BankAccount): BmlSession? { val byProfile = if (account.profileId.isNotBlank()) bmlSessions[account.profileId] else null return byProfile ?: bmlSessions[account.loginTag.removePrefix("bml_")] } /** Returns any available BML session (for non-account-specific operations). */ fun anyBmlSession(): BmlSession? = bmlSessions.values.firstOrNull() /** * Returns any active BML session for the given loginId. * Tries all profiles for that login; falls back to legacy loginId key. */ fun anyBmlSessionFor(loginId: String): BmlSession? { val profiles = bmlProfilesMap[loginId] if (!profiles.isNullOrEmpty()) { return profiles.firstNotNullOfOrNull { bmlSessions[it.profileId] } } return bmlSessions[loginId] } /** Returns the BmlLoginFlow for a given loginId, creating and caching it if needed. */ fun bmlFlowFor(loginId: String): BmlLoginFlow = bmlLoginFlows.getOrPut(loginId) { BmlLoginFlow() } // ─── Fahipay helpers ────────────────────────────────────────────────────── /** Returns the Fahipay session for the given account (matched via loginTag = "fahipay_${profileId}"). */ fun fahipaySessionFor(account: BankAccount): FahipaySession? = fahipaySessions[account.loginTag.removePrefix("fahipay_")] /** Serialises all MIB profile-switch + request sequences to prevent session corruption. */ val mibMutex = Mutex() override fun onCreate() { super.onCreate() DynamicColors.applyToActivitiesIfAvailable(this) val theme = getSharedPreferences("prefs", MODE_PRIVATE).getString("theme", "system") AppCompatDelegate.setDefaultNightMode(when (theme) { "dark" -> AppCompatDelegate.MODE_NIGHT_YES "light" -> AppCompatDelegate.MODE_NIGHT_NO else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM }) } }