import { escapeHtml, highlightCode } from './highlight';

function renderInline(text: string): string {
	let result = escapeHtml(text);

	result = result.replace(/`([^`]+)`/g, '<code>$1</code>');
	result = result.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" loading="lazy" />');
	result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
	result = result.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
	result = result.replace(/__([^_]+)__/g, '<strong>$1</strong>');
	result = result.replace(/\*([^*]+)\*/g, '<em>$1</em>');
	result = result.replace(/(?<!\w)_([^_]+)_(?!\w)/g, '<em>$1</em>');

	return result;
}

export function renderMarkdown(markdown: string): string {
	const lines = markdown.replace(/\r\n/g, '\n').split('\n');
	const blocks: string[] = [];

	let paragraph: string[] = [];
	let list: { ordered: boolean; items: string[] } | null = null;

	function flushParagraph() {
		if (paragraph.length) {
			blocks.push(`<p>${renderInline(paragraph.join(' '))}</p>`);
			paragraph = [];
		}
	}

	function flushList() {
		if (list) {
			const tag = list.ordered ? 'ol' : 'ul';
			const items = list.items.map((item) => `<li>${renderInline(item)}</li>`).join('');
			blocks.push(`<${tag}>${items}</${tag}>`);
			list = null;
		}
	}

	let i = 0;
	while (i < lines.length) {
		const line = lines[i];

		if (!line.trim()) {
			flushParagraph();
			flushList();
			i++;
			continue;
		}

		const heading = line.match(/^(#{1,6})\s+(.*)$/);
		if (heading) {
			flushParagraph();
			flushList();
			const level = heading[1].length;
			blocks.push(`<h${level}>${renderInline(heading[2])}</h${level}>`);
			i++;
			continue;
		}

		if (/^(-{3,}|\*{3,}|_{3,})$/.test(line.trim())) {
			flushParagraph();
			flushList();
			blocks.push('<hr />');
			i++;
			continue;
		}

		if (line.trim().startsWith('```')) {
			flushParagraph();
			flushList();
			const lang = line.trim().slice(3).trim();
			const codeLines: string[] = [];
			i++;
			while (i < lines.length && !lines[i].trim().startsWith('```')) {
				codeLines.push(lines[i]);
				i++;
			}
			i++;
			const codeClass = lang ? ` class="language-${lang}"` : '';
			const codeText = codeLines.join('\n');
			const codeHtml = lang ? highlightCode(codeText, lang) : escapeHtml(codeText);
			blocks.push(`<pre><code${codeClass}>${codeHtml}</code></pre>`);
			continue;
		}

		const quote = line.match(/^>\s?(.*)$/);
		if (quote) {
			flushParagraph();
			flushList();
			const quoteLines = [quote[1]];
			i++;
			while (i < lines.length && lines[i].match(/^>\s?(.*)$/)) {
				quoteLines.push(lines[i].match(/^>\s?(.*)$/)![1]);
				i++;
			}
			blocks.push(`<blockquote><p>${renderInline(quoteLines.join(' '))}</p></blockquote>`);
			continue;
		}

		const unordered = line.match(/^[-*+]\s+(.*)$/);
		const ordered = line.match(/^\d+\.\s+(.*)$/);
		if (unordered || ordered) {
			const isOrdered = !!ordered;
			const itemText = (unordered ?? ordered)![1];
			if (!list || list.ordered !== isOrdered) {
				flushList();
				list = { ordered: isOrdered, items: [] };
			}
			list.items.push(itemText);
			i++;
			continue;
		}

		flushList();
		paragraph.push(line.trim());
		i++;
	}

	flushParagraph();
	flushList();

	return blocks.join('\n');
}
