import { base as app_base } from '$app/paths';
import {
	escape,
	extractFrontmatter,
	markedTransform,
	normalizeSlugify,
	removeMarkdown
} from '@sveltejs/site-kit/markdown';
import { CONTENT_BASE_PATHS } from '../../../constants.js';
import { render_content } from '../renderer';

/**
 * @param {import('./types').DocsData} docs_data
 * @param {string} slug
 */
export async function get_parsed_docs(docs_data, slug) {
	for (const { pages } of docs_data) {
		for (const page of pages) {
			if (page.slug === slug) {
				return {
					...page,
					content: await render_content(page.file, page.content)
				};
			}
		}
	}

	return null;
}

/** @return {Promise<import('./types').DocsData>} */
export async function get_docs_data(base = CONTENT_BASE_PATHS.DOCS) {
	const { readdir, readFile } = await import('node:fs/promises');

	/** @type {import('./types').DocsData} */
	const docs_data = [];

	for (const category_dir of await readdir(base)) {
		const match = /\d{2}-(.+)/.exec(category_dir);
		if (!match) continue;

		const category_slug = match[1];

		// Read the meta.json
		const { title: category_title, draft = 'false' } = JSON.parse(
			await readFile(`${base}/${category_dir}/meta.json`, 'utf-8')
		);

		if (draft === 'true') continue;

		/** @type {import('./types').Category} */
		const category = {
			title: category_title,
			slug: category_slug,
			pages: []
		};

		for (const filename of await readdir(`${base}/${category_dir}`)) {
			if (filename === 'meta.json') continue;
			const match = /\d{2}-(.+)/.exec(filename);
			if (!match) continue;

			const page_slug = match[1].replace('.md', '');

			const page_data = extractFrontmatter(
				await readFile(`${base}/${category_dir}/${filename}`, 'utf-8')
			);

			if (page_data.metadata.draft === 'true') continue;

			const page_title = page_data.metadata.title;
			const page_content = page_data.body;

			category.pages.push({
				title: page_title,
				slug: page_slug,
				content: page_content,
				category: category_title,
				sections: await get_sections(page_content),
				path: `${app_base}/docs/${page_slug}`,
				file: `${category_dir}/${filename}`
			});
		}

		docs_data.push(category);
	}

	return docs_data;
}

/** @param {import('./types').DocsData} docs_data */
export function get_docs_list(docs_data) {
	return docs_data.map((category) => ({
		title: category.title,
		pages: category.pages.map((page) => ({
			title: page.title,
			path: page.path
		}))
	}));
}

/** @param {string} str */
const titled = async (str) =>
	removeMarkdown(
		escape(await markedTransform(str, { paragraph: (txt) => txt }))
			.replace(/<\/?code>/g, '')
			.replace(/&#39;/g, "'")
			.replace(/&quot;/g, '"')
			.replace(/&lt;/g, '<')
			.replace(/&gt;/g, '>')
			.replace(/&amp;/, '&')
			.replace(/<(\/)?(em|b|strong|code)>/g, '')
	);

/**
 * @param {string} markdown
 * @returns {Promise<import('./types').Section[]>}
 */
export async function get_sections(markdown) {
	const lines = markdown.split('\n');
	const root = /** @type {import('./types').Section} */ ({
		title: 'Root',
		slug: 'root',
		sections: [],
		breadcrumbs: [''],
		text: ''
	});
	let currentNodes = [root];

	for (const line of lines) {
		const match = line.match(/^(#{2,4})\s(.*)/);
		if (match) {
			const level = match[1].length - 2;
			const text = await titled(match[2]);
			const slug = normalizeSlugify(text);

			// Prepare new node
			/** @type {import('./types').Section} */
			const newNode = {
				title: text,
				slug,
				sections: [],
				breadcrumbs: [...currentNodes[level].breadcrumbs, text],
				text: ''
			};

			// Add the new node to the tree
			const sections = currentNodes[level].sections;
			if (!sections) throw new Error(`Could not find section ${level}`);
			sections.push(newNode);

			// Prepare for potential children of the new node
			currentNodes = currentNodes.slice(0, level + 1);
			currentNodes.push(newNode);
		} else if (line.trim() !== '') {
			// Add non-heading line to the text of the current section
			currentNodes[currentNodes.length - 1].text += line + '\n';
		}
	}

	return /** @type {import('./types').Section[]} */ (root.sections);
}