import * as commonmark from './commonmark.min.js';

function escape(text) {
	return (text ?? '')
		.replaceAll('&', '&amp;')
		.replaceAll('<', '&lt;')
		.replaceAll('>', '&gt;');
}

function escapeAttribute(text) {
	return (text ?? '')
		.replaceAll('&', '&amp;')
		.replaceAll('<', '&lt;')
		.replaceAll('>', '&gt;')
		.replaceAll('"', '&quot;')
		.replaceAll("'", '&#39;');
}

export async function get_blog_message(id) {
	let message;
	await ssb.sqlAsync(
		'SELECT author, timestamp, content FROM messages WHERE id = ?',
		[id],
		function (row) {
			let content = JSON.parse(row.content);
			message = {
				author: row.author,
				timestamp: row.timestamp,
				blog: content?.blog,
				title: content?.title,
			};
		}
	);
	if (message) {
		await ssb.sqlAsync(
			`
				SELECT json_extract(content, '$.name') AS name
				FROM messages
				WHERE author = ?
				AND json_extract(content, '$.type') = 'about'
				AND json_extract(content, '$.about') = author
				AND name IS NOT NULL
				ORDER BY sequence DESC LIMIT 1
			`,
			[message.author],
			function (row) {
				message.name = row.name;
			}
		);
	}
	return message;
}

export function markdown(md) {
	let reader = new commonmark.Parser();
	let writer = new commonmark.HtmlRenderer({safe: true});
	let parsed = reader.parse(md || '');
	let walker = parsed.walker();
	let event, node;
	while ((event = walker.next())) {
		node = event.node;
		if (event.entering) {
			if (node.destination?.startsWith('&')) {
				node.destination =
					'/' + node.destination + '/view?filename=' + node.firstChild?.literal;
			} else if (
				node.destination?.startsWith('@') ||
				node.destination?.startsWith('%')
			) {
				node.destination = '/~core/ssb/#' + escape(node.destination);
			}
		}
	}
	return writer.render(parsed);
}

export async function render_blog_post_html(blog_post) {
	let blob = utf8Decode(await ssb.blobGet(blog_post.blog));
	return `<!DOCTYPE html>
		<html>
			<head>
				<title>🪵Tilde Friends Blog - ${markdown(blog_post.title)}</title>
				<base target="_top">
			</head>
			<body>
				<h1><a href="./">🪵Tilde Friends Blog</a></h1>
				<div>
					<div><a href="../ssb/#${escapeAttribute(blog_post.author)}">${escape(blog_post.name)}</a> ${escape(new Date(blog_post.timestamp).toString())}</div>
					<div>${markdown(blob)}</div>
				</div>
			</body>
		</html>
	`;
}

function render_blog_post(blog_post) {
	return `
		<div>
			<h2><a href="/~${core.app.owner}/${core.app.name}/${escapeAttribute(blog_post.id)}">${escape(blog_post.title)}</a></h2>
			<div><a href="../ssb/#${escapeAttribute(blog_post.author)}">${escape(blog_post.name)}</a> ${escape(new Date(blog_post.timestamp).toString())}</div>
			<div>${markdown(blog_post.summary)}</div>
		</div>
	`;
}

export function render_html(blogs) {
	return `<!DOCTYPE html>
		<html>
			<head>
				<title>🪵Tilde Friends Blog</title>
				<link href="./atom" type="application/atom+xml" rel="alternate" title="🪵Tilde Blog"/>
				<style>
					html {
						background-color: #ccc;
					}
				</style>
				<base target="_top">
			</head>
			<body>
				<div style="display: flex; flex-direction: row; align-items: center; gap: 1em">
					<h1>🪵Tilde Friends Blog</h1>
					<div style="font-size: xx-small; vertical-align: middle"><a href="/~cory/blog/atom">atom feed</a></div>
				</div>
				${blogs.map((blog_post) => render_blog_post(blog_post)).join('\n')}
			</body>
		</html>`;
}

function render_blog_post_atom(blog_post) {
	return `<entry>
		<title>${escape(blog_post.title)}</title>
		<link href="/~cory/ssb/#${blog_post.id}" />
		<id>${blog_post.id}</id>
		<published>${escape(new Date(blog_post.timestamp).toString())}</published>
		<summary>${escape(blog_post.summary)}</summary>
		<author>
			<name>${escape(blog_post.name)}</name>
			<feed>${escape(blog_post.author)}</feed>
		</author>
	</entry>`;
}

export function render_atom(blogs) {
	return `<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>🪵Tilde Blog</title>
	<subtitle>A subtitle.</subtitle>
	<link href="${core.url}/atom" rel="self"/>
	<link href="${core.url}"/>
	<id>${core.url}</id>
	<updated>${new Date().toString()}</updated>
	${blogs.map((blog_post) => render_blog_post_atom(blog_post)).join('\n')}
</feed>`;
}

export async function get_posts() {
	let blogs = [];
	let ids = await ssb.getIdentities();
	await ssb.sqlAsync(
		`
		WITH
			blogs AS (
				SELECT
					messages.author,
					messages.id,
					json_extract(messages.content, '$.title') AS title,
					json_extract(messages.content, '$.summary') AS summary,
					json_extract(messages.content, '$.blog') AS blog,
					messages.timestamp
				FROM messages_fts('blog')
				JOIN messages ON messages.rowid = messages_fts.rowid
				WHERE json_extract(messages.content, '$.type') = 'blog'),
			public AS (
				SELECT author FROM (
					SELECT
						messages.author,
						RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank,
						json_extract(messages.content, '$.publicWebHosting') AS is_public
					FROM messages_fts('about')
					JOIN messages ON messages.rowid = messages_fts.rowid
					WHERE json_extract(messages.content, '$.type') = 'about' AND is_public IS NOT NULL)
				WHERE author_rank = 1 AND is_public),
			names AS (
				SELECT author, name FROM (
					SELECT
						messages.author,
						RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank,
						json_extract(messages.content, '$.name') AS name
					FROM messages_fts('about')
					JOIN messages ON messages.rowid = messages_fts.rowid
					WHERE json_extract(messages.content, '$.type') = 'about' AND
						json_extract(messages.content, '$.about') = messages.author AND
						name IS NOT NULL)
				WHERE author_rank = 1)
		SELECT blogs.*, names.name FROM blogs
		JOIN json_each(?) AS self ON self.value = blogs.author
		JOIN public ON public.author = blogs.author
		LEFT OUTER JOIN names ON names.author = blogs.author
		ORDER BY blogs.timestamp DESC LIMIT 20
	`,
		[JSON.stringify(ids)],
		function (row) {
			blogs.push(row);
		}
	);
	return blogs;
}
