import { createStore } from "solid-js/store";
import {
	type InferOutput,
	literal,
	pipe,
	safeParse,
	strictObject,
	string,
	union,
	uuid,
} from "valibot";
import { base } from "../network";
import { createContext, createEffect, onCleanup } from "solid-js";
import { useSearchImageContext } from "../contexts/SearchImageContext";

const processingImagesValidator = strictObject({
	ImageID: pipe(string(), uuid()),
	ImageName: string(),
	Status: union([
		literal("not-started"),
		literal("in-progress"),
		literal("complete"),
	]),
});

type NotificationState = {
	ProcessingImages: Record<
		string,
		InferOutput<typeof processingImagesValidator> | undefined
	>;
};

export const Notifications = (onCompleteImage: () => void) => {
	const [state, setState] = createStore<NotificationState>({
		ProcessingImages: {},
	});

	const { processingImages } = useSearchImageContext();

	const access = localStorage.getItem("access");
	if (access == null) {
		throw new Error("Access token not defined");
	}

	const dataEventListener = (e: MessageEvent<unknown>) => {
		if (typeof e.data !== "string") {
			console.error("Error type is not string");
			return;
		}

		let jsonData: object = {};
		try {
			jsonData = JSON.parse(e.data);
		} catch (e) {
			console.error(e);
			return;
		}

		const processingImage = safeParse(processingImagesValidator, jsonData);
		if (!processingImage.success) {
			console.error("Processing image could not be parsed.", e.data);
			return;
		}

		console.log("SSE: ", processingImage);

		const { ImageID, Status } = processingImage.output;

		if (Status === "complete") {
			setState("ProcessingImages", ImageID, undefined);
			onCompleteImage();
		} else {
			setState("ProcessingImages", ImageID, processingImage.output);
		}
	};

	const upsertImageProcessing = (
		images: NotificationState["ProcessingImages"],
	) => {
		setState("ProcessingImages", (currentImages) => ({
			...currentImages,
			...images,
		}));
	};

	createEffect(() => {
		const images = processingImages();
		if (images == null) {
			return;
		}

		upsertImageProcessing(
			Object.fromEntries(
				images.map((i) => [
					i.ImageID,
					{
						ImageID: i.ImageID,
						ImageName: i.Image.ImageName,
						Status: i.Status,
					},
				]),
			),
		);
	});

	const events = new EventSource(`${base}/notifications?token=${access}`);

	events.addEventListener("data", dataEventListener);

	events.onerror = (e) => {
		console.error(e);
	};

	onCleanup(() => {
		events.removeEventListener("data", dataEventListener);
		events.close();
	});

	return {
		state,
	};
};

export const NotificationsContext =
	createContext<ReturnType<typeof Notifications>>();
