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

core.register('setActiveIdentity', async function (id) {
	await tfrpc.rpc.set('identity', id);
});

tfrpc.register(function getUser() {
	return core?.user?.credentials?.session?.name;
});

tfrpc.register(async function getActiveIdentity() {
	return await ssb.getActiveIdentity();
});

tfrpc.register(async function globalSettingsGet(key) {
	return core.globalSettingsGet(key);
});

tfrpc.register(async function globalSettingsSet(key, value) {
	return core.globalSettingsSet(key, value);
});

tfrpc.register(function isAdministrator() {
	return core.user?.credentials?.permissions?.administration;
});

tfrpc.register(async function appendMessage(id, message) {
	return ssb.appendMessageWithIdentity(id, message);
});

tfrpc.register(function blobHas(id) {
	return ssb.blobHas(id);
});

tfrpc.register(function saveApp(owner, app, id) {
	return core.saveApp(owner, app, id);
});

async function get_names(identities) {
	let result = {};
	await ssb.sqlAsync(
		`
			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)],
		function (row) {
			result[row.author] = row.name;
		}
	);
	return result;
}

async function fetch_info(apps) {
	let result = {};
	for (let [key, value] of Object.entries(apps)) {
		let blob_id;
		if (value.startsWith('&')) {
			blob_id = value;
		} else if (value.startsWith('%')) {
			let app;
			await ssb.sqlAsync(
				`
				SELECT
					author,
					timestamp,
					json(json_group_object(key, value)) ->> 'app' AS app,
					json(json_group_object(key, value)) ->> 'tombstone' AS tombstone
				FROM (
					SELECT
						messages.author,
						messages.timestamp,
						fields.key,
						RANK() OVER (PARTITION BY fields.key ORDER BY messages.sequence DESC) AS rank,
						fields.value
					FROM
						messages app
					JOIN
						messages
					ON
						app.author = messages.author AND
						(app.id = messages.id OR app.id = messages.content ->> 'updates')
					JOIN
						json_each(messages.content) AS fields
					WHERE
						app.id = ? AND
						app.content ->> 'type' = 'tildefriends-app' AND
						messages.content ->> 'type' IN ('tildefriends-app', 'tildefriends-app-edit') AND
						fields.key IN ('app', 'tombstone')
				)
				WHERE rank = 1
				`,
				[value],
				function (row) {
					result[key] = {
						message_id: value,
						author: row.author,
						timestamp: row.timestamp,
						installed: true,
					};
					blob_id = row.app;
				}
			);
		}

		if (blob_id) {
			let blob = await ssb.blobGet(blob_id);
			blob = blob ? utf8Decode(blob) : '{}';
			result[key] = Object.assign(result[key] ?? {}, JSON.parse(blob));
			result[key].blob_id = value;
			result[key].package_name = key;
		}
	}
	return Object.values(result);
}

async function get_apps_by_authors(authors) {
	let apps = {};
	await ssb.sqlAsync(
		`
			SELECT
				id,
				author,
				timestamp,
				json(json_group_object(key, value)) ->> 'app' AS app,
				json(json_group_object(key, value)) ->> 'description' AS description,
				json(json_group_object(key, value)) ->> 'change' AS change,
				json(json_group_object(key, value)) ->> 'name' AS name,
				json(json_group_object(key, value)) ->> 'tombstone' AS tombstone
			FROM (
				SELECT
					app.id,
					app.author,
					messages.timestamp,
					fields.key,
					RANK() OVER (PARTITION BY messages.author, COALESCE(messages.content ->> 'updates', messages.id), fields.key ORDER BY messages.sequence DESC) AS rank,
					fields.value
				FROM
					messages app
				JOIN
					json_each(?) AS authors ON app.author = authors.value
				JOIN
					messages
				ON
					app.author = messages.author AND
					(app.id = messages.id OR app.id = messages.content ->> 'updates')
				JOIN
					json_each(messages.content) AS fields
				WHERE
					app.content ->> 'type' = 'tildefriends-app' AND
					messages.content ->> 'type' IN ('tildefriends-app', 'tildefriends-app-edit') AND
					fields.key IN ('app', 'description', 'change', 'name', 'tombstone')
			)
			WHERE rank = 1
			GROUP BY author, id
		`,
		[JSON.stringify(authors)],
		function (row) {
			apps[JSON.stringify([row.author, row.name])] = row;
		}
	);
	return Object.values(apps);
}

tfrpc.register(async function get_revisions(message_id, limit) {
	let result = [];
	await ssb.sqlAsync(
		`
			SELECT
				revision.id,
				revision.timestamp,
				revision.content ->> 'app' AS app,
				revision.content ->> 'change' AS change,
				revision.content ->> 'description' AS description,
				revision.content ->> 'tombstone' AS tombstone
			FROM messages app
			JOIN messages revision ON (
				revision.id = app.id OR
				(revision.author = app.author AND revision.content ->> 'updates' = app.id)
			)
			WHERE app.id = ?
			ORDER BY revision.sequence DESC
			LIMIT ?
		`,
		[message_id, limit],
		function (row) {
			result.push(row);
		}
	);
	return result;
});

tfrpc.register(async function get_my_apps() {
	let result = (await fetch_info(await core.apps())).map((x) =>
		Object.assign({package_owner: core?.user?.credentials?.session?.name}, x)
	);
	for (let app of await get_apps_by_authors([await ssb.getActiveIdentity()])) {
		for (let found of result) {
			if (found.package_name === app.name) {
				found.shared = true;
				found.modified = found.blob_id !== app.app;
				found.message_id = app.id;
				found.description = app.description;
				found.change = app.change;
				found.tombstone = app.tombstone;
			}
		}
	}
	return result;
});

tfrpc.register(async function get_shared_apps() {
	let following = await ssb.following([await ssb.getActiveIdentity()], 2);
	let apps = await get_apps_by_authors(Object.keys(following));
	let result = {};
	for (let app of apps.sort((x, y) => y.timestamp - x.timestamp)) {
		let app_object = JSON.parse(utf8Decode(await ssb.blobGet(app.app)));
		if (app_object && !app.tombstone) {
			app_object.author = app.author;
			app_object.message_id = app.id;
			app_object.blob_id = app.app;
			app_object.package_name = app.name;
			app_object.timestamp = app.timestamp;
			app_object.description = app.description;
			result[`${app.author}:${app.name}`] = app_object;
		}
	}
	let names = await get_names([
		...new Set([...Object.values(result).map((x) => x.author)]),
	]);
	for (let app of Object.values(result)) {
		app.author_name = names[app.author] ?? app.author;
	}
	return Object.values(result).filter((x) => !x.tombstone);
});

tfrpc.register(async function get_core_apps() {
	return (await fetch_info(await core.apps('core'))).map((x) =>
		Object.assign({package_owner: 'core'}, x)
	);
});

async function main() {
	await app.setDocument(utf8Decode(getFile('index.html')));
}

main();
