import type {
AccessControl,
SecurityAvailability,
} from '../sensitive-info.nitro'
import { getSupportedSecurityLevels } from './storage'
/**
* Pure mapping table: which capability does each {@link AccessControl} policy require?
*
* Kept as a `const` literal lookup (not a `switch`, not a `Map`) so the minifier can constant-fold
* it and dead-code-eliminate unused branches. Internal helper — not exported from the public API.
*
* @internal
*/
const POLICY_PREDICATES: {
readonly [P in AccessControl]: (levels: SecurityAvailability) => boolean
} = {
secureEnclaveBiometry: (l) =>
(l.secureEnclave || l.strongBox) && l.biometryStatus === 'available',
biometryCurrentSet: (l) => l.biometryStatus === 'available',
biometryAny: (l) => l.biometryStatus === 'available',
devicePasscode: (l) => l.deviceCredential,
none: () => true,
}
/**
* Synchronous variant of {@link canUseAccessControl} for callers that already hold a
* {@link SecurityAvailability} snapshot (e.g. inside a render that consumes
* {@link useSecurityAvailability}).
*
* Pure function — no native call, no IPC round-trip. Safe to call inside React render paths.
*
* @param policy - The {@link AccessControl} policy you intend to write with.
* @param levels - Capability snapshot, typically from {@link getSupportedSecurityLevels} or
* {@link useSecurityAvailability}.
* @returns `true` when the device currently satisfies the policy's requirements; `false` when a
* write would fail or be silently downgraded.
*
* @example
* ```tsx
* const { data: caps } = useSecurityAvailability()
* const canEnable = caps ? canUseAccessControlSync('secureEnclaveBiometry', caps) : false
* return
* ```
*
* @see {@link canUseAccessControl}
* @public
*/
export function canUseAccessControlSync(
policy: AccessControl,
levels: SecurityAvailability
): boolean {
return POLICY_PREDICATES[policy](levels)
}
/**
* Predicts whether a given {@link AccessControl} policy can be satisfied on the current device
* **right now** — useful for gating biometric toggles before attempting a write that would
* otherwise fail or be silently downgraded.
*
* Internally maps the requested policy onto the {@link SecurityAvailability} snapshot returned by
* {@link getSupportedSecurityLevels}. Pass `levels` to skip the native round-trip.
*
* @param policy - The {@link AccessControl} policy you intend to write with.
* @param levels - Optional pre-fetched capability snapshot. When omitted, the helper calls
* {@link getSupportedSecurityLevels} internally.
* @returns Resolves to `true` when the policy can be applied, `false` otherwise. Resolves to
* `false` (rather than throwing) for `'unknown'` biometry status — gate UI off availability
* instead.
*
* @remarks
* This is a *predictive* check, not a guarantee — the user could change biometric settings
* between this call and the subsequent write. Always handle {@link KeyInvalidatedError} and
* {@link AuthenticationCanceledError} on the write path as well.
*
* @example
* ```ts
* if (await canUseAccessControl('secureEnclaveBiometry')) {
* await setItem('session', token, { accessControl: 'secureEnclaveBiometry' })
* } else {
* await setItem('session', token, { accessControl: 'devicePasscode' })
* }
* ```
*
* @see {@link canUseAccessControlSync}
* @see {@link getSupportedSecurityLevels}
* @public
*/
export async function canUseAccessControl(
policy: AccessControl,
levels?: SecurityAvailability
): Promise {
const snapshot = levels ?? (await getSupportedSecurityLevels())
return canUseAccessControlSync(policy, snapshot)
}