import { JSDOM } from 'jsdom';
import config from '../config';
import { INote } from '../models/note';
import { intersperse } from '../prelude/array';
import { MfmForest, MfmTree } from './prelude';

export function toHtml(tokens: MfmForest, mentionedRemoteUsers: INote['mentionedRemoteUsers'] = []) {
	if (tokens == null) {
		return null;
	}

	const { window } = new JSDOM('');

	const doc = window.document;

	function appendChildren(children: MfmForest, targetElement: any): void {
		for (const child of children.map(t => handlers[t.node.type](t))) targetElement.appendChild(child);
	}

	const handlers: { [key: string]: (token: MfmTree) => any } = {
		bold(token) {
			const el = doc.createElement('b');
			appendChildren(token.children, el);
			return el;
		},

		big(token) {
			const el = doc.createElement('strong');
			appendChildren(token.children, el);
			return el;
		},

		small(token) {
			const el = doc.createElement('small');
			appendChildren(token.children, el);
			return el;
		},

		strike(token) {
			const el = doc.createElement('del');
			appendChildren(token.children, el);
			return el;
		},

		italic(token) {
			const el = doc.createElement('i');
			appendChildren(token.children, el);
			return el;
		},

		motion(token) {
			const el = doc.createElement('i');
			appendChildren(token.children, el);
			return el;
		},

		spin(token) {
			const el = doc.createElement('i');
			appendChildren(token.children, el);
			return el;
		},

		jump(token) {
			const el = doc.createElement('i');
			appendChildren(token.children, el);
			return el;
		},

		flip(token) {
			const el = doc.createElement('span');
			appendChildren(token.children, el);
			return el;
		},

		blockCode(token) {
			const pre = doc.createElement('pre');
			const inner = doc.createElement('code');
			inner.innerHTML = token.node.props.code;
			pre.appendChild(inner);
			return pre;
		},

		center(token) {
			const el = doc.createElement('div');
			appendChildren(token.children, el);
			return el;
		},

		emoji(token) {
			return doc.createTextNode(token.node.props.emoji ? token.node.props.emoji : `:${token.node.props.name}:`);
		},

		hashtag(token) {
			const a = doc.createElement('a');
			a.href = `${config.url}/tags/${token.node.props.hashtag}`;
			a.textContent = `#${token.node.props.hashtag}`;
			a.setAttribute('rel', 'tag');
			return a;
		},

		inlineCode(token) {
			const el = doc.createElement('code');
			el.textContent = token.node.props.code;
			return el;
		},

		mathInline(token) {
			const el = doc.createElement('code');
			el.textContent = token.node.props.formula;
			return el;
		},

		mathBlock(token) {
			const el = doc.createElement('code');
			el.textContent = token.node.props.formula;
			return el;
		},

		link(token) {
			const a = doc.createElement('a');
			a.href = token.node.props.url;
			appendChildren(token.children, a);
			return a;
		},

		mention(token) {
			const a = doc.createElement('a');
			const { username, host, acct } = token.node.props;
			switch (host) {
				case 'github.com':
					a.href = `https://github.com/${username}`;
					break;
				case 'twitter.com':
					a.href = `https://twitter.com/${username}`;
					break;
				default:
					const remoteUserInfo = mentionedRemoteUsers.find(remoteUser => remoteUser.username === username && remoteUser.host === host);
					a.href = remoteUserInfo ? remoteUserInfo.uri : `${config.url}/${acct}`;
					a.className = 'mention';
					break;
			}
			a.textContent = acct;
			return a;
		},

		quote(token) {
			const el = doc.createElement('blockquote');
			appendChildren(token.children, el);
			return el;
		},

		title(token) {
			const el = doc.createElement('h1');
			appendChildren(token.children, el);
			return el;
		},

		text(token) {
			const el = doc.createElement('span');
			const nodes = (token.node.props.text as string).split(/\r\n|\r|\n/).map(x => doc.createTextNode(x) as Node);

			for (const x of intersperse<Node | 'br'>('br', nodes)) {
				el.appendChild(x === 'br' ? doc.createElement('br') : x);
			}

			return el;
		},

		url(token) {
			const a = doc.createElement('a');
			a.href = token.node.props.url;
			a.textContent = token.node.props.url;
			return a;
		},

		search(token) {
			const a = doc.createElement('a');
			a.href = `https://www.google.com/?#q=${token.node.props.query}`;
			a.textContent = token.node.props.content;
			return a;
		}
	};

	appendChildren(tokens, doc.body);

	return `<p>${doc.body.innerHTML}</p>`;
}
