'use client';
import React__default, { useMemo, useCallback, useRef, useEffect, useContext, createContext } from 'react';
import { isStytchSSRProxy } from '../bindings/StytchSSRProxy.mjs';
import { useAsyncState } from '../utils/async.mjs';
import { noProviderError, providerMustBeUniqueError } from '../utils/errors.mjs';
import { invariant } from '../utils/invariant.mjs';
import { mergeWithStableProps } from '../../../../../js-utils/src/mergeWithStableProps.mjs';

const initialUser = {
    user: null,
    fromCache: false,
    isInitialized: false
};
const initialSession = {
    session: null,
    fromCache: false,
    isInitialized: false
};
const StytchContext = /*#__PURE__*/ createContext({
    isMounted: false
});
const StytchUserContext = /*#__PURE__*/ createContext(initialUser);
const StytchSessionContext = /*#__PURE__*/ createContext(initialSession);
const useIsMounted__INTERNAL = ()=>useContext(StytchContext).isMounted;
/**
 * Returns the active User.
 * The Stytch SDKs are used for client-side authentication and session management.
 * Check the isInitialized property to determine if the SDK has completed initialization.
 * Check the fromCache property to determine if the user data is from persistent storage.
 * @example
 * const {user, isInitialized, fromCache} = useStytchUser();
 * if (!isInitialized) {
 *     return <p>Loading...</p>;
 * }
 * return (<h1>Welcome, {user.name.first_name}</h1>);
 */ const useStytchUser = ()=>{
    invariant(useIsMounted__INTERNAL(), noProviderError('useStytchUser'));
    return useContext(StytchUserContext);
};
/**
 * Returns the active user's Stytch session.
 * The Stytch SDKs are used for client-side authentication and session management.
 * Check the isInitialized property to determine if the SDK has completed initialization.
 * Check the fromCache property to determine if the session data is from persistent storage.
 * @example
 * const {session, isInitialized, fromCache} = useStytchSession();
 * useEffect(() => {
 *   if (!isInitialized) {
 *     return;
 *   }
 *   if (!session) {
 *     router.replace('/login')
 *   }
 * }, [session, isInitialized]);
 */ const useStytchSession = ()=>{
    invariant(useIsMounted__INTERNAL(), noProviderError('useStytchSession'));
    return useContext(StytchSessionContext);
};
/**
 * Returns the Stytch client stored in the Stytch context.
 *
 * @example
 * const stytch = useStytch();
 * useEffect(() => {
 *   stytch.magicLinks.authenticate('...')
 * }, [stytch]);
 */ const useStytch = ()=>{
    const ctx = useContext(StytchContext);
    invariant(ctx.isMounted, noProviderError('useStytch'));
    return ctx.client;
};
const withStytch = (Component)=>{
    const WithStytch = (props)=>{
        invariant(useIsMounted__INTERNAL(), noProviderError('withStytch'));
        return /*#__PURE__*/ React__default.createElement(Component, {
            ...props,
            stytch: useStytch()
        });
    };
    WithStytch.displayName = `withStytch(${Component.displayName || Component.name || 'Component'})`;
    return WithStytch;
};
const withStytchUser = (Component)=>{
    const WithStytchUser = (props)=>{
        invariant(useIsMounted__INTERNAL(), noProviderError('withStytchUser'));
        const { user, isInitialized, fromCache } = useStytchUser();
        return /*#__PURE__*/ React__default.createElement(Component, {
            ...props,
            stytchUser: user,
            stytchUserIsInitialized: isInitialized,
            stytchUserIsFromCache: fromCache
        });
    };
    WithStytchUser.displayName = `withStytchUser(${Component.displayName || Component.name || 'Component'})`;
    return WithStytchUser;
};
const withStytchSession = (Component)=>{
    const WithStytchSession = (props)=>{
        invariant(useIsMounted__INTERNAL(), noProviderError('withStytchSession'));
        const { session, isInitialized, fromCache } = useStytchSession();
        return /*#__PURE__*/ React__default.createElement(Component, {
            ...props,
            stytchSession: session,
            stytchSessionIsInitialized: isInitialized,
            stytchSessionIsFromCache: fromCache
        });
    };
    WithStytchSession.displayName = `withStytchSession(${Component.displayName || Component.name || 'Component'})`;
    return WithStytchSession;
};
/**
 * Determines whether the logged-in user is allowed to perform the specified action on the specified resource.
 * Returns `true` if the user can perform the action, `false` otherwise.
 *
 * If the user is not logged in, this method will always return false.
 * If the resource or action provided are not valid for the configured RBAC policy, this method will return false.
 *
 * Remember - authorization checks for sensitive actions should always occur on the backend as well.
 * @example
 * const { isAuthorized } = useStytchIsAuthorized('documents', 'edit');
 * return <button disabled={!isAuthorized}>Edit</button>
 */ const useStytchIsAuthorized = (resourceId, action)=>{
    invariant(useIsMounted__INTERNAL(), noProviderError('useStytchIsAuthorized', 'StytchProvider'));
    const client = useStytch();
    const { user } = useStytchUser();
    const [isAuthorized, setIsAuthorized] = useAsyncState({
        isInitialized: false,
        fromCache: false,
        isAuthorized: false
    });
    useEffect(()=>{
        if (isStytchSSRProxy(client)) {
            return;
        }
        setIsAuthorized({
            isInitialized: true,
            fromCache: true,
            isAuthorized: client.rbac.isAuthorizedSync(resourceId, action)
        });
    }, [
        action,
        client,
        resourceId,
        setIsAuthorized
    ]);
    useEffect(()=>{
        if (isStytchSSRProxy(client)) {
            return;
        }
        client.rbac.isAuthorized(resourceId, action).then((isAuthorized)=>{
            setIsAuthorized({
                isAuthorized,
                fromCache: false,
                isInitialized: true
            });
        });
    }, [
        client,
        user?.roles,
        resourceId,
        action,
        setIsAuthorized
    ]);
    return isAuthorized;
};
/**
 * Wrap your component with this HOC in order to receive the permissions for the logged-in user.
 * Evaluates all permissions granted to the logged-in user.
 * Returns a Record<RoleId, Record<Action, boolean>> response indicating the user's permissions.
 * Each boolean will be `true` if the user can perform the action, `false` otherwise.
 *
 * If the user is not logged in, all values will be false.
 *
 * Remember - authorization checks for sensitive actions should always occur on the backend as well.
 * @example
 * type Permissions = {
 *   document: 'create' | 'read' | 'write
 *   image: 'create' | 'read'
 * }
 *
 * const MyComponent = (props) => {
 *   const canEditDocuments = props.stytchPermissions.document.edit;
 *   const canReadImages = props.stytchPermissions.image.read;
 * }
 * return withStytchPermissions<Permissions>(MyComponent)
 */ const withStytchPermissions = (Component)=>{
    const WithStytchPermissions = (props)=>{
        invariant(useIsMounted__INTERNAL(), noProviderError('useRBACPermissions', 'StytchProvider'));
        const client = useStytch();
        const { user } = useStytchUser();
        const [permissions, setPermissions] = useAsyncState({
            loaded: false,
            value: null
        });
        useEffect(()=>{
            client.rbac.allPermissions().then((permissions)=>{
                setPermissions({
                    loaded: true,
                    value: permissions
                });
            });
        }, [
            client,
            user?.roles,
            setPermissions
        ]);
        if (!permissions.loaded) {
            return null;
        }
        return /*#__PURE__*/ React__default.createElement(Component, {
            ...props,
            stytchPermissions: permissions.value
        });
    };
    WithStytchPermissions.displayName = `withStytchPermissions(${Component.displayName || Component.name || 'Component'})`;
    return WithStytchPermissions;
};
/**
 * The Stytch Context Provider.
 * Wrap your application with this component in order to use Stytch everywhere in your app.
 * @example
 * const stytch = createStytchClient('public-token-<find yours in the stytch dashboard>')
 *
 * return (
 *   <StytchProvider stytch={stytch}>
 *     <App />
 *   </StytchProvider>
 * )
 */ const StytchProvider = ({ stytch, children, assumeHydrated = false })=>{
    invariant(!useIsMounted__INTERNAL(), providerMustBeUniqueError);
    invariant(!assumeHydrated || typeof window !== 'undefined', 'The `assumeHydrated` prop must be set to `false` when using StytchProvider in a server environment.');
    const ctx = useMemo(()=>({
            client: stytch,
            isMounted: true
        }), [
        stytch
    ]);
    const getHydratedState = useCallback(()=>{
        return {
            session: {
                ...stytch.session.getInfo(),
                isInitialized: true
            },
            user: {
                ...stytch.user.getInfo(),
                isInitialized: true
            }
        };
    }, [
        stytch
    ]);
    const getInitialState = ()=>{
        return {
            session: initialSession,
            user: initialUser
        };
    };
    const [{ user, session }, setClientState] = useAsyncState(()=>assumeHydrated ? getHydratedState() : getInitialState());
    // Store the initial value of `assumeHydrated` in a ref, because it is
    // logically only relevant for the first render
    const assumeHydratedRef = useRef(assumeHydrated);
    useEffect(()=>{
        if (isStytchSSRProxy(stytch)) {
            return;
        }
        const updateState = ()=>{
            setClientState((oldState)=>mergeWithStableProps(oldState, getHydratedState()));
        };
        if (!assumeHydratedRef.current) {
            updateState();
        }
        return stytch.onStateChange(updateState);
    }, [
        getHydratedState,
        setClientState,
        stytch
    ]);
    return /*#__PURE__*/ React__default.createElement(StytchContext.Provider, {
        value: ctx
    }, /*#__PURE__*/ React__default.createElement(StytchUserContext.Provider, {
        value: user
    }, /*#__PURE__*/ React__default.createElement(StytchSessionContext.Provider, {
        value: session
    }, children)));
};

export { StytchProvider, useIsMounted__INTERNAL, useStytch, useStytchIsAuthorized, useStytchSession, useStytchUser, withStytch, withStytchPermissions, withStytchSession, withStytchUser };
//# sourceMappingURL=StytchContext.mjs.map
