import type { RequestHandler } from '@sveltejs/kit';
import { getSession } from '$lib/server/session';
import { Permissions } from '$lib/permissions';
import { Admin, Report, User } from '$lib/server/database';
import type { Attributes } from 'sequelize';
import { Op } from 'sequelize';
import { env } from '$env/dynamic/private';
import crypto from 'crypto';

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

	const data: {
		limit: number | null;
		from: number | null;

		draft: boolean | null;
		status: 'none' | 'review' | 'reviewed' | null;
		reporter: string | null;
		reported: string | null;
	} = await request.json();

	const reportFindOptions: Attributes<Report> = {};
	if (data.draft != null) reportFindOptions.draft = data.draft;
	reportFindOptions.status = data.status == null ? ['none', 'review'] : data.status;
	if (data.reporter != null) {
		const reporter_ids = await User.findAll({
			attributes: ['id'],
			where: { username: { [Op.like]: `%${data.reporter}%` } }
		});
		reportFindOptions.reporter_id = reporter_ids.map((u) => u.id);
	}
	if (data.reported != null) {
		const reported_ids = await User.findAll({
			attributes: ['id'],
			where: { username: { [Op.like]: `%${data.reported}%` } }
		});
		reportFindOptions.reported_id = reported_ids.map((u) => u.id);
	}
	let reports = await Report.findAll({
		where: reportFindOptions,
		include: [
			{ model: User, as: 'reporter' },
			{ model: User, as: 'reported' },
			{ model: Admin, as: 'auditor' }
		],
		order: ['created_at'],
		offset: data.from || 0,
		limit: data.limit || 100
	});
	reports = reports.map((r) => {
		if (r.auditor_id === null && r.status != 'none') {
			// if the report was edited by the admin account set by the env variable, it has no relation to the admin
			// table in the database, so it gets manually created here. we just assume that the auditor_id is never null
			// when not edited by the env admin account
			r.auditor_id = -1;
			r.dataValues.auditor = {
				id: -1,
				username: env.ADMIN_USER,
				permissions: new Permissions(Permissions.allPermissions()),
				createdAt: 0,
				updatedAt: 0
			};
		} else if (r.auditor) {
			delete r.dataValues.auditor.password;
		}
		return r;
	});

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

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

	const data: {
		id: number;
		auditor: number;
		notice: string | null;
		statement: string | null;
		status: 'none' | 'review' | 'reviewed' | null;
	} = await request.json();

	if (data.id === null || data.auditor === null) return new Response(null, { status: 400 });

	const report = await Report.findOne({ where: { id: data.id } });
	const admin = await Admin.findOne({ where: { id: data.auditor } });
	if (report === null || (admin === null && data.auditor != -1))
		return new Response(null, { status: 400 });

	if (data.notice != null) report.notice = data.notice;
	if (data.statement != null) report.statement = data.statement;
	if (data.status != null) report.status = data.status;
	if (admin != null) report.auditor_id = admin.id;

	await report.save();

	return new Response();
}) satisfies RequestHandler;

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

	const data: {
		reporter: string;
		reported: string;
		reason: string;
		body: string | null;
	} = await request.json();

	if (
		data.reporter == null ||
		data.reported == null ||
		data.reason == null ||
		data.body === undefined
	)
		return new Response(null, { status: 400 });

	const reporter = await User.findOne({ where: { uuid: data.reporter } });
	const reported = await User.findOne({ where: { uuid: data.reported } });

	if (reporter == null || reported == null) return new Response(null, { status: 400 });

	const report = await Report.create({
		subject: data.reason,
		body: data.body,
		draft: data.body === null,
		status: 'none',
		url_hash: crypto.randomBytes(18).toString('hex'),
		completed: false,
		reporter_id: reporter.id,
		reported_id: reported.id
	});
	report.dataValues.reporter = await User.findOne({ where: { id: report.reporter_id } });
	report.dataValues.reported = await User.findOne({ where: { id: report.reported_id } });
	report.dataValues.auditor = null;

	return new Response(JSON.stringify(report), {
		status: 201
	});
}) satisfies RequestHandler;
