import { MiiGender, MiiPlatform } from "@prisma/client";
import { z } from "zod";

// profanity censoring bypasses the regex in some of these but I think it's funny

export const querySchema = z
	.string()
	.trim()
	.min(2, { error: "Search query must be at least 2 characters long" })
	.max(64, { error: "Search query cannot be more than 64 characters long" })
	.regex(/^[a-zA-Z0-9-_. ']+$/, {
		error: "Search query can only contain letters, numbers, dashes, underscores, apostrophes, and spaces.",
	});

// Miis
export const nameSchema = z
	.string()
	.trim()
	.min(2, { error: "Name must be at least 2 characters long" })
	.max(64, { error: "Name cannot be more than 64 characters long" })
	.regex(/^[a-zA-Z0-9-_. ']+$/, {
		error: "Name can only contain letters, numbers, dashes, underscores, apostrophes, and spaces.",
	});

export const tagsSchema = z
	.array(
		z
			.string()
			.min(2, { error: "Tags must be at least 2 characters long" })
			.max(20, { error: "Tags cannot be more than 20 characters long" })
			.regex(/^[a-z0-9-_]+$/, {
				error: "Tags can only contain lowercase letters, numbers, dashes, and underscores.",
			}),
	)
	.min(1, { error: "There must be at least 1 tag" })
	.max(8, { error: "There cannot be more than 8 tags" });

export const idSchema = z.coerce.number({ error: "ID must be a number" }).int({ error: "ID must be an integer" }).positive({ error: "ID must be valid" });

export const searchSchema = z.object({
	q: querySchema.optional(),
	sort: z.enum(["likes", "newest", "oldest", "random"], { error: "Sort must be either 'likes', 'newest', 'oldest', or 'random'" }).default("newest"),
	tags: z
		.string()
		.optional()
		.transform((value) =>
			value
				?.split(",")
				.map((tag) => tag.trim())
				.filter((tag) => tag.length > 0),
		),
	exclude: z
		.string()
		.optional()
		.transform((value) =>
			value
				?.split(",")
				.map((tag) => tag.trim())
				.filter((tag) => tag.length > 0),
		),
	platform: z.enum(MiiPlatform, { error: "Platform must be either 'THREE_DS', or 'SWITCH'" }).optional(),
	gender: z.enum(MiiGender, { error: "Gender must be either 'MALE', 'FEMALE', or 'NONBINARY' if on Switch platform" }).optional(),
	allowCopying: z.coerce.boolean({ error: "Allow Copying must be either true or false" }).optional(),
	// todo: incorporate tagsSchema
	// Pages
	limit: z.coerce
		.number({ error: "Limit must be a number" })
		.int({ error: "Limit must be an integer" })
		.min(1, { error: "Limit must be at least 1" })
		.max(100, { error: "Limit cannot be more than 100" })
		.optional(),
	page: z.coerce.number({ error: "Page must be a number" }).int({ error: "Page must be an integer" }).min(1, { error: "Page must be at least 1" }).optional(),
	// Random sort
	seed: z.coerce.number({ error: "Seed must be a number" }).int({ error: "Seed must be an integer" }).optional(),
});

export const userNameSchema = z
	.string()
	.trim()
	.min(2, { error: "Name must be at least 2 characters long" })
	.max(64, { error: "Name cannot be more than 64 characters long" })
	.regex(/^[a-zA-Z0-9-_. ']+$/, {
		error: "Name can only contain letters, numbers, dashes, underscores, apostrophes, and spaces.",
	});

const colorSchema = z.number().int().min(0).max(107).optional();
const geometrySchema = z.number().int().min(-10).max(10).optional();

export const switchMiiInstructionsSchema = z
	.object({
		head: z.object({ type: z.number().int().min(0).max(15).optional(), skinColor: z.number().int().min(0).max(121).optional() }).optional(),
		hair: z
			.object({
				setType: z.number().int().min(0).max(244).optional(),
				bangsType: z.number().int().min(0).max(82).optional(),
				backType: z.number().int().min(0).max(110).optional(),
				color: colorSchema,
				subColor: colorSchema,
				style: z.number().int().min(1).max(3).optional(),
				isFlipped: z.boolean().optional(),
			})
			.optional(),
		eyebrows: z
			.object({
				type: z.number().int().min(0).max(42).optional(),
				color: colorSchema,
				height: geometrySchema,
				distance: geometrySchema,
				rotation: geometrySchema,
				size: geometrySchema,
				stretch: geometrySchema,
			})
			.optional(),
		eyes: z
			.object({
				eyesType: z.number().int().min(0).max(120).optional(),
				eyelashesTop: z.number().int().min(0).max(5).optional(),
				eyelashesBottom: z.number().int().min(0).max(1).optional(),
				eyelidTop: z.number().int().min(0).max(2).optional(),
				eyelidBottom: z.number().int().min(0).max(2).optional(),
				eyeliner: z.number().int().min(0).max(1).optional(),
				pupil: z.number().int().min(0).max(9).optional(),
				color: colorSchema,
				height: geometrySchema,
				distance: geometrySchema,
				rotation: geometrySchema,
				size: geometrySchema,
				stretch: geometrySchema,
			})
			.optional(),
		nose: z
			.object({
				type: z.number().int().min(0).max(31).optional(),
				height: geometrySchema,
				size: geometrySchema,
			})
			.optional(),
		lips: z
			.object({
				type: z.number().int().min(0).max(52).optional(),
				color: colorSchema,
				height: geometrySchema,
				rotation: geometrySchema,
				size: geometrySchema,
				stretch: geometrySchema,
				hasLipstick: z.boolean().optional(),
			})
			.optional(),
		ears: z
			.object({
				type: z.number().int().min(0).max(4).optional(),
				height: geometrySchema,
				size: geometrySchema,
			})
			.optional(),
		glasses: z
			.object({
				type: z.number().int().min(0).max(57).optional(),
				ringColor: colorSchema,
				shadesColor: colorSchema,
				height: geometrySchema,
				size: geometrySchema,
				stretch: geometrySchema,
			})
			.optional(),
		other: z
			.object({
				wrinkles1: z
					.object({
						type: z.number().int().min(0).max(8).optional(),
						color: colorSchema,
						height: geometrySchema,
						distance: geometrySchema,
						size: geometrySchema,
						stretch: geometrySchema,
					})
					.optional(),
				wrinkles2: z
					.object({
						type: z.number().int().min(0).max(14).optional(),
						color: colorSchema,
						height: geometrySchema,
						distance: geometrySchema,
						size: geometrySchema,
						stretch: geometrySchema,
					})
					.optional(),
				beard: z
					.object({
						type: z.number().int().min(0).max(14).optional(),
						color: colorSchema,
						height: geometrySchema,
						distance: geometrySchema,
						size: geometrySchema,
						stretch: geometrySchema,
					})
					.optional(),
				moustache: z
					.object({
						type: z.number().int().min(0).max(15).optional(),
						color: colorSchema,
						height: geometrySchema,
						distance: geometrySchema,
						size: geometrySchema,
						stretch: geometrySchema,
					})
					.optional(),
				goatee: z
					.object({
						type: z.number().int().min(0).max(13).optional(),
						color: colorSchema,
						height: geometrySchema,
						distance: geometrySchema,
						size: geometrySchema,
						stretch: geometrySchema,
					})
					.optional(),
				mole: z
					.object({
						type: z.number().int().min(0).max(1).optional(),
						color: colorSchema,
						height: geometrySchema,
						distance: geometrySchema,
						size: geometrySchema,
						stretch: geometrySchema,
					})
					.optional(),
				eyeShadow: z
					.object({
						type: z.number().int().min(0).max(3).optional(),
						color: colorSchema,
						height: geometrySchema,
						distance: geometrySchema,
						size: geometrySchema,
						stretch: geometrySchema,
					})
					.optional(),
				blush: z
					.object({
						type: z.number().int().min(0).max(7).optional(),
						color: colorSchema,
						height: geometrySchema,
						distance: geometrySchema,
						size: geometrySchema,
						stretch: geometrySchema,
					})
					.optional(),
			})
			.optional(),
		height: z.number().int().min(0).max(100).optional(),
		weight: z.number().int().min(0).max(100).optional(),
		datingPreferences: z.array(z.enum(MiiGender)).optional(),
		voice: z
			.object({
				speed: z.number().int().min(0).max(100).optional(),
				pitch: z.number().int().min(0).max(100).optional(),
				depth: z.number().int().min(0).max(100).optional(),
				delivery: z.number().int().min(0).max(100).optional(),
				tone: z.number().int().min(1).max(6).optional(),
			})
			.optional(),
		personality: z
			.object({
				movement: z.number().int().min(1).max(8).optional(),
				speech: z.number().int().min(1).max(8).optional(),
				energy: z.number().int().min(1).max(8).optional(),
				thinking: z.number().int().min(1).max(8).optional(),
				overall: z.number().int().min(1).max(8).optional(),
			})
			.optional(),
	})
	.optional();
