import { jsx as _jsx } from "react/jsx-runtime";
import { createContext, useContext, useState, useCallback, useRef, useEffect, useMemo } from 'react';
import { Platform, AppState } from 'react-native';
import { PersSDK } from '@explorins/pers-sdk/core';
import { ReactNativeHttpClient } from './react-native-http-client';
import { createReactNativeAuthProvider } from './react-native-auth-provider';
import { ReactNativeDPoPProvider } from './rn-dpop-provider';
// Create the context
const SDKContext = createContext(null);
/**
 * PERS SDK Provider for React Native
 *
 * Wraps your app to provide SDK context to all child components.
 * Handles platform-specific initialization (DPoP, storage, etc.).
 *
 * @param config - SDK configuration (see PersConfig)
 * @param config.apiProjectKey - Your PERS project key (required)
 * @param config.environment - 'staging' | 'production' (default: 'staging')
 * @param config.captureWalletEvents - Enable real-time blockchain events (default: true)
 * @param config.dpop - DPoP configuration for enhanced security
 *
 * @example Basic usage
 * ```tsx
 * <PersSDKProvider config={{ apiProjectKey: 'my-project' }}>
 *   <App />
 * </PersSDKProvider>
 * ```
 *
 * @example Disable wallet events
 * ```tsx
 * <PersSDKProvider config={{
 *   apiProjectKey: 'my-project',
 *   captureWalletEvents: false  // Disable auto-connect to blockchain events
 * }}>
 *   <App />
 * </PersSDKProvider>
 * ```
 */
export const PersSDKProvider = ({ children, config }) => {
    const initializingRef = useRef(false);
    const eventUnsubscribeRef = useRef(null);
    // State refs for stable functions to read current values
    const sdkRef = useRef(null);
    const isInitializedRef = useRef(false);
    const isAuthenticatedRef = useRef(false);
    const [sdk, setSdk] = useState(null);
    const [authProvider, setAuthProvider] = useState(null);
    const [isInitialized, setIsInitialized] = useState(false);
    const [isAuthenticated, setIsAuthenticated] = useState(false);
    const [user, setUser] = useState(null);
    // Keep state refs in sync immediately (not in useEffect to avoid race conditions)
    sdkRef.current = sdk;
    isInitializedRef.current = isInitialized;
    isAuthenticatedRef.current = isAuthenticated;
    const initialize = useCallback(async (config) => {
        // Prevent multiple initializations
        if (isInitialized || initializingRef.current) {
            return;
        }
        initializingRef.current = true;
        try {
            // Create HTTP client
            const httpClient = new ReactNativeHttpClient();
            const dpopConfig = config.dpop ?? {};
            const isDpopEnabled = dpopConfig.enabled ?? true;
            // Prepare DPoP settings for Auth Provider
            const dpopSettings = {
                enabled: isDpopEnabled,
                cryptoProvider: dpopConfig.cryptoProvider
            };
            // Inject Native DPoP Provider (crypto-based) for mobile platforms if not already provided
            if (Platform.OS !== 'web' && isDpopEnabled && !dpopSettings.cryptoProvider) {
                dpopSettings.cryptoProvider = new ReactNativeDPoPProvider();
            }
            // Create platform-aware auth provider if not provided
            let authProvider;
            if (config.authProvider) {
                authProvider = config.authProvider;
            }
            else {
                // Use our platform-aware auth provider that automatically selects LocalStorage (web) or AsyncStorage (mobile)
                authProvider = createReactNativeAuthProvider(config.apiProjectKey || 'default-project', {
                    debug: false,
                    dpop: dpopSettings
                });
            }
            // Enhanced config with platform-appropriate auth provider
            const sdkConfig = {
                ...config,
                authProvider,
                dpop: dpopSettings
            };
            // Initialize PersSDK with platform-aware auth provider
            const sdkInstance = new PersSDK(httpClient, sdkConfig);
            // Subscribe to auth events BEFORE exposing SDK to consumers
            // This ensures no events are missed due to React timing
            eventUnsubscribeRef.current = sdkInstance.events.subscribe((event) => {
                if (event.type === 'session_restored' || event.type === 'login_success') {
                    const details = event.details;
                    if (details?.user) {
                        setUser(details.user);
                        setIsAuthenticated(true);
                    }
                }
                if (event.type === 'logout_success' || event.type === 'session_restoration_failed' || event.code === 'AUTH_FAILED') {
                    setUser(null);
                    setIsAuthenticated(false);
                }
            }, { domains: ['authentication'], replay: true });
            setAuthProvider(authProvider);
            setSdk(sdkInstance);
            setIsInitialized(true);
        }
        catch (error) {
            console.error('Failed to initialize PERS SDK:', error);
            initializingRef.current = false;
            throw error;
        }
        finally {
            initializingRef.current = false;
        }
    }, [isInitialized]);
    // Cleanup subscription on unmount
    useEffect(() => {
        return () => {
            if (eventUnsubscribeRef.current) {
                eventUnsubscribeRef.current();
                eventUnsubscribeRef.current = null;
            }
        };
    }, []);
    const setAuthenticationState = useCallback((user, isAuthenticated) => {
        setUser(user);
        setIsAuthenticated(isAuthenticated);
    }, []);
    // Auto-initialize if config is provided
    useEffect(() => {
        if (config && !isInitialized && !initializingRef.current) {
            initialize(config).catch(err => {
                console.error('Auto-initialization failed:', err);
            });
        }
    }, [config, isInitialized, initialize]);
    const refreshUserData = useCallback(async () => {
        // Read from refs to get current values
        const currentSdk = sdkRef.current;
        const currentIsInitialized = isInitializedRef.current;
        if (!currentSdk || !currentIsInitialized) {
            throw new Error('SDK not initialized. Cannot refresh user data.');
        }
        try {
            const freshUserData = await currentSdk.users.getCurrentUser();
            setUser(freshUserData);
        }
        catch (error) {
            console.error('Failed to refresh user data:', error);
            throw error;
        }
    }, []); // No dependencies - reads from refs
    const restoreSession = useCallback(async () => {
        // Read from refs to get current values
        const currentSdk = sdkRef.current;
        const currentIsInitialized = isInitializedRef.current;
        if (!currentSdk || !currentIsInitialized) {
            throw new Error('SDK not initialized. Call initialize() first.');
        }
        try {
            const userData = await currentSdk.restoreSession();
            if (userData) {
                setAuthenticationState(userData, true);
            }
            return userData;
        }
        catch (error) {
            console.error('[PersSDK] Failed to restore session:', error);
            throw error;
        }
    }, [setAuthenticationState]);
    // iOS/Android: Monitor app state and validate tokens when app becomes active
    useEffect(() => {
        if (!sdk || Platform.OS === 'web') {
            return;
        }
        const handleAppStateChange = async (nextAppState) => {
            if (nextAppState === 'active' && isAuthenticated) {
                try {
                    // Trigger token validation when app resumes from background
                    // This ensures tokens are checked after extended inactivity
                    await sdk.auth.ensureValidToken();
                }
                catch (error) {
                    console.warn('[PersSDK] Token validation failed on app resume:', error);
                }
            }
        };
        const subscription = AppState.addEventListener('change', handleAppStateChange);
        return () => {
            subscription?.remove();
        };
    }, [sdk, isAuthenticated]);
    const contextValue = useMemo(() => ({
        // Main SDK instance
        sdk,
        // Platform-specific providers
        authProvider,
        // State
        isInitialized,
        isAuthenticated,
        user,
        // Methods - expose functions directly, not through refs
        initialize,
        setAuthenticationState,
        refreshUserData,
        restoreSession,
    }), [
        sdk,
        authProvider,
        isInitialized,
        isAuthenticated,
        user,
        initialize,
        setAuthenticationState,
        refreshUserData,
        restoreSession
    ]);
    return (_jsx(SDKContext.Provider, { value: contextValue, children: children }));
};
// Custom hook to use the SDK context
export const usePersSDK = () => {
    const context = useContext(SDKContext);
    if (!context) {
        throw new Error('usePersSDK must be used within a PersSDKProvider');
    }
    return context;
};
