use pulldown_cmark::{CowStr, Event, Parser, Tag};

use crate::setting::{InlineStyle, ListStyle, TerminalTypesetter, Typesetter};

#[derive(Debug)]
pub enum Child<'a> {
    Node(Node<'a>),
    Text(CowStr<'a>),
}

#[derive(Debug)]
pub struct Node<'a> {
    tag: Tag<'a>,
    content: Content<'a>,
}

#[derive(Debug, Default)]
pub struct Content<'a> {
    children: Vec<Child<'a>>,
}

impl<'a> Node<'a> {
    pub fn new(tag: Tag<'a>) -> Self {
        Self {
            tag,
            content: Content::default(),
        }
    }

    fn print(&self, printer: &mut TerminalTypesetter) {
        match self.tag {
            Tag::List(Some(n)) => {
                printer.push_list(ListStyle::Numbered(n as usize));
                self.content.print(printer);
                printer.pop_list();
            }
            Tag::List(None) => {
                printer.push_list(ListStyle::Bullet('•'));
                self.content.print(printer);
                printer.pop_list();
            }
            Tag::Paragraph => {
                printer.push_paragraph();
                self.content.print(printer);
                printer.pop_paragraph();
            }
            Tag::BlockQuote(_) => {
                printer.push_blockquote();
                self.content.print(printer);
                printer.pop_blockquote();
            }
            Tag::Item => {
                printer.push_list_item();
                self.content.print(printer);
                printer.pop_list_item();
            }
            Tag::CodeBlock(_) => {
                printer.push_code_block();
                self.content.print(printer);
                printer.pop_code_block();
            }
            Tag::Strong => {
                printer.push_style(InlineStyle::Bold);
                self.content.print(printer);
                printer.pop_style();
            }
            Tag::Emphasis => {
                printer.push_style(InlineStyle::Italic);
                self.content.print(printer);
                printer.pop_style();
            }
            _ => {
                self.content.print(printer);
            }
        }
    }
}

impl<'a> Content<'a> {
    pub fn print(&self, printer: &mut TerminalTypesetter) {
        for (i, child) in self.children.iter().enumerate() {
            match child {
                Child::Node(node) => node.print(printer),
                Child::Text(text) => printer.append_text(i != 0, text),
            }
        }
    }

    pub fn parse(&mut self, parser: &mut Parser<'a>) {
        loop {
            let Some(event) = parser.next() else {
                break;
            };
            match event {
                // End
                Event::End(_) => {
                    break;
                }
                // Child
                Event::Start(tag) => {
                    let mut child = Node::new(tag);
                    child.content.parse(parser);
                    self.children.push(Child::Node(child));
                }
                Event::Text(text) => {
                    self.children.push(Child::Text(text));
                }
                _ => (),
            }
        }
    }
}
