use lazy_static::lazy_static;
use markdown_it::generics::inline::full_link;
use markdown_it::{MarkdownIt, Node};
use maud::{Markup, PreEscaped, Render, html};
use page::RequestContext;
use page::page::{Page, PageMetadata};

use crate::components::md::emote::InlineEmote;
use crate::components::md::frontmatter::{TomlFrontMatter, YamlFrontMatter};
use crate::components::md::link::SmartLink;
use crate::components::md::mdx::InlineMdx;

mod emote;
mod frontmatter;
mod link;
mod mdx;

lazy_static! {
	static ref MdParser: MarkdownIt = {
		let mut md = markdown_it::MarkdownIt::new();

		{

				use markdown_it::plugins::cmark::*;

				inline::newline::add(&mut md);
				inline::escape::add(&mut md);
				inline::backticks::add(&mut md);
				inline::emphasis::add(&mut md);

				// Replaced with smart links
				//inline::link::add(&mut md);
				full_link::add::<false>(&mut md, |href, title| {
					Node::new(SmartLink {
						url: href.unwrap_or_default(),
						title,
					})
				});


				inline::image::add(&mut md);
				inline::autolink::add(&mut md);
				inline::entity::add(&mut md);

				block::code::add(&mut md);
				block::fence::add(&mut md);
				block::blockquote::add(&mut md);
				block::hr::add(&mut md);
				block::list::add(&mut md);
				block::reference::add(&mut md);
				block::heading::add(&mut md);
				block::lheading::add(&mut md);
				block::paragraph::add(&mut md);

		}

		{
			markdown_it::plugins::html::add(&mut md);
			md_footnote::add(&mut md);
		}


		md.block.add_rule::<YamlFrontMatter>().before_all();
		md.block.add_rule::<TomlFrontMatter>().before_all();

		md.inline.add_rule::<InlineEmote>();
		md.inline.add_rule::<InlineMdx>();

		md
	};
}

pub struct Markdown<'a>(pub &'a str);

impl Render for Markdown<'_> {
	fn render(&self) -> Markup {
		let md = Self::parse(self.0);
		let html = md.render();
		return PreEscaped(html);
	}
}

impl Markdown<'_> {
	pub fn parse(md_str: &str) -> Node {
		MdParser.parse(md_str)
	}
}

//
// MARK: helpers
//

/// Try to read page metadata from a markdown file's frontmatter.
/// - returns `none` if there is no frontmatter
/// - returns an error if we fail to parse frontmatter
pub fn meta_from_markdown(root_node: &Node) -> Result<Option<PageMetadata>, toml::de::Error> {
	root_node
		.children
		.first()
		.and_then(|x| x.cast::<TomlFrontMatter>())
		.map(|x| toml::from_str::<PageMetadata>(&x.content))
		.map_or(Ok(None), |v| v.map(Some))
}

pub fn backlinks(page: &Page, ctx: &RequestContext) -> Option<Markup> {
	let mut last = None;
	let mut backlinks = vec![("/", "home")];

	if page.meta.backlinks.unwrap_or(false) {
		let mut segments = ctx.route.split("/").skip(1).collect::<Vec<_>>();
		last = segments.pop();

		let mut end = 0;
		for s in segments {
			end += s.len();
			backlinks.push((&ctx.route[0..=end], s));
			end += 1; // trailing slash
		}
	}

	last.map(|last| {
		html! {
			div {
				@for (url, text) in backlinks {
					a href=(url) style="padding-left:5pt;padding-right:5pt;" { (text) }
					"/"
				}

				span style="color:var(--metaColor);padding-left:5pt;padding-right:5pt;" { (last) }
			}
		}
	})
}
