import {
	type Accessor,
	type Component,
	type ParentProps,
	createContext,
	createMemo,
	createResource,
	useContext,
} from "solid-js";
import { CategoryUnion, getUserImages } from "../network";
import { groupPropertiesWithImage } from "../utils/groupPropertiesWithImage";

export type ImageWithRawData = Awaited<
	ReturnType<typeof getUserImages>
>["ImageProperties"][number] & {
	rawData: string[];
};

type TaggedCategory<T extends CategoryUnion["type"]> = Extract<
	CategoryUnion,
	{ type: T }
>["data"];

type CategoriesSpecificData = {
	[K in CategoryUnion["type"]]: Array<TaggedCategory<K>>;
};

export type SearchImageStore = {
	images: Accessor<ImageWithRawData[]>;

	imagesWithProperties: Accessor<ReturnType<typeof groupPropertiesWithImage>>;
	processingImages: Accessor<
		Awaited<ReturnType<typeof getUserImages>>["ProcessingImages"] | undefined
	>;

	categories: Accessor<CategoriesSpecificData>;

	onRefetchImages: () => void;
};

// How wonderfully functional
const getAllValues = (object: object): Array<string> => {
	const loop = (acc: Array<string>, next: object): Array<string> => {
		for (const [key, _value] of Object.entries(next)) {
			if (key === "ID") {
				continue;
			}

			const value: unknown = _value;
			switch (typeof value) {
				case "object":
					if (value != null) {
						acc.push(...loop(acc, value));
					}
					break;
				case "string":
				case "number":
				case "boolean":
					acc.push(value.toString());
					break;
				default:
					break;
			}
		}

		return acc;
	};

	return loop([], object);
};

const SearchImageContext = createContext<SearchImageStore>();
export const SearchImageContextProvider: Component<ParentProps> = (props) => {
	const [data, { refetch }] = createResource(getUserImages);

	const imageData = createMemo<ImageWithRawData[]>(() => {
		const d = data();
		if (d == null) {
			return [];
		}

		return d.ImageProperties.map((d) => ({
			...d,
			rawData: getAllValues(d),
		}));
	});

	const processingImages = () => data()?.ProcessingImages ?? [];

	const imagesWithProperties = createMemo<
		ReturnType<typeof groupPropertiesWithImage>
	>(() => {
		const d = data();
		if (d == null) {
			return {};
		}

		return groupPropertiesWithImage(d);
	});

	const categories = createMemo(() => {
		const c: ReturnType<SearchImageStore["categories"]> = {
			contact: [],
			event: [],
			location: [],
			note: [],
		};

		for (const category of data()?.ImageProperties ?? []) {
			if (category.type === "location" || category.type === "contact") {
				continue;
			}

			c[category.type].push(category.data as any);
		}

		return c;
	});

	return (
		<SearchImageContext.Provider
			value={{
				images: imageData,
				imagesWithProperties: imagesWithProperties,
				processingImages,
				onRefetchImages: refetch,
				categories,
			}}
		>
			{props.children}
		</SearchImageContext.Provider>
	);
};

export const useSearchImageContext = () => {
	const context = useContext(SearchImageContext);
	if (context == null) {
		throw new Error(
			"Unreachable: We should always have a mounted context and no undefined values",
		);
	}

	return context;
};
