import os
import shutil
from copy import deepcopy

from .configuration import Configuration
from .template import Template
from .database import Database
from .parser import MDParser
from .page import Page
from .discovery import get_file_list, get_dir_structure

class HTMLBuilder:
    def __init__(self, config: Configuration,
                 template: Template,
                 db: Database):
        self.src: str = config.src
        self.dst: str = config.dst
        self.base_url: str = config.base_url
        self.dformat: str = config.dformat
        self.l_dformat: str = config.l_dformat
        self.lsep_dformat: str = config.lsep_dformat
        self.force: bool = config.force

        self.template: Template = template
        self.db: Database = db

        self.dirs: list[str] = None
        self.md_files: list[str] = None
        self.html_files: list[str] = None

        self.all_pages: list[Page] = None


    def build(self) -> None:
        self.dirs = get_dir_structure(self.src, ['templates'])
        self.md_files = get_file_list(self.src, ['.md'], ['templates'])
        self.html_files = get_file_list(self.src, ['.html'], ['templates'])

        self.__create_dir_structure()
        self.__copy_html_files()

        parser: MDParser = MDParser(self.src, self.md_files, self.db)
        parser.parse()

        # just to be able to extract all pages out of this class
        self.all_pages = parser.all_pages

        # create the article index
        self.__create_article_index(parser.all_tags, parser.all_pages)

        # create each category of html pages
        # check if all pages should be created
        if self.force:
            self.__create_articles(parser.all_pages)
        else:
            self.__create_articles(parser.updated_pages)
        self.__create_tags(parser.all_tags, parser.all_pages)


    def get_pages(self) -> list[Page]:
        return self.all_pages


    def __create_dir_structure(self) -> None:
        for d in self.dirs:
            # for the dir structure,
            # doesn't matter if the dir already exists
            try:
                os.makedirs(os.path.join(self.dst, d))
            except FileExistsError:
                pass


    def __copy_html_files(self) -> None:
        src_file: str = None
        dst_file: str = None

        for f in self.html_files:
            src_file = os.path.join(self.src, f)
            dst_file = os.path.join(self.dst, f)

            # only copy files if they have been modified (or are new)
            if self.db.update(src_file, remove=f'{self.src}/'):
                shutil.copy2(src_file, dst_file)


    # this is really similar to create_tag (singular)
    def __create_article_index(self, tags: list[str],
                               pages: list[Page]) -> None:
        # make temporary template
        t: Template = deepcopy(self.template)

        # do basic replacements
        # get page and tag list formated, both functions do replacements
        p_list: list[str] = self.__get_pages_formatted(pages, t)
        t_list: list[str] = self.__get_tags_formatted(tags, t)
        # common
        t.header = t.header.replace("$$LANG", 'en')
        t.header = t.header.replace('$$TITLE', f'Index')

        with open(os.path.join(self.dst, 'index.html'), 'w') as f:
            f.write(t.header)
            f.write(t.articles.header)

            f.write(t.tags.list_header)
            for tag in t_list:
                f.write(tag)
            f.write(t.tags.list_footer)

            f.write(t.articles.list_header)
            for page in p_list:
                f.write(page)
            f.write(t.articles.list_footer)

            f.write(t.articles.footer)
            f.write(t.footer)


    def __create_articles(self, pages: list[Page]) -> None:
        for p in pages:
            self.__create_article(p)


    def __create_article(self, page: Page) -> None:
        # TODO: create better solution for replace
        # make temporary template
        t: Template = deepcopy(self.template)

        # prepare html file name
        f_name: str = page.name
        f_name = f_name.replace('.md', '.html')

        # get timestamps
        c_date: str = page.c_datetime.strftime(self.dformat)
        m_date: str = None
        if page.m_datetime is not None:
            m_date: str = page.m_datetime.strftime(self.dformat)

        # do basic replacements
        # get tag list formatted (some replacements done inside
        # get_tags_formatted)
        t_list: list[str] = None
        if page.tags is not None:
            t_list = self.__get_tags_formatted(page.tags, t)

        # common
        t.header = t.header.replace("$$LANG", page.lang)
        t.header = t.header.replace('$$TITLE', page.title)

        # article header
        t.article.header = t.article.header.replace('$$TITLE', page.title)
        t.article.header = t.article.header.replace('$$AUTHOR', page.author)
        t.article.header = t.article.header.replace('$$CTIME', c_date)
        if m_date is not None:
            t.article.header = t.article.header.replace('$$MTIME', m_date)
        else:
            t.article.header = t.article.header.replace('$$MTIME', '')

        # article footer (same replaces as header)
        t.article.footer = t.article.footer.replace('$$TITLE', page.title)
        t.article.footer = t.article.footer.replace('$$AUTHOR', page.author)
        t.article.footer = t.article.footer.replace('$$CTIME', c_date)
        if m_date is not None:
            t.article.footer = t.article.footer.replace('$$MTIME', m_date)
        else:
            t.article.footer = t.article.footer.replace('$$MTIME', '')


        with open(os.path.join(self.dst, f_name), 'w') as f:
            f.write(t.header)
            f.write(t.article.header)
            f.write(page.html)

            if t_list is not None:
                f.write(t.tags.list_header)
                for tag in t_list:
                    f.write(tag)
                f.write(t.tags.list_footer)

            f.write(t.article.footer)
            f.write(t.footer)


    def __get_tags_formatted(self, tags: list[str],
                             template: Template) -> list[str]:
        tag_amount: int = len(tags)
        tags_formatted: list[str] = []
        for i, t in enumerate(tags):
            # t_e=tag entry
            t_e: str = template.tags.list_entry
            t_e = t_e.replace('$$URL',
                              f'{self.base_url}/tag/@{t}.html')
            t_e = t_e.replace('$$NAME', t)

            tags_formatted.append(t_e)
            if i != tag_amount - 1:
                tags_formatted.append(template.tags.list_separator)

        return tags_formatted


    def __create_tags(self, tags: list[str],
                      pages: list[Page]) -> None:
        for t in tags:
            # get a list of all pages that have current tag
            tag_pages: list[Page] = []
            for p in pages:
                if p.tags is not None and t in p.tags:
                    tag_pages.append(p)

            # build tag page
            self.__create_tag(t, tag_pages)

            # clean list of pages with current tag
            tag_pages = []


    def __create_tag(self, tag: str,
                     pages: list[Page]) -> None:
        # TODO: create better solution for replace
        # make temporary template
        t: Template = deepcopy(self.template)

        # do basic replacements
        # get page list formated (some replacements done inside
        # get_pages_formatted)
        p_list: list[str] = self.__get_pages_formatted(pages, t)
        # common
        t.header = t.header.replace("$$LANG", 'en')
        t.header = t.header.replace('$$TITLE', f'Posts filtered by: {tag}')

        # tag header
        tag_url: str = f'{self.base_url}/tag/@{tag}.html'
        t.tags.header = t.tags.header.replace('$$NAME', tag)

        with open(os.path.join(self.dst, f'tag/@{tag}.html'), 'w') as f:
            f.write(t.header)
            f.write(t.tags.header)

            f.write(t.articles.list_header)
            for p in p_list:
                f.write(p)
            f.write(t.articles.list_footer)

            f.write(t.tags.footer)
            f.write(t.footer)


    def __get_pages_formatted(self, pages: list[Page],
                              template: Template) -> list[str]:
        month_year: str = '-'
        pages_formatted: list[str] = []
        for p in pages:
            # check if the monthly separator should be included
            c_month_year: str = p.c_datetime.strftime(self.lsep_dformat)
            if c_month_year != month_year:
                month_year = c_month_year

                month_sep: str = template.articles.list_separator
                month_sep = month_sep.replace('$$SEP', month_year)

                pages_formatted.append(month_sep)

            f_name: str = p.name
            f_name = f_name.replace('.md', '.html')

            # p_e=page entry
            p_e: str = template.articles.list_entry
            p_e = p_e.replace('$$URL', f'{self.base_url}/{f_name}')
            p_e = p_e.replace('$$DATE', p.c_datetime.strftime(self.l_dformat))
            p_e = p_e.replace('$$TITLE', p.title)

            pages_formatted.append(p_e)

        return pages_formatted
