package dev.ulfrx.recipe.auth

import dev.lokksmith.Lokksmith

/**
 * Common Authentik OIDC client built on Lokksmith.
 *
 * Lokksmith owns PKCE, state, nonce, token storage, refresh, and end-session
 * (D-06, D-16, D-19, D-20). This class only orchestrates: build the flow request
 * and hand its [dev.lokksmith.client.request.flow.AuthFlow.Initiation] to the
 * caller-supplied [AuthBrowser] (Lokksmith's `rememberAuthFlowLauncher` on
 * mobile; a fake in tests), then map the terminal result.
 *
 * Logout still clears local state if remote end-session fails so users are never
 * trapped in a stale session.
 */
class OidcClient(
    private val lokksmith: Lokksmith,
) {
    suspend fun login(browser: AuthBrowser): OidcResult {
        val client = lokksmith.recipeClient()
        val flow = client.recipeAuthorizationCodeFlow()

        return when (val failure = browser.launchAndAwait(flow.prepare()).toOidcFailureOrNull()) {
            null -> {
                runCatching { client.toOidcSuccess() }
                    .getOrElse { OidcResult.AuthError(it.message ?: "OIDC login failed", it) }
            }

            else -> {
                failure
            }
        }
    }

    suspend fun refresh(authStateJson: String): OidcResult {
        if (authStateJson != LOKKSMITH_AUTH_STATE_MARKER) {
            return OidcResult.AuthError("Stored auth state is not a Lokksmith session")
        }
        val client = lokksmith.recipeClient()
        return runCatching { client.toOidcSuccess() }
            .getOrElse { OidcResult.AuthError(it.message ?: "OIDC refresh failed", it) }
    }

    suspend fun logout(
        authStateJson: String,
        browser: AuthBrowser,
    ) {
        val client = lokksmith.recipeClient()
        val flow = client.recipeEndSessionFlow()

        if (flow != null) {
            runCatching { browser.launchAndAwait(flow.prepare()) }
        }

        client.resetTokens()
    }
}
