from .content_parser import parse_content
from slugify import slugify
import logging
import more_itertools
import typing
from pathlib import Path
from typing import List

from jinja2 import Markup
from markdown2 import markdown

from ._type_hint_helpers import PathString

# some attributes will need to be protected from manipulation


class Page:
    """
    Base component used to make web pages.

    All components that represent content in HTML, XML, or JSON generated by
    Render Engine should be a Page object.

    Pages can be rendered directly from a template or from a file.

    Page objects can be used to extend existing page objects.


    Examples:
        ```
        # Basic Page with No Template Variables
        @site.register_route('basic_page.html')
        class BasicPage(Page):
            template = 'template_file.html' # user provided template

        # Basic Page with Variables
        @site.register_route('page_with_vars')
        class PageWithVars(Page):
            title = 'Site Title'

        # Page Loading from File
        @site.register_route('page_from_file')
        class PageFromFile(Page):
            content_path = 'index.md' # loaded from content path can be '.md' or '.html'

        # Page Inherited from Other Page
        @site.register_route('basic_page.html')
        class BasicPage(Page):
            template = 'template_file.html' # user provided template
            title = 'Base Page'

        @site.register_route('other_page.html')
        class InheritingPage(Page):
            # template will be inherited from the BasicPage
            title = 'Inherited Page'
        ```

    Attributes:
        engine: str, optional
            The engine that the Site should refer to or the site's default engine
        template: str
            The template that the Site should refer to. If empty, use site's default
        routes: List[str]
            all routes that the file should be created at. default []
        content_path: List[PathString], optional
            the filepath to load content from.
        slug: str
            The engine's default route filename
        content: str
            preprocessed text stripped from initialized content. This will not
            include any defined attrs
        html: str
            text converted to html from _content
    """

    engine: typing.Optional[str] = ""  # (default) inherits from Site
    template: typing.Optional[str] = ""  # (default) inherits from Site
    routes = [""]  # create page at each route
    list_attrs: typing.Optional[typing.Union[str]] = []  # comma-delim listed attrs
    no_index: bool = False  # hides from search index
    matcher: str = r"(^\w+: \b.+$)"  # expression to find attrs/vals
    content_path: typing.Optional[str] = ""
    base_content: typing.Optional[str] = ""
    title: typing.Optional[str] = ""
    slug: typing.Optional[str] = ""
    markdown_extras = ["fenced-code-blocks", "footnotes"]

    def __init__(
        self,
    ):

        if self.content_path:
            content = Path(self.content_path).read_text()

            valid_attrs, self.base_content = parse_content(
                content,
                matcher=self.matcher,
            )

            for attr in valid_attrs:
                name, value = attr.split(": ", maxsplit=1)

                # comma delimit attributes.
                if name.lower() in self.list_attrs:
                    value = [attrval.lower() for attrval in value.split(", ")]

                else:
                    value = value.strip()

                setattr(self, name.lower(), value)

        if not self.title:
            self.title = self.__class__.__name__

        if not self.slug:
            self.slug = self.title or self.__class__.__name__

        self.slug = slugify(self.slug)

    @property
    def url(self):
        return f"{self.routes[0]}/{self.slug}"

    @classmethod
    def from_content_path(cls, filepath, **kwargs):
        class NewPage(cls):
            content_path = filepath

            def __init__(self, markdown_extras: typing.List[str] = []):
                for extra in markdown_extras:
                    if extra not in self.markdown_extras:
                        self.markdown_extras.append(extra)

                super().__init__()

        newpage = NewPage(**kwargs)

        return newpage

    @property
    def html(self):
        """Text from self.content converted to html"""

        if self.base_content:
            return markdown(self.base_content, extras=self.markdown_extras)

        else:
            return ""

    @property
    def markup(self):
        """html = rendered html (not marked up). Is `None` if `content == None`"""
        if self.html:
            return Markup(self.html)

        else:
            return ""

    @property
    def content(self):
        return self.markup
