import os
import glob
import yaml
import markdown
import pydantic
from typing import Optional
from dotmap import DotMap
from datetime import date

from .common import filepath_or_string, SiteConfig
from .templating import format_html_template

class ArticleMetadata(pydantic.BaseModel):
    title: str
    date: date
    published: bool
    tags: list
    author: Optional[str] = None
    lastmod: Optional[date] = None
    thumbnail: Optional[str] = None
    

def load_markdown(md: str) -> tuple[ArticleMetadata|None, str]:
    '''Loads a Markdown file into a (metadata: ArticleMetadata, content: str) pair.'''
    
    # Load the file contents if a filepath is specified, and strip document delimiters ('---').
    md = filepath_or_string(md).strip().strip('---').strip()

    # If there is no `---` delimiter, then the article has no metadata.
    if '---' not in md.strip('---'):
        return None, md
    
    # Split the metadata from the contents.
    [raw_metadata, raw_article] = md.split('---')
    
    # Use YAML to parse the metadata.
    metadata = yaml.safe_load(raw_metadata)

    # Convert the contents to a HTML string.
    content = markdown.markdown(raw_article)

    return ArticleMetadata(**metadata), content


def build_index(site: SiteConfig) -> dict:
    '''Loads the sites articles into an index mapping the filename stem
    to a (metadata: dict, content: str) tuple.'''

    index = {}

    # Expand any globbed expressions.
    expanded_article_list = []
    for a in site.articles or {}:
        expanded_article_list.extend(
            # Article paths are defined relative to the build cache; construct the full path.
            glob.glob(f'{site.build_cache}/{a.lstrip("/")}')
        )
            

    for article in expanded_article_list:
        metadata, content = load_markdown(article)

        # Skip unpublished articles.
        if not metadata.published:
            continue

        article_filestem = os.path.splitext(os.path.basename(article))[0]
        index[article_filestem] = (metadata, content)
    
    return index


def format_article_tags(tags: list[str], template = 'templates/components/blog_tag.html') -> list[str]:
    '''Generates HTML article tag components from a list of tag names.'''
    return [
        format_html_template(template, tag_name = t) for t in tags
    ]


def build_articles(site: SiteConfig, index: dict[str, tuple[ArticleMetadata, str]], templates: DotMap):
    '''Generates HTML files for all of a given site's Markdown articles
    by interpolating the contents and metadata into the HTML templates.'''

    for filestem, (metadata, content) in index.items():
        article = format_html_template(
            templates.components.blog_article,
            content = content,
            blog_tags = ' '.join(format_article_tags(metadata.tags)),
            metadata = metadata,
            templates = templates
        )

        page = format_html_template(
            templates.pages.default,
            content = article,
            templates = templates
            
        )

        with open(f'{site.web_root.rstrip('/')}/{filestem}.html', 'w') as f:
            f.write(page)

        