import React, { createContext, useCallback, useContext, type FC, type ReactNode } from 'react';
import type { ArrayPath, Path, PathValue } from 'react-hook-form';
import { get, set } from '@wener/utils';
import { create as produce } from 'mutative';
import { createStore, useStore, type StoreApi } from 'zustand';

const DefaultContextStore = createStore(() => {
	return {};
});

const Context = createContext<StoreApi<any> | undefined>(undefined);

export const ContextStoreProvider: FC<{ value: StoreApi<any>; children?: ReactNode }> = ({ value, children }) => {
	return <Context.Provider value={value}>{children}</Context.Provider>;
};

function getContextStore() {
	return DefaultContextStore;
}

export function useContextStore<O extends Record<string, any>>(): UseContextStoreReturn<O> {
	const store = useContext(Context) ?? DefaultContextStore;
	return {
		store,
		set(path: string, value: any) {
			store.setState(
				produce((s: any) => {
					set(s, path, value, false);
				}),
			);
		},
		get(path: string): any {
			return get(store.getState(), path);
		},
		useWatch(path: string): any {
			return useStore(
				store,
				useCallback((s) => get(s, path), [path]),
			);
		},
	};
}

export interface UseContextStoreReturn<O extends Record<string, any> = Record<string, any>> {
	store: StoreApi<any>;

	set<P extends Path<O> | ArrayPath<O>, V extends PathValue<O, P>>(type: P, payload: V): void;

	get<P extends Path<O> | ArrayPath<O>, V extends PathValue<O, P>>(path: P): V;

	useWatch<P extends Path<O> | ArrayPath<O>, V extends PathValue<O, P>>(path: P): V;
}
