use pretty::{Arena, DocAllocator, DocBuilder};

use crate::{
    ast::{Image, Inline, Link, LinkDefinition, Tag, Wikilink},
    render::{
        Config, Context, ToDoc,
        util::{ArenaExt as _, escape},
    },
};

impl<'a> ToDoc<'a> for Vec<Inline> {
    fn to_doc(
        &self,
        config: &'a Config<'a>,
        context: &'a Context,
        arena: &'a Arena<'a>,
    ) -> DocBuilder<'a, Arena<'a>, ()> {
        arena.concat(
            self.iter()
                .map(|inline| inline.to_doc(config, context, arena))
                .collect::<Vec<_>>(),
        )
    }
}

impl<'a> ToDoc<'a> for Inline {
    fn to_doc(
        &self,
        config: &'a Config<'a>,
        context: &'a Context,
        arena: &'a Arena<'a>,
    ) -> DocBuilder<'a, Arena<'a>, ()> {
        match self {
            Self::Text(t) => arena.text(escape(t)),
            Self::LineBreak => arena.tag("br", vec![], arena.nil()),
            Self::Code(code) => arena.tag("code", vec![], arena.text(escape(code))),
            Self::Html(html) => arena.text(html.clone()),
            Self::Emphasis(children) => {
                arena.tag("em", vec![], children.to_doc(config, context, arena))
            }
            Self::Strong(children) => {
                arena.tag("b", vec![], children.to_doc(config, context, arena))
            }
            Self::Strikethrough(children) => {
                arena.tag("s", vec![], children.to_doc(config, context, arena))
            }
            Self::Tag(Tag { text }) => arena.tag(
                "a",
                vec![("data-tag".to_owned(), String::new())],
                arena.text(text.to_owned()),
            ),
            Self::Wikilink(Wikilink { link, target, name }) => {
                let destination = config.rewrite_wikilink(link, target.as_deref());

                arena.tag(
                    "a",
                    vec![("href".to_owned(), escape(&destination))],
                    arena.text(name.as_ref().unwrap_or(link).to_owned()),
                )
            }
            Self::Link(Link {
                destination,
                title,
                children,
            }) => {
                let destination = config.rewrite_link(destination, None);
                let mut attributes = vec![("href".to_owned(), escape(&destination))];
                if let Some(title) = title {
                    attributes.push(("title".to_owned(), escape(title)));
                }
                arena.tag("a", attributes, children.to_doc(config, context, arena))
            }
            Self::Image(Image {
                destination,
                title,
                alt,
            }) => {
                let mut attributes = vec![
                    ("src".to_owned(), escape(destination)),
                    ("alt".to_owned(), escape(alt)),
                ];
                if let Some(title) = title {
                    attributes.push(("title".to_owned(), escape(title)));
                }
                arena.tag("img", attributes, arena.nil())
            }
            Self::Autolink(link) => {
                let destination = config.rewrite_link(link, None);

                arena.tag(
                    "a",
                    vec![("href".to_owned(), escape(&destination))],
                    arena.text(escape(link)),
                )
            }
            Self::FootnoteReference(label) => {
                let Some(index) = context.get_footnote_index(label) else {
                    return arena.nil();
                };
                arena.tag(
                    "a",
                    vec![
                        ("class".to_owned(), "footnote-reference".to_owned()),
                        ("href".to_owned(), escape(&format!("#footnote-{index}"))),
                    ],
                    arena.text(format!("[{index}]")),
                )
            }
            Self::LinkReference(v) => {
                let definition: &LinkDefinition = match context.get_link_definition(&v.label) {
                    Some(v) => v,
                    None => return arena.nil(),
                };
                let destination = config.rewrite_link(&definition.destination, None);
                let mut attributes = vec![("href".to_owned(), escape(&destination))];
                if let Some(title) = &definition.title {
                    attributes.push(("title".to_owned(), escape(title)));
                }
                arena.tag("a", attributes, v.text.to_doc(config, context, arena))
            }
            Self::Empty => arena.nil(),
        }
    }
}
