import {
	createContext,
	useCallback,
	useEffect,
	useMemo,
	useState,
} from "react";
import { Env, Insight, type MCPToolRequest } from "../../..";

/**
 * Context of the react data
 */
export const InsightContext = createContext<
	| {
			isInitialized: Insight["isInitialized"];
			isAuthorized: Insight["isAuthorized"];
			isReady: Insight["isReady"];
			error: Insight["error"];
			system: Insight["system"];
			actions: Insight["actions"];
			insightId: Insight["insightId"];
			tool?: MCPToolRequest;
	  }
	| undefined
>(undefined);

interface InsightProviderProps {
	/**
	 * Content to render with the insight
	 */
	children: React.ReactNode;

	/**
	 * Options to load into the app
	 */
	options?: Parameters<Insight["initialize"]>[0];

	/**
	 * Whether to destroy the insight on unmount
	 * Defaults to true
	 */
	destroyOnUnmount?: boolean;
}

export const InsightProvider = (props: InsightProviderProps) => {
	const { children, options, destroyOnUnmount = true } = props;

	// create the new insight on load
	const insight = useMemo(() => {
		return new Insight();
	}, []);

	const [isInitialized, setIsInitialized] =
		useState<Insight["isInitialized"]>(false);
	const [isAuthorized, setIsAuthorized] =
		useState<Insight["isAuthorized"]>(false);
	const [isReady, setIsReady] = useState<Insight["isReady"]>(false);
	const [error, setError] = useState<Insight["error"]>(null);
	const [system, setSystem] = useState<Insight["system"]>(null);
	const [insightId, setInsightId] = useState<Insight["insightId"]>("");

	/**
	 * Sync the insight with react
	 */
	const syncInsight = useCallback(() => {
		setError(insight.error);
		setSystem(insight.system);
		setIsAuthorized(insight.isAuthorized);
		setIsInitialized(insight.isInitialized);
		setIsReady(insight.isReady);
		setInsightId(insight.insightId);
	}, [insight]);

	const wrappedActions = useMemo(() => {
		return Object.keys(insight.actions).reduce(
			(acc, val) => {
				acc[val] = async (...args: unknown[]) => {
					// wait for the action to complete
					const response = await insight.actions[val].apply(null, [
						...args,
					]);

					// sync it
					syncInsight();

					// return the response
					return response;
				};

				return acc;
			},
			{} as Insight["actions"],
		);
	}, [insight, insight.actions, syncInsight]);

	// initialize the insight / destroy
	// biome-ignore lint/correctness/useExhaustiveDependencies: options is checked in the string
	useEffect(() => {
		// initialize the insight
		insight.initialize(options).finally(() => syncInsight());

		return () => {
			if (destroyOnUnmount) {
				// destroy the insight
				insight.destroy().finally(() => {
					// update the state
					syncInsight();
				});
				return;
			}

			// update the state without destroying
			syncInsight();
		};
	}, [insight, JSON.stringify(options), destroyOnUnmount, syncInsight]);

	return (
		<InsightContext.Provider
			value={{
				isInitialized: isInitialized,
				isAuthorized: isAuthorized,
				isReady: isReady,
				error: error,
				system: system,
				actions: wrappedActions,
				insightId: insightId,
				// ENV.TOOL should not change once the insight is ready
				tool: Env.TOOL ?? null,
			}}
		>
			{children}
		</InsightContext.Provider>
	);
};
