import { fetch } from "@tauri-apps/plugin-http";

import {
	type InferOutput,
	array,
	literal,
	null_,
	nullable,
	parse,
	pipe,
	strictObject,
	string,
	union,
	uuid,
	variant,
} from "valibot";

type BaseRequestParams = Partial<{
	path: string;
	body: RequestInit["body"];
	method: "GET" | "POST";
}>;

// export const base = "https://haystack.johncosta.tech";
export const base = "http://192.168.1.199:3040";

const getBaseRequest = ({ path, body, method }: BaseRequestParams): Request => {
	return new Request(`${base}/${path}`, {
		body,
		method,
	});
};

const getBaseAuthorizedRequest = ({
	path,
	body,
	method,
}: BaseRequestParams): Request => {
	return new Request(`${base}/${path}`, {
		headers: {
			Authorization: `Bearer ${localStorage.getItem("access")?.toString()}`,
		},
		body,
		method,
	});
};
const sendImageResponseValidator = strictObject({
	ID: pipe(string(), uuid()),
	ImageID: pipe(string(), uuid()),
	UserID: pipe(string(), uuid()),
	Status: string(),
});

export const sendImageFile = async (
	imageName: string,
	file: File,
): Promise<InferOutput<typeof sendImageResponseValidator>> => {
	const request = getBaseAuthorizedRequest({
		path: `image/${imageName}`,
		body: file,
		method: "POST",
	});

	request.headers.set("Content-Type", "application/oclet-stream");

	const res = await fetch(request).then((res) => res.json());

	return parse(sendImageResponseValidator, res);
};

export const sendImage = async (
	imageName: string,
	base64Image: string,
): Promise<InferOutput<typeof sendImageResponseValidator>> => {
	const request = getBaseAuthorizedRequest({
		path: `image/${imageName}`,
		body: base64Image,
		method: "POST",
	});

	request.headers.set("Content-Type", "application/base64");

	const res = await fetch(request).then((res) => res.json());

	return parse(sendImageResponseValidator, res);
};

const locationValidator = strictObject({
	ID: pipe(string(), uuid()),
	CreatedAt: pipe(string()),
	Name: string(),
	Address: nullable(string()),
	Description: nullable(string()),
	Images: array(pipe(string(), uuid())),
});

const contactValidator = strictObject({
	ID: pipe(string(), uuid()),
	CreatedAt: pipe(string()),
	Name: string(),
	Description: nullable(string()),
	PhoneNumber: nullable(string()),
	Email: nullable(string()),
	Images: array(pipe(string(), uuid())),
});

const eventValidator = strictObject({
	ID: pipe(string(), uuid()),
	CreatedAt: nullable(pipe(string())),
	Name: string(),
	StartDateTime: nullable(pipe(string())),
	EndDateTime: nullable(pipe(string())),
	Description: nullable(string()),
	LocationID: nullable(pipe(string(), uuid())),
	// Location: nullable(locationValidator),
	OrganizerID: nullable(pipe(string(), uuid())),
	// Organizer: nullable(contactValidator),
	Images: array(pipe(string(), uuid())),
});

const noteValidator = strictObject({
	ID: pipe(string(), uuid()),
	CreatedAt: pipe(string()),
	Name: string(),
	Description: nullable(string()),
	Content: string(),
	Images: array(pipe(string(), uuid())),
});

const locationDataType = strictObject({
	type: literal("location"),
	data: locationValidator,
});

const eventDataType = strictObject({
	type: literal("event"),
	data: eventValidator,
});

const noteDataType = strictObject({
	type: literal("note"),
	data: noteValidator,
});

const contactDataType = strictObject({
	type: literal("contact"),
	data: contactValidator,
});

const dataTypeValidator = variant("type", [
	locationDataType,
	eventDataType,
	noteDataType,
	contactDataType,
]);

export type CategoryUnion = InferOutput<typeof dataTypeValidator>;

const userImageValidator = strictObject({
	ID: pipe(string(), uuid()),
	CreatedAt: pipe(string()),
	ImageID: pipe(string(), uuid()),
	UserID: pipe(string(), uuid()),
	Image: strictObject({
		ID: pipe(string(), uuid()),
		ImageName: string(),
		Image: null_(),
	}),
});

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

export type UserImage = InferOutput<typeof dataTypeValidator>;

const imageRequestValidator = strictObject({
	UserImages: array(userImageValidator),
	ImageProperties: array(dataTypeValidator),
	ProcessingImages: array(userProcessingImageValidator),
});

export const getUserImages = async (): Promise<
	InferOutput<typeof imageRequestValidator>
> => {
	const request = getBaseAuthorizedRequest({ path: "image" });

	const res = await fetch(request).then((res) => res.json());

	console.log("BACKEND RESPONSE: ", res);

	return parse(imageRequestValidator, res);
};

export const getImage = async (imageId: string): Promise<UserImage> => {
	const request = getBaseAuthorizedRequest({
		path: `image-properties/${imageId}`,
	});

	const res = await fetch(request).then((res) => res.json());
	return parse(dataTypeValidator, res);
};

export const postLogin = async (email: string): Promise<void> => {
	const request = getBaseRequest({
		path: "login",
		body: JSON.stringify({ email }),
		method: "POST",
	});

	await fetch(request);
};

export const postDemoLogin = async (): Promise<
	InferOutput<typeof codeValidator>
> => {
	const request = getBaseRequest({
		path: "demo-login",
	});

	const res = await fetch(request).then((res) => res.json());

	return parse(codeValidator, res);
};

const codeValidator = strictObject({
	access: string(),
	refresh: string(),
});

export const postCode = async (
	email: string,
	code: string,
): Promise<InferOutput<typeof codeValidator>> => {
	const request = getBaseRequest({
		path: "code",
		body: JSON.stringify({ email, code }),
		method: "POST",
	});

	const res = await fetch(request).then((res) => res.json());

	return parse(codeValidator, res);
};
