import { env } from '$env/dynamic/private';
import { AuditAction } from '$lib/server/audit';
import { Audit } from '$lib/server/audit/audit.js';
import { Changesets } from '$lib/server/changesets.js';
import { Users } from '$lib/server/users/index.js';
import { emailRegex, passwordRegex, usernameRegex } from '$lib/validators.js';
import { error, fail, redirect } from '@sveltejs/kit';
import { RateLimiter } from 'sveltekit-rate-limiter/server';

interface RegisterData {
	username: string;
	displayName: string;
	email: string;
	password: string;
}

const fields: (keyof RegisterData)[] = ['username', 'displayName', 'email', 'password'];

const limiter = new RateLimiter({
	IP: [6, 'm']
});

export const actions = {
	default: async (event) => {
		const { request, locals } = event;
		if (await limiter.isLimited(event)) throw error(429);

		// Logged in users cannot make more accounts
		if (locals.session.data?.user || env.REGISTRATIONS === 'false') {
			return redirect(303, '/');
		}

		const body = await request.formData();
		const changes = Changesets.take<RegisterData>(fields, body);
		const { username, displayName, email, password } = changes;
		// Each field must be present
		if (!username || !displayName || !email || !password) {
			return fail(400, {
				username,
				displayName,
				email,
				errors: ['required'],
				fields: fields.reduce<string[]>(
					(missing, field) => (!changes[field] ? [...missing, field] : missing),
					[]
				)
			});
		}

		if (!usernameRegex.test(username)) {
			return fail(400, {
				username,
				displayName,
				email,
				errors: ['invalidUsername'],
				fields: ['username']
			});
		}

		if (displayName.length < 3 || displayName.length > 32) {
			return fail(400, {
				username,
				displayName,
				email,
				errors: ['invalidDisplayName'],
				fields: ['displayName']
			});
		}

		if (!emailRegex.test(email)) {
			return fail(400, {
				username,
				displayName,
				email,
				errors: ['invalidEmail'],
				fields: ['email']
			});
		}

		if (!passwordRegex.test(password)) {
			return fail(400, {
				username,
				displayName,
				email,
				errors: ['invalidPassword'],
				fields: ['password']
			});
		}

		if (!(await Users.checkRegistration(username, email))) {
			return fail(400, {
				username,
				displayName,
				email,
				errors: ['existingRegistration'],
				fields: ['username', 'email']
			});
		}

		// TODO: check for registration token
		const newUser = await Users.register({ username, displayName, password, email });

		await Audit.insertRequest(AuditAction.REGISTRATION, event, newUser);

		return {
			success: newUser.activated ? 'userCreated' : 'emailSent'
		};
	}
};

export const load = ({ locals }) => {
	if (locals.session.data?.user) {
		return redirect(301, '/');
	}

	return {
		enabled: env.REGISTRATIONS === 'true'
	};
};