use std::path::PathBuf;

use crate::error::CustomError;

use super::registry::Registry;
use super::IDocumentElement;
use super::IHeading;
use super::ISection;

#[derive(Debug)]
pub(crate) struct BlogPostPage {
    /// Relative path from the root of the blog post.
    pub(crate) path: PathBuf,

    pub(crate) title: Option<String>,

    pub(crate) children: Vec<IDocumentElement>,
}

impl BlogPostPage {
    pub(crate) async fn new<'parse, P: Into<PathBuf>>(
        path: P,
        registry: &mut Registry<'parse>,
        document: &organic::types::Document<'parse>,
    ) -> Result<BlogPostPage, CustomError> {
        let path = path.into();
        let mut children = Vec::new();
        if let Some(section) = document.zeroth_section.as_ref() {
            children.push(IDocumentElement::Section(
                ISection::new(registry, section).await?,
            ));
        }
        for heading in document.children.iter() {
            children.push(IDocumentElement::Heading(
                IHeading::new(registry, heading).await?,
            ));
        }

        Ok(BlogPostPage {
            path,
            title: get_title(&document),
            children,
        })
    }

    /// Get the output path relative to the post directory.
    pub(crate) fn get_output_path(&self) -> PathBuf {
        let mut ret = self.path.clone();
        ret.set_extension("html");
        ret
    }
}

fn get_title(document: &organic::types::Document<'_>) -> Option<String> {
    organic::types::AstNode::from(document)
        .iter_all_ast_nodes()
        .filter_map(|node| match node {
            organic::types::AstNode::Keyword(kw) if kw.key.eq_ignore_ascii_case("title") => {
                Some(kw)
            }
            _ => None,
        })
        .last()
        .map(|kw| kw.value.to_owned())
}