/**
 * android-keystore-token-store.ts
 *
 * Token store backed by `react-native-keychain` for bare React Native on Android.
 *
 * On Android, `react-native-keychain` routes to EncryptedSharedPreferences
 * backed by the Android Keystore system — hardware-backed AES-256-GCM
 * encryption where available (API 23+). This is the **recommended**
 * implementation for proper hardware-backed security.
 *
 * Alternative: `@react-native-async-storage/async-storage` can store the
 * token but is NOT hardware-backed. If you cannot use `react-native-keychain`,
 * wrap async-storage with an additional encryption layer (e.g.
 * `react-native-encrypted-storage`). The `react-native-keychain` path is
 * strongly preferred for production apps.
 *
 * `react-native-keychain` is an **optional peer dependency** — this module
 * does NOT import it at the top level.
 *
 * ## Installation
 *
 * ```sh
 * npm install react-native-keychain
 * # Android: auto-linked via Gradle; no manual step required
 * ```
 *
 * Error messages use the format: [what happened] · [why] · [what to do]
 */
import { GoodVibesSdkError } from '@pellux/goodvibes-errors';
const REACT_NATIVE_KEYCHAIN_MODULE = 'react-native-keychain';
let _mod = null;
async function loadKeychain() {
    if (_mod !== null)
        return _mod;
    try {
        _mod = (await import(REACT_NATIVE_KEYCHAIN_MODULE));
        return _mod;
    }
    catch {
        throw new GoodVibesSdkError('react-native-keychain is not installed — the Android Keystore token store cannot be initialised. ' +
            'This optional peer dependency is required to persist tokens in Android Keystore-backed storage. ' +
            'Run `npm install react-native-keychain` and rebuild your app.', {
            code: 'RN_KEYCHAIN_NOT_INSTALLED',
            category: 'config',
            source: 'config',
            recoverable: false,
        });
    }
}
// ---------------------------------------------------------------------------
// Fixed username slot
// ---------------------------------------------------------------------------
const USERNAME_SLOT = 'goodvibes-sdk';
// ---------------------------------------------------------------------------
// Factory
// ---------------------------------------------------------------------------
/**
 * Create a `GoodVibesTokenStore` backed by the Android Keystore system via
 * `react-native-keychain`.
 *
 * On Android, `react-native-keychain` uses `EncryptedSharedPreferences` backed
 * by the Android Keystore — hardware-backed AES-256-GCM encryption where
 * supported (API 23+). This is strongly preferred over plain AsyncStorage.
 *
 * Pass `accessControl: 'BIOMETRY_ANY'` or `'DEVICE_PASSCODE'` to require
 * interactive user authentication before reading the stored credential.
 *
 * `react-native-keychain` is an **optional peer dependency** — install it with:
 *
 * ```sh
 * npm install react-native-keychain
 * # No manual Gradle/CocoaPods step required for Android
 * ```
 *
 * @example
 * ```ts
 * import { createAndroidKeystoreTokenStore, createReactNativeGoodVibesSdk } from '@pellux/goodvibes-sdk/react-native';
 *
 * const tokenStore = createAndroidKeystoreTokenStore({
 *   service: 'com.myapp.gv',
 *   accessControl: 'BIOMETRY_ANY',
 * });
 * const sdk = createReactNativeGoodVibesSdk({ baseUrl: 'https://daemon.example.com', tokenStore });
 * ```
 */
export function createAndroidKeystoreTokenStore(options = {}, __loadModule) {
    const service = options.service ?? 'com.pellux.goodvibes-sdk';
    const accessible = options.accessible ?? 'WHEN_UNLOCKED_THIS_DEVICE_ONLY';
    const accessControl = options.accessControl;
    async function resolveModule() {
        if (__loadModule !== undefined) {
            return __loadModule();
        }
        return loadKeychain();
    }
    function buildOptions(mod) {
        const opts = { service };
        const accessibleValue = mod.ACCESSIBLE[accessible];
        if (accessibleValue !== undefined) {
            opts.accessible = accessibleValue;
        }
        else {
            throw new Error(`react-native-keychain does not expose ACCESSIBLE.${accessible}; choose a supported keychain accessibility constant.`);
        }
        if (accessControl !== undefined) {
            const acValue = mod.ACCESS_CONTROL[accessControl];
            if (acValue !== undefined) {
                opts.accessControl = acValue;
            }
            else {
                throw new Error(`react-native-keychain does not expose ACCESS_CONTROL.${accessControl}; choose a supported keychain access-control constant.`);
            }
        }
        return opts;
    }
    async function readPayload() {
        const mod = await resolveModule();
        const result = await mod.getGenericPassword(buildOptions(mod));
        if (result === false)
            return null;
        try {
            return JSON.parse(result.password);
        }
        catch (err) {
            await mod.resetGenericPassword(buildOptions(mod));
            throw new GoodVibesSdkError('Android keystore token store payload is corrupt and was cleared.', {
                code: 'SDK_TOKEN_STORE_CORRUPT',
                category: 'authentication',
                source: 'runtime',
                recoverable: true,
                cause: err,
            });
        }
    }
    async function writePayload(payload) {
        const mod = await resolveModule();
        if (payload === null) {
            await mod.resetGenericPassword(buildOptions(mod));
            return;
        }
        await mod.setGenericPassword(USERNAME_SLOT, JSON.stringify(payload), buildOptions(mod));
    }
    return {
        async getToken() {
            const payload = await readPayload();
            return payload?.token ?? null;
        },
        async setToken(token) {
            if (token === null) {
                await writePayload(null);
                return;
            }
            await writePayload({ token, expiresAt: null });
        },
        async clearToken() {
            await writePayload(null);
        },
        async setTokenEntry(token, expiresAt) {
            if (token === null) {
                await writePayload(null);
                return;
            }
            await writePayload({ token, expiresAt: expiresAt ?? null });
        },
        async getTokenEntry() {
            const payload = await readPayload();
            if (payload === null)
                return { token: null };
            const expiresAt = payload.expiresAt ?? undefined;
            return expiresAt !== undefined ? { token: payload.token, expiresAt } : { token: payload.token };
        },
    };
}
