/* eslint-disable no-undef */
"use strict";

// FIXME: Think about whether to do type(clauses) or [ type(), ... clauses ]
// FIXME: Defaults via defaultTo! And make sure to document that these can *only* consist of query objects (literals or expressions), not arbitrary JS
// FIXME: Think about field validation API; both schema-time CHECK constraints and runtime validation
// FIXME: Think about some way to integrate partial cacheing of queries (eg. for determining permissions for roles)
// FIXME: Document that due to how schema creation works, only belongsTo(...) is possible and has(...) isn't
// FIXME: last() in addition to first()? Would need to find a way to combine that with sorting, and think about how to handle that when no sorting criterium is given.
// FIXME: duration() and time parsing
// FIXME: ensure that relations work on collapsed queries, but *only* if the FK column appears in the permitted output columns
// FIXME: Separate batch-insert API so that data(...) calls are composable without implying multiple items
// FIXME: Have a short-hand (non-composable) API for create("foo", {obj}) and select("foo", {whereObj}) type usage? Especially for eg. `select("posts", { id: postID })`
// FIXME: Also for createTable? So that you can do eg. createTable("posts", { title: string() }) instead of the `columns` indirection, when you don't have stuff like composite indexes
// FIXME: Allow passing a custom type to autoID?

let timestamps = {
	created_at: [ timestamp(), defaultTo(now()) ],
	updated_at: [ timestamp(), optional() ]
};

createTable("permissions", {
	role: belongsTo("roles.id"),
	permission: string() // defined in the application code
});

createTable("roles", {
	name: string(),
	color: string()
});

createTable("users", {
	username: string(),
	password_hash: string(),
	email_address: string(),
	is_banned: [ boolean(), defaultTo(false) ],
	ban_reason: [ string(), optional() ],
	last_activity: [ timestamp(), defaultTo(now()), index() ],
	activation_key: [ uuid(), optional(), index() ],
	activation_expiry: [ timestamp(), optional() ],
	password_reset_key: [ uuid(), optional(), index() ],
	password_reset_expiry: [ timestamp(), optional() ]
});

createTable("categories", {
	name: string(),
	created_by: belongsTo("users.id"),
	visible_on_frontpage: [ boolean(), defaultTo(true) ]
});

createTable("threads", {
	title: string(),
	category: belongsTo("categories.id"),
	user: belongsTo("users.id"), // FIXME: Figure out how to auto-detect the column type for relations
	visible: [ boolean(), defaultTo(true) ],
	... timestamps
});

createTable("posts", {
	thread: belongsTo("threads.id"),
	user: belongsTo("users.id"),
	body: string(),
	visible: [ boolean(), defaultTo(true) ],
	... timestamps
});

////////////////////////////////////////////////////////////

// List active users and include their role information for highlighting moderators etc.

function timeAgo(time) {
	return subtract(now(), duration(time));
}

select("users", [
	where({ last_activity: lessThan(timeAgo("1h")) }),
	withRelations({ role: belongsTo("role") })
]);

// Count the active users by role
// NOTE: This returns an object { role, count } where `role` is the actual data from the `roles` table

select("users", [
	where({ last_activity: moreThan(timeAgo("1h")) }),
	withRelations({ role: belongsTo("role") }),
	collapseBy("role", [
		compute({ count: count() })
	])
]);

// Update a user's last activity

update("users", [
	where({ id: userID }),
	set({ last_activity: now() })
]);

// Show latest threads in all categories except hidden threads and frontpage-hidden categories

function mostRecent(field) {
	return [
		first(),
		sortedBy(descending(field))
	];
}

select("threads", [
	define("category", belongsTo("category")),
	define("last_post", has("posts.thread", [
		mostRecent("created_at")
	])),
	where({
		visible: true,
		category: { visible_on_frontpage: true }
	}),
	sortedBy(descending("last_post.created_at"))
]);

// Get a thread with all its posts

select("threads", [
	first(),
	where({ id: threadID }),
	withRelations({
		posts: has("posts.thread", [
			where({ visible: true }),
			startAt(offset),
			first(10)
		])
	})
]);

// Create a new thread

create("threads", [
	withRelations({ posts: has("posts.thread") }),
	set({
		title: title,
		user: userID,
		posts: [{
			user: userID,
			body: body
		}]
	})
]);

// Update the thread title

update("threads", [
	where({ id: threadID }),
	set({
		title: newTitle,
		updated_at: now()
	})
]);

// Create a new post

create("posts", {
	thread: threadID,
	user: userID,
	body: body
});

// Edit a post body

update("posts", [
	where({ id: postID }),
	set({
		body: newBody,
		updated_at: now()
	})
]);
