import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
import { jsx } from "react/jsx-runtime";
import { hasPermission, isAtLeastRole } from "@platform-modules/auth";
//#region src/errors.ts
/** Typed boundary error when the session is no longer valid. */
var AuthSessionExpiredError = class extends Error {
	name = "AuthSessionExpiredError";
	constructor(message = "session expired") {
		super(message);
	}
};
function isAuthSessionExpiredError(e) {
	return typeof e === "object" && e !== null && e.name === "AuthSessionExpiredError";
}
var AuthProviderError = class extends Error {
	name = "AuthProviderError";
	constructor(hook) {
		super(`${hook} must be used within <AuthProvider>`);
	}
};
function isAuthProviderError(e) {
	return typeof e === "object" && e !== null && e.name === "AuthProviderError";
}
//#endregion
//#region src/AuthProvider.tsx
const AuthContext = createContext(null);
function useAuthContext(hook) {
	const ctx = useContext(AuthContext);
	if (!ctx) throw new AuthProviderError(hook);
	return ctx;
}
/** Holds the current Principal snapshot and refreshes it on mount. */
function AuthProvider({ client, children }) {
	const [user, setUser] = useState(null);
	const [loading, setLoading] = useState(true);
	const [error, setError] = useState(null);
	const [reloadToken, setReloadToken] = useState(0);
	const clientRef = useRef(client);
	clientRef.current = client;
	const reload = useCallback(() => {
		setReloadToken((t) => t + 1);
	}, []);
	useEffect(() => {
		let cancelled = false;
		setLoading(true);
		setError(null);
		clientRef.current.getCurrentUser().then((principal) => {
			if (!cancelled) {
				setUser(principal);
				setLoading(false);
			}
		}).catch((e) => {
			if (!cancelled) {
				setError(e instanceof Error ? e : new Error(String(e)));
				setLoading(false);
			}
		});
		return () => {
			cancelled = true;
		};
	}, [reloadToken]);
	const value = useMemo(() => ({
		client,
		user,
		loading,
		error,
		reload,
		setUser
	}), [
		client,
		user,
		loading,
		error,
		reload
	]);
	return /* @__PURE__ */ jsx(AuthContext.Provider, {
		value,
		children
	});
}
//#endregion
//#region src/useCurrentUser.ts
function useCurrentUser() {
	const { user, loading, error, reload } = useAuthContext("useCurrentUser");
	return {
		user,
		loading,
		error,
		reload
	};
}
//#endregion
//#region src/useSignIn.ts
function useSignIn() {
	const { client, setUser } = useAuthContext("useSignIn");
	const [pending, setPending] = useState(false);
	const [error, setError] = useState(null);
	const clientRef = useRef(client);
	clientRef.current = client;
	return {
		signIn: useCallback(async (creds) => {
			setPending(true);
			setError(null);
			try {
				const principal = await clientRef.current.signIn(creds);
				setUser(principal);
				setPending(false);
				return principal;
			} catch (e) {
				const err = e instanceof Error ? e : new Error(String(e));
				setError(err);
				setPending(false);
				throw err;
			}
		}, [setUser]),
		pending,
		error
	};
}
//#endregion
//#region src/useSignUp.ts
function useSignUp() {
	const { client, setUser } = useAuthContext("useSignUp");
	const [pending, setPending] = useState(false);
	const [error, setError] = useState(null);
	const clientRef = useRef(client);
	clientRef.current = client;
	return {
		signUp: useCallback(async (input) => {
			setPending(true);
			setError(null);
			try {
				const principal = await clientRef.current.signUp(input);
				setUser(principal);
				setPending(false);
				return principal;
			} catch (e) {
				const err = e instanceof Error ? e : new Error(String(e));
				setError(err);
				setPending(false);
				throw err;
			}
		}, [setUser]),
		pending,
		error
	};
}
//#endregion
//#region src/useSignOut.ts
function useSignOut() {
	const { client, setUser } = useAuthContext("useSignOut");
	const [pending, setPending] = useState(false);
	const [error, setError] = useState(null);
	const clientRef = useRef(client);
	clientRef.current = client;
	return {
		signOut: useCallback(async () => {
			setPending(true);
			setError(null);
			try {
				await clientRef.current.signOut();
				setUser(null);
				setPending(false);
			} catch (e) {
				const err = e instanceof Error ? e : new Error(String(e));
				setError(err);
				setPending(false);
				throw err;
			}
		}, [setUser]),
		pending,
		error
	};
}
//#endregion
//#region src/usePasswordReset.ts
function usePasswordReset() {
	const { client } = useAuthContext("usePasswordReset");
	const [pending, setPending] = useState(false);
	const [error, setError] = useState(null);
	const clientRef = useRef(client);
	clientRef.current = client;
	return {
		request: useCallback(async (email) => {
			setPending(true);
			setError(null);
			try {
				await clientRef.current.requestPasswordReset(email);
				setPending(false);
			} catch (e) {
				const err = e instanceof Error ? e : new Error(String(e));
				setError(err);
				setPending(false);
				throw err;
			}
		}, []),
		reset: useCallback(async (token, newPassword) => {
			setPending(true);
			setError(null);
			try {
				await clientRef.current.resetPassword(token, newPassword);
				setPending(false);
			} catch (e) {
				const err = e instanceof Error ? e : new Error(String(e));
				setError(err);
				setPending(false);
				throw err;
			}
		}, []),
		pending,
		error
	};
}
//#endregion
//#region src/RequireRole.tsx
/**
* UX-only role guard — hides children when the current user lacks the required role.
* Authorization path: `isAtLeastRole` from `@platform-modules/auth` when `hierarchy` is
* provided; otherwise exact `principal.roles` membership. The server route MUST
* re-authorize; hiding a button is not access control.
*/
function RequireRole({ role, hierarchy, fallback = null, children }) {
	const { user, loading } = useCurrentUser();
	if (loading || !user) return fallback;
	return (hierarchy ? isAtLeastRole(user, role, hierarchy) : user.roles.includes(role)) ? children : fallback;
}
//#endregion
//#region src/RequireCapability.tsx
/**
* UX-only capability guard — hides children when the current user lacks the capability.
* Authorization path: `hasPermission` from `@platform-modules/auth`. The server route
* MUST re-authorize; hiding a button is not access control.
*/
function RequireCapability({ capability, fallback = null, children }) {
	const { user, loading } = useCurrentUser();
	if (loading || !user) return fallback;
	return hasPermission(user, capability) ? children : fallback;
}
//#endregion
export { AuthProvider, AuthProviderError, AuthSessionExpiredError, RequireCapability, RequireRole, isAuthProviderError, isAuthSessionExpiredError, useCurrentUser, usePasswordReset, useSignIn, useSignOut, useSignUp };
