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 } from '../utils/errors.mjs';
import { invariant } from '../utils/invariant.mjs';
import { mergeWithStableProps } from '../../../../../js-utils/src/mergeWithStableProps.mjs';

const initialMember = {
    member: null,
    fromCache: false,
    isInitialized: false
};
const initialMemberSession = {
    session: null,
    fromCache: false,
    isInitialized: false
};
const initialOrganization = {
    organization: null,
    fromCache: false,
    isInitialized: false
};
const StytchB2BContext = /*#__PURE__*/ createContext({
    isMounted: false
});
const StytchMemberContext = /*#__PURE__*/ createContext(initialMember);
const StytchMemberSessionContext = /*#__PURE__*/ createContext(initialMemberSession);
const StytchOrganizationContext = /*#__PURE__*/ createContext(initialOrganization);
const useIsMounted__INTERNAL = ()=>useContext(StytchB2BContext).isMounted;
/**
 * Returns the active Member.
 * 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 member data is from persistent storage.
 * @example
 * const {member, isInitialized, fromCache} = useStytchMember();
 * if (!isInitialized) {
 *     return <p>Loading...</p>;
 * }
 * return (<h1>Welcome, {member.name}</h1>);
 */ const useStytchMember = ()=>{
    invariant(useIsMounted__INTERNAL(), noProviderError('useStytchMember', 'StytchB2BProvider'));
    return useContext(StytchMemberContext);
};
/**
 * Returns the active member's Stytch member 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} = useStytchMemberSession();
 * useEffect(() => {
 *   if (!isInitialized) {
 *     return;
 *   }
 *   if (!session) {
 *     router.replace('/login')
 *   }
 * }, [session, isInitialized]);
 */ const useStytchMemberSession = ()=>{
    invariant(useIsMounted__INTERNAL(), noProviderError('useStytchMemberSession', 'StytchB2BProvider'));
    return useContext(StytchMemberSessionContext);
};
/**
 * Returns the active Stytch organization.
 * 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 organization data is from persistent storage.
 * @example
 * const {organization, isInitialized, fromCache} = useStytchOrganization();
 * if (!isInitialized) {
 *     return <p>Loading...</p>;
 * }
 * return (<p>Welcome to {organization.organization_name}</p>);
 */ const useStytchOrganization = ()=>{
    invariant(useIsMounted__INTERNAL(), noProviderError('useStytchOrganization', 'StytchB2BProvider'));
    return useContext(StytchOrganizationContext);
};
/**
 * Determines whether the logged-in member is allowed to perform the specified action on the specified resource.
 * Returns `true` if the member can perform the action, `false` otherwise.
 *
 * If the member 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', 'StytchB2BProvider'));
    const client = useStytchB2BClient();
    const { session } = useStytchMemberSession();
    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,
        session?.roles,
        resourceId,
        action,
        setIsAuthorized
    ]);
    return isAuthorized;
};
/**
 * Returns the Stytch B2B client stored in the Stytch context.
 *
 * @example
 * const stytch = useStytchB2BClient();
 * useEffect(() => {
 *   stytch.magicLinks.authenticate('...')
 * }, [stytch]);
 */ const useStytchB2BClient = ()=>{
    const ctx = useContext(StytchB2BContext);
    invariant(ctx.isMounted, noProviderError('useStytchB2BClient', 'StytchB2BProvider'));
    return ctx.client;
};
const withStytchB2BClient = (Component)=>{
    const WithStytch = (props)=>{
        invariant(useIsMounted__INTERNAL(), noProviderError('withStytchB2BClient', 'StytchB2BProvider'));
        return /*#__PURE__*/ React__default.createElement(Component, {
            ...props,
            stytch: useStytchB2BClient()
        });
    };
    WithStytch.displayName = `withStytch(${Component.displayName || Component.name || 'Component'})`;
    return WithStytch;
};
const withStytchMember = (Component)=>{
    const WithStytchUser = (props)=>{
        invariant(useIsMounted__INTERNAL(), noProviderError('withStytchMember', 'StytchB2BProvider'));
        const { member, isInitialized, fromCache } = useStytchMember();
        return /*#__PURE__*/ React__default.createElement(Component, {
            ...props,
            stytchMember: member,
            stytchMemberIsInitialized: isInitialized,
            stytchMemberIsFromCache: fromCache
        });
    };
    WithStytchUser.displayName = `withStytchMember(${Component.displayName || Component.name || 'Component'})`;
    return WithStytchUser;
};
const withStytchMemberSession = (Component)=>{
    const WithStytchSession = (props)=>{
        invariant(useIsMounted__INTERNAL(), noProviderError('withStytchMemberSession', 'StytchB2BProvider'));
        const { session, isInitialized, fromCache } = useStytchMemberSession();
        return /*#__PURE__*/ React__default.createElement(Component, {
            ...props,
            stytchMemberSession: session,
            stytchMemberSessionIsInitialized: isInitialized,
            stytchMemberSessionIsFromCache: fromCache
        });
    };
    WithStytchSession.displayName = `withStytchMemberSession(${Component.displayName || Component.name || 'Component'})`;
    return WithStytchSession;
};
const withStytchOrganization = (Component)=>{
    const WithStytchOrganization = (props)=>{
        invariant(useIsMounted__INTERNAL(), noProviderError('withStytchOrganization', 'StytchB2BProvider'));
        const { organization, isInitialized, fromCache } = useStytchOrganization();
        return /*#__PURE__*/ React__default.createElement(Component, {
            ...props,
            stytchOrganization: organization,
            stytchOrganizationIsInitialized: isInitialized,
            stytchOrganizationIsFromCache: fromCache
        });
    };
    WithStytchOrganization.displayName = `withStytchOrganization(${Component.displayName || Component.name || 'Component'})`;
    return WithStytchOrganization;
};
/**
 * Wrap your component with this HOC in order to receive the permissions for the logged-in member.
 * Evaluates all permissions granted to the logged-in member.
 * Returns a Record<RoleId, Record<Action, boolean>> response indicating the member's permissions.
 * Each boolean will be `true` if the member can perform the action, `false` otherwise.
 *
 * If the member 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', 'StytchB2BProvider'));
        const client = useStytchB2BClient();
        const { session } = useStytchMemberSession();
        const [permissions, setPermissions] = useAsyncState({
            loaded: false,
            value: null
        });
        useEffect(()=>{
            client.rbac.allPermissions().then((permissions)=>setPermissions({
                    loaded: true,
                    value: permissions
                }));
        }, [
            client,
            session?.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 = createStytchB2BClient('public-token-<find yours in the stytch dashboard>')
 *
 * return (
 *   <StytchB2BProvider stytch={stytch}>
 *     <App />
 *   </StytchB2BProvider>
 * )
 */ const StytchB2BProvider = ({ stytch, children, assumeHydrated = false })=>{
    invariant(!useIsMounted__INTERNAL(), 'You cannot render a <StytchB2BProvider> inside another <StytchB2BProvider>.');
    invariant(!assumeHydrated || typeof window !== 'undefined', 'The `assumeHydrated` prop must be set to `false` when using StytchB2BProvider in a server environment.');
    const ctx = useMemo(()=>({
            client: stytch,
            isMounted: true
        }), [
        stytch
    ]);
    const getHydratedState = useCallback(()=>{
        return {
            member: {
                ...stytch.self.getInfo(),
                isInitialized: true
            },
            session: {
                ...stytch.session.getInfo(),
                isInitialized: true
            },
            organization: {
                ...stytch.organization.getInfo(),
                isInitialized: true
            }
        };
    }, [
        stytch
    ]);
    const getInitialState = ()=>{
        return {
            member: initialMember,
            session: initialMemberSession,
            organization: initialOrganization
        };
    };
    const [{ member, session, organization }, 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(StytchB2BContext.Provider, {
        value: ctx
    }, /*#__PURE__*/ React__default.createElement(StytchOrganizationContext.Provider, {
        value: organization
    }, /*#__PURE__*/ React__default.createElement(StytchMemberContext.Provider, {
        value: member
    }, /*#__PURE__*/ React__default.createElement(StytchMemberSessionContext.Provider, {
        value: session
    }, children))));
};

export { StytchB2BProvider, useIsMounted__INTERNAL, useStytchB2BClient, useStytchIsAuthorized, useStytchMember, useStytchMemberSession, useStytchOrganization, withStytchB2BClient, withStytchMember, withStytchMemberSession, withStytchOrganization, withStytchPermissions };
//# sourceMappingURL=StytchB2BContext.mjs.map
