import { getSession } from '$lib/server/session';
import { Permissions } from '$lib/permissions';
import { error, type RequestHandler } from '@sveltejs/kit';
import { User } from '$lib/server/database';
import { type Attributes, Op } from 'sequelize';
import { ApiError, getJavaUuid, getNoAuthUuid, RateLimitError, UserNotFoundError } from '$lib/server/minecraft';
import { UserAddSchema, UserDeleteSchema, UserEditSchema, UserListSchema } from './schema';

export const POST = (async ({ request, cookies }) => {
	if (getSession(cookies, { permissions: [Permissions.Users] }) == null) {
		return new Response(null, {
			status: 401
		});
	}

	const parseResult = await UserListSchema.safeParseAsync(await request.json());
	if (!parseResult.success) {
		return new Response(null, { status: 400 });
	}
	const data = parseResult.data;

	const usersFindOptions: Attributes<User> = {};
	if (data.name) {
		Object.assign(usersFindOptions, {
			[Op.or]: {
				firstname: { [Op.like]: `%${data.name}%` },
				lastname: { [Op.like]: `%${data.name}%` },
				username: { [Op.like]: `%${data.name}%` }
			}
		});
	} else if (data.search) {
		Object.assign(usersFindOptions, {
			[Op.or]: {
				username: { [Op.like]: `%${data.search}%` },
				uuid: { [Op.like]: `%${data.search}%` }
			}
		});
	}
	if (data.playertype) {
		usersFindOptions.playertype = data.playertype;
	}
	const users = await User.findAll({
		where: usersFindOptions,
		attributes: data.slim ? ['username', 'uuid'] : undefined,
		offset: data.from || 0,
		limit: data.limit || 100,
		order: data.sort ? [[data.sort.key, data.sort.asc ? 'ASC' : 'DESC']] : undefined
	});

	return new Response(JSON.stringify(users));
}) satisfies RequestHandler;

export const PATCH = (async ({ request, cookies }) => {
	if (getSession(cookies, { permissions: [Permissions.Users] }) == null) {
		return new Response(null, {
			status: 401
		});
	}

	const parseResult = await UserEditSchema.safeParseAsync(await request.json());
	if (!parseResult.success) {
		return new Response(null, { status: 400 });
	}
	const data = parseResult.data;

	const user = await User.findOne({ where: { id: data.id } });
	if (!user) {
		return new Response(null, {
			status: 400
		});
	}

	if (data.firstname) user.firstname = data.firstname;
	if (data.lastname) user.lastname = data.lastname;
	if (data.birthday) user.birthday = data.birthday;
	if (data.telephone) user.telephone = data.telephone;
	if (data.username) user.username = data.username;
	if (data.playertype) user.playertype = data.playertype;
	if (data.uuid) user.uuid = data.uuid;
	await user.save();

	return new Response();
}) satisfies RequestHandler;

export const PUT = (async ({ request, cookies }) => {
	if (getSession(cookies, { permissions: [Permissions.Users] }) == null) {
		return new Response(null, {
			status: 401
		});
	}

	const parseResult = await UserAddSchema.safeParseAsync(await request.json());
	if (!parseResult.success) {
		return new Response(null, { status: 400 });
	}
	const data = parseResult.data;

	let uuid: string | null;
	try {
		switch (data.playertype) {
			case 'java':
				uuid = await getJavaUuid(data.username);
				break;
			case 'bedrock':
				uuid = null;
				// uuid = await getBedrockUuid(username);
				break;
			case 'noauth':
				uuid = getNoAuthUuid(data.username);
				break;
			default:
				throw new Error(`invalid player type (${data.playertype})`);
		}
	} catch (e) {
		if (e instanceof UserNotFoundError) {
			throw error(400, `Der Spielername ${data.username} existiert nicht`);
		} else if (e instanceof ApiError) {
			console.error((e as Error).message);
			uuid = null;
		} else if (e instanceof RateLimitError) {
			console.error(`uuid request rate limited for user '${data.username}'`);
			uuid = null;
		} else {
			console.error((e as Error).message);
			throw error(500);
		}
	}

	if (uuid && (await User.findOne({ where: { uuid: uuid } }))) {
		throw error(400, 'Dieser Minecraft-Account wurde bereits registriert');
	} else if (
		await User.findOne({
			where: {
				firstname: data.firstname,
				lastname: data.lastname,
				birthday: new Date(data.birthday).toUTCString()
			}
		})
	) {
		throw error(400, 'Ein Nutzer mit demselben Namen und Geburtstag wurde bereits registriert');
	}

	await User.create({
		firstname: data.firstname,
		lastname: data.lastname,
		birthday: new Date(data.birthday).toUTCString(),
		telephone: data.telephone,
		username: data.username,
		playertype: data.playertype,
		password: null,
		uuid: uuid
	});

	return new Response();
}) satisfies RequestHandler;

export const DELETE = (async ({ request, cookies }) => {
	if (getSession(cookies, { permissions: [Permissions.Users] }) == null) {
		return new Response(null, {
			status: 401
		});
	}

	const parseResult = await UserDeleteSchema.safeParseAsync(await request.json());
	if (!parseResult.success) {
		return new Response(null, { status: 400 });
	}
	const data = parseResult.data;

	await User.destroy({ where: { id: data.id } });

	return new Response();
}) satisfies RequestHandler;