import * as tfrpc from '/static/tfrpc.js';

let g_hash;

core.register('message', async function message_handler(message) {
	if (message.event == 'hashChange') {
		g_hash = message.hash;
		await tfrpc.rpc.hashChanged(message.hash);
	}
});
tfrpc.register(function getHash(id, message) {
	return g_hash;
});
tfrpc.register(function setHash(hash) {
	return app.setHash(hash);
});
tfrpc.register(async function store_blob(blob) {
	if (Array.isArray(blob)) {
		blob = Uint8Array.from(blob);
	}
	return await ssb.blobStore(blob);
});
tfrpc.register(async function appendMessage(id, message) {
	return ssb.appendMessageWithIdentity(id, message);
});
tfrpc.register(async function getActiveIdentity() {
	return await ssb.getActiveIdentity();
});

async function query(sql, args) {
	let result = [];
	let start = new Date();
	await ssb.sqlAsync(sql, args, function (row) {
		result.push(row);
	});
	let end = new Date();
	print((end - start) / 1000, sql);
	return result;
}

async function get_names(identities) {
	return query(
		`
		SELECT author, name FROM (
			SELECT
				messages.author,
				RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank,
				messages.content ->> 'name' AS name
			FROM messages
			JOIN json_each(?) AS identities ON identities.value = messages.author
			WHERE
				json_extract(messages.content, '$.type') = 'about' AND
				content ->> 'about' = messages.author AND name IS NOT NULL)
		WHERE author_rank = 1
	`,
		[JSON.stringify(identities)]
	);
}

async function get_books(following) {
	let result = await query(
		`
		WITH books as (
			SELECT
				messages.author AS author,
				messages.timestamp AS timestamp,
				COALESCE(messages.content ->> 'updates', messages.id) AS id,
				RTRIM(fields.key, 's') AS key,
				RANK() OVER (PARTITION BY COALESCE(messages.content ->> 'updates', messages.id), RTRIM(fields.key, 's') ORDER BY messages.timestamp DESC) AS rank,
				fields.value AS value
			FROM
				messages,
				json_each(messages.content) AS fields
			JOIN json_each(?) AS following
			ON messages.author = following.value
			WHERE
				messages.content ->> 'type' IN ('bookclub', 'bookclubUpdate') AND
				RTRIM(fields.key, 's') IN ('title', 'author', 'image', 'description')
		)
		SELECT books.id, MAX(books.timestamp) AS timestamp, json(json_group_object(books.key, books.value)) AS book FROM books
		WHERE books.rank = 1
		GROUP BY books.id
		ORDER BY timestamp DESC
	`,
		[JSON.stringify([...new Set(following)])]
	);
	result = Object.fromEntries(
		result.map((x) => [x.id, Object.assign({id: x.id}, JSON.parse(x.book))])
	);
	for (let book of Object.values(result)) {
		try {
			book.image = JSON.parse(book.image);
			if (book.image instanceof Array) {
				book.image = book.image[0];
			}
			if (typeof book.image == 'string') {
				book.image = {link: book.image};
			}
		} catch {}
	}
	return result;
}

async function get_reviews(books, following) {
	return await query(
		`
		SELECT id, author, about, json(json_group_object(key, value)) AS content
		FROM (
			SELECT
				messages.id,
				messages.author,
				COALESCE(messages.content ->> '$.updates', messages.content ->> '$.about') AS about,
				RTRIM(fields.key, 's') AS key,
				RANK() OVER (PARTITION BY messages.author, COALESCE(messages.content ->> '$.updates', messages.content ->> '$.about'), RTRIM(fields.key, 's') ORDER BY messages.timestamp DESC) AS rank,
				fields.value
			FROM messages, json_each(messages.content) AS fields
			JOIN json_each(?2) AS following
			ON messages.author = following.value
			JOIN json_each(?1) AS book
			ON (messages.content ->> '$.about' = book.value OR messages.content ->> '$.updates' = book.value)
			WHERE
				messages.content ->> 'type' IN ('about', 'bookclubUpdate')
				AND RTRIM(fields.key, 's') IN ('rating', 'ratingMax', 'ratingType', 'review', 'shelve')
			) WHERE rank = 1
		GROUP BY author, about
	`,
		[JSON.stringify(Object.keys(books)), JSON.stringify(following)]
	);
}

async function main() {
	await app.setDocument(`
		<!DOCTYPE html>
		<html>
			<head>
				<link rel="stylesheet" href="w3.css" />
			</head>
			<body class="w3-light-gray">
				<div
					class="w3-display-middle w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge w3-xlarge w3-dark-gray"
				>
					<span class="w3-spin" style="display: inline-block">📖</span>
					Loading...
				</div>
			</body>
		</html>
	`);
	let whoami = await ssb.getActiveIdentity();
	let following = Object.keys(await ssb.following([whoami], 2));
	let books = await get_books(following);
	for (let book of Object.values(books)) {
		try {
			book.authors = JSON.parse(book.authors);
		} catch {
			book.authors = [book.authors];
		}
		book.reviews = [];
	}
	let reviews = await get_reviews(books, following);
	let identities = [
		...new Set([
			...Object.values(books).map((x) => x.author),
			...reviews.map((x) => x.author),
		]),
	];
	identities = Object.fromEntries(
		(await get_names(identities)).map((x) => [x.author, x.name])
	);
	for (let book of Object.values(books)) {
		book.author_name = identities[book.author];
	}
	for (let review of reviews) {
		review.content = JSON.parse(review.content);
		review.author_name = identities[review.author];
		books[review.about].reviews.push(review);
	}
	await app.setDocument(
		utf8Decode(getFile('index.html')).replace('G_DATA', JSON.stringify(books))
	);
}

main();
