import fs from 'fs';

import { DateTime } from 'luxon';
import pluginRss from '@11ty/eleventy-plugin-rss';
import pluginSyntaxHighlight from '@11ty/eleventy-plugin-syntaxhighlight';
import pluginNavigation from '@11ty/eleventy-navigation';
import Image from '@11ty/eleventy-img';
import markdownIt from 'markdown-it';
import markdownItAnchor from 'markdown-it-anchor';
import markdownItAttrs from 'markdown-it-attrs';

function hyphenize(input) {
	return input
		.replace(/[^\w- ]/, '')
		.replace(/[_ ]/, '-')
		.toLowerCase();
}

const siteName = process.env.SITE;
if (!siteName) {
	throw new Error(
		'Cannot determine the name of the site to build. Make sure to set the SITE environment variable.'
	);
}

export default function (eleventyConfig) {
	eleventyConfig.addPlugin(pluginRss);
	eleventyConfig.addPlugin(pluginSyntaxHighlight);
	eleventyConfig.addPlugin(pluginNavigation);

	eleventyConfig.setDataDeepMerge(true);

	//
	// Filters
	//

	eleventyConfig.addFilter('readableDate', (dateObj) => {
		return DateTime.fromJSDate(dateObj, { zone: 'utc' }).toFormat(
			'dd LLL yyyy'
		);
	});
	eleventyConfig.addFilter('htmlDateString', (dateObj) => {
		return DateTime.fromJSDate(dateObj, { zone: 'utc' }).toFormat('yyyy-LL-dd');
	});
	eleventyConfig.addFilter('head', (array, n) =>
		n < 0 ? array.slice(n) : array.slice(0, n)
	);
	eleventyConfig.addFilter('min', (...numbers) =>
		Math.min.apply(null, numbers)
	);

	function filterTagList(tags) {
		return (tags || []).filter(
			(tag) => ['all', 'nav', 'post', 'posts'].indexOf(tag) === -1
		);
	}
	eleventyConfig.addFilter('filterTagList', filterTagList);

	// Build collections for the top and bottom navigation - the first one
	// contains the main sites and the latter contains legal pages.
	eleventyConfig.addCollection('topNavigation', (collection) => {
		return collection
			.getAll()
			.filter((item) => !(item.data.tags || []).includes('legal'));
	});
	eleventyConfig.addCollection('bottomNavigation', (collection) => {
		return collection
			.getAll()
			.filter((item) => (item.data.tags || []).includes('legal'));
	});
	eleventyConfig.addCollection('posts', (collection) => {
		return collection.getAll().filter((item) => item.data.layout === 'post');
	});

	//
	// Widgets
	//

	eleventyConfig.addPairedShortcode(
		'section',
		(content, inverted) => `
<section class="page-section${inverted ? ' inverse' : ''}">
${content}
</section>
  `
	);

	eleventyConfig.addPairedShortcode(
		'tabs',
		(content) => `
<div class="tabs-widget">
${content}
</div>
  `
	);
	eleventyConfig.addPairedShortcode('tab', (content, title) => {
		const hyphenizedTitle = hyphenize(title);
		return `
<div id="${hyphenizedTitle}" class="tab">
${content}
</div>
<a href="#${hyphenizedTitle}" rel="tab">${title}</a>
    `;
	});

	eleventyConfig.addPairedShortcode('timeline', (content, stamp) => {
		return `
<section class="timeline">
<span class="stamp${stamp === undefined ? ' small' : ''}">
${stamp || ''}
</span>
<div class="content">
${content}
</div>
</section>
    `;
	});

	eleventyConfig.addPairedAsyncShortcode(
		'banner',
		async (content, title, backgroundSource, backgroundAlt) => {
			const backgroundMetadata = await Image(
				`sites/${siteName}/_images/${backgroundSource}`,
				{
					widths: [1200, 1980, 4000],
					formats: ['avif', 'webp', 'jpeg'],
					urlPath: '/assets/img',
					outputDir: './dist/assets/img',
					sharpAvifOptions: { quality: 40 },
					sharpWebpOptions: { quality: 50 },
					sharpJpegOptions: { quality: 65 },
				}
			);
			const backgroundHTML = Image.generateHTML(backgroundMetadata, {
				alt: backgroundAlt,
				sizes: '100vw',
				loading: 'lazy',
				decoding: 'async',
				whitespaceMode: 'inline',
			});

			return `
<div class="page-banner">
<div class="background">${backgroundHTML}</div>
<div class="content">
${title ? '<div class="title">' + title + '</div>' : ''}
${
	// The '\n's here are required so that markdown still gets rendered in the
	// content block:
	content.trim() ? '<div>\n' + content + '\n</div>' : ''
}
</div>
</div>
    `;
		}
	);

	//
	// Templating
	//

	eleventyConfig.addLayoutAlias('page', 'layouts/page.njk');
	eleventyConfig.addLayoutAlias('post', 'layouts/post.njk');

	let markdownLibrary = markdownIt({
		html: true,
		breaks: false,
		linkify: true,
	})
		.use(markdownItAnchor)
		.use(markdownItAttrs);
	eleventyConfig.setLibrary('md', markdownLibrary);

	//
	// Build settings
	//

	eleventyConfig.addPassthroughCopy({
		assets: 'assets',
		[`sites/${siteName}/_static`]: '.',
	});

	eleventyConfig.setBrowserSyncConfig({
		callbacks: {
			ready: function (err, browserSync) {
				const content_404 = fs.readFileSync('dist/404.html');

				browserSync.addMiddleware('*', (req, res) => {
					// Provides the 404 content without redirect.
					res.writeHead(404, { 'Content-Type': 'text/html; charset=UTF-8' });
					res.write(content_404);
					res.end();
				});
			},
		},
		ui: false,
		ghostMode: false,
	});
}

export const config = {
	dir: {
		input: `sites/${siteName}`,
		output: 'dist',
		// These are all relative to the input directory so the paths get a little
		// weird:
		includes: '../../includes',
		data: '_data',
	},

	templateFormats: ['md', 'njk', 'html', 'liquid'],

	markdownTemplateEngine: 'njk',
	htmlTemplateEngine: 'njk',
	dataTemplateEngine: false,
};
