from datetime import datetime 
import os
import sys
import shutil
from collections import defaultdict
import markdown
import frontmatter
from pathlib import Path
from logger import logger
from config import config
#from jinja_env import env, content_item_template, index_template
from jinja2 import Environment, FileSystemLoader

# Get the script base_dir and check for templates dir
if Path(f'themes/{config.theme}/templates').exists():
    base_dir = "."
elif getattr(sys, 'frozen', False):
    # Running fron Pyinstaller-packed binary
    base_dir =  Path(sys._MEIPASS)
else:
    # Running as a plain Python app
    base_dir = Path(__file__).resolve().parent    

# Prepare template rendering engine
if Path(f"themes/{config.theme}/templates").exists():
    # Use the templates from the initialized site instance and the installed theme 
    jinja_env_dir = f"themes/{config.theme}/templates"
    logger.debug(f"Using locally initialized templates from {jinja_env_dir}")
else:
    # Use default templates from the default theme shipped with the app
    # i.e. PyInstaller-packed single executable
    logger.debug("Using shipped default temlpates.")
    jinja_env_dir = f"{base_dir}/themes/default/templates"
env = Environment(loader=FileSystemLoader(f"{jinja_env_dir}"))
env.globals['header_image'] = f"{base_dir}/themes/{config.theme}/static/images/header.jpg"
index_template = env.get_template("index.html")
content_item_template = env.get_template("content_item.html")

class ContentItem:
    def render_content(self, categories, target_file = False):
        if target_file:
            self.target_file = Path(target_file)
        logger.debug(f"Rendering {self.source_file} to {self.target_file}")
        if hasattr(config, "footer"):
            footer_data = config.footer
        else:
            footer_data = ''
        try:
            if self.image_file and self.image_file.exists():
                image_targetfile = Path(f"{config.output_dir}/{config.static_dir}/images/{self.image_file.name}")
                logger.debug(f"Copying {self.image_file} to {image_targetfile}")
                shutil.copyfile(self.image_file, image_targetfile)
                self.image_file = f"{self.image_file.stem}.jpg"
            if self.css_file and self.css_file.exists():
                css_targetfile = Path(f"{config.output_dir}/{config.static_dir}/css/{self.css_file.name}")
                logger.debug(f"Copying {self.css_file} to {css_targetfile}")
                shutil.copyfile(self.css_file, css_targetfile)
            if self.js_file and self.js_file.exists():
                js_targetfile = Path(f"{config.output_dir}/{config.static_dir}/js/{self.js_file.name}")
                logger.debug(f"Copying {self.js_file} to {js_targetfile}")
                shutil.copyfile(self.js_file, js_targetfile)
            with self.target_file.open("w", encoding="utf-8") as f:
                f.write(content_item_template.render(content_item = self, page_title = f"{config.site_name}: {self.title}", parent_path = '../', categories = categories, footer_data = footer_data))
        except Exception as e:
            logger.error(f"Renderer: {e}")

    def parse_content(self):
        logger.debug(f"Parsing file {self.source_file}")
        try:
            self.source_file = Path(self.source_file)
            self.parent_dir = self.source_file.parent # Most likely './content'
            self.slug = self.source_file.stem
            self.target_file = Path(f"{config.output_dir}/{self.source_file.parent}/{self.source_file.stem}.html")
            self.data = frontmatter.load(self.source_file)
            self.preview = self.data.content.replace('\n', '<br>')[:300]
            self.title = self.data.get("title", self.slug)
            self.omit_second_title = self.data.get("omit_second_title", False)
            self.date = self.data.get("date", "2000-01-01T00:00:00+03:00")
            self.categories = [c for c in self.data.get("categories", []) if c != 'default']
            self.hidden = self.data.get("hidden", str(False))
            self.data.content = self.data.content.replace('\n', '  \n') # For markdown newline rendering
            self.html = markdown.markdown(self.data.content)
            cover_image_path = Path(f"{self.source_file.parent}/{self.source_file.stem}.jpg") 
            self.image_file = cover_image_path if cover_image_path.exists() else ""
            css_filepath = Path(f"{self.source_file.parent}/{self.source_file.stem}.css")
            self.css_file = css_filepath if css_filepath.exists() else ""
            js_filepath = Path(f"{self.source_file.parent}/{self.source_file.stem}.js")
            self.js_file = js_filepath if js_filepath.exists() else ""
            logger.debug(f"CCC {self.source_file.parts} : {self.source_file.parent} / {self.source_file.stem}.jpg")
        except Exception as e:
            logger.error(f"Parser error, {e}")

    def create_content(self):
        with open(self.source_file, mode="w", encoding="utf-8") as f:
            f.writelines([
                "---\n", 
                f"title: {self.source_file.stem}\n", 
                f"date: '{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}'\n",
                "description: ''\n",
                "author: ''\n",
                "categories: ['default']\n",
                "hidden: False\n",
                "omit_second_title: False\n",
                "---\n",
                "\n\n\n"
            ])

    def __init__(self, file):
        self.source_file = file

class Site:
    def __init__(self):
        self.output_dir = Path(config.output_dir)
        self.content_dir = Path(config.content_dir)
        self.static_dir = Path(config.static_dir)
        self.templates_dir = Path(config.templates_dir)
        self.images_dir = Path(f"{config.static_dir}/images")
        self.css_dir = Path(f"{config.static_dir}/css")
        self.js_dir = Path(f"{config.static_dir}/js")
        self.output_dir = Path(config.output_dir)
        self.content_items = []
        self.categories = defaultdict(list)

    def init_site(self):
        # Exit if current directory not empty
        if os.path.isdir('.') and os.listdir('.'):
            logger.error("Current directory is not empty.")
            sys.exit(1)
        logger.info("Initializing new site")
        config.create_default_config()
        # Create directories
        for subdir in [self.content_dir]:
            os.makedirs(subdir, exist_ok=True)

        # Copy default theme
        shutil.copytree(f"{base_dir}/themes/default", "themes/default", dirs_exist_ok=True)

    def get_content_items(self):
        logger.debug("Getting content items")
        self.get_content_items = []
        logger.debug(f"Scanning {Path(config.content_dir)}")
        for md_file in Path(config.content_dir).glob("*.md"):
            content_item = ContentItem(md_file)
            content_item.parse_content()
            self.content_items.append(content_item)
        self.content_items.sort(key=lambda i: (-datetime.fromisoformat(i.date).timestamp(), i.title))

    def map_categories(self):
        for content_item in self.content_items:
            for category in content_item.categories:
                if not category == "default":
                    self.categories[category].append(content_item.slug)

    def build(self):
        # Recreate the output dir if exists
        if self.output_dir.exists():
            shutil.rmtree(self.output_dir)
        self.output_dir.mkdir()

        # Create public subdirs
        subdirs = ["categories", "content", "static"]
        for subdir in subdirs:
            subdir = self.output_dir / subdir
            subdir.mkdir(parents=True, exist_ok=True)

        # Copy theme's static dir to 'public/static
        shutil.copytree(f"{base_dir}/themes/{config.theme}/static", f"{self.output_dir}/static", dirs_exist_ok=True)

        # Get content items
        self.get_content_items()

        # Build categories map
        self.map_categories()

        # Render the content files
        for content_item in self.content_items:
            content_item.render_content(categories = self.categories)

        # Render the about file
        about_content = ContentItem(Path(f'themes/{config.theme}/content/about.md'))
        about_content.parse_content()
        about_content.render_content(categories = self.categories, target_file='public/static/about.html')

        # Render the index file
        if hasattr(config, "footer"):
            footer_data = config.footer
        else:
            footer_data = ''
        visible_content_items = [c for c in self.content_items if c.data.get("hidden") != True]
        with (self.output_dir / "index.html").open("w", encoding="utf-8") as f:
            f.write(index_template.render(page_title = config.site_name, content_items=visible_content_items, categories = self.categories, footer_data = footer_data))

        # Render the categories indices
        if hasattr(config, "footer"):
            footer_data = config.footer
        else:
            footer_data = ''
        visible_content_items = [c for c in self.content_items if c.data.get("hidden") != True]
        for category in self.categories:
            category_index = Path(f"{self.output_dir}/categories/{category}.html")
            category_items = [i for i in visible_content_items if category in i.data.get("categories")]
            with (category_index).open(mode="w", encoding="utf-8") as f:
                f.write(index_template.render(page_title=f"{config.site_name}: {category}", content_items=category_items, categories = self.categories, parent_path = '../', footer_data = footer_data))

        logger.info(f"Created {len(self.content_items)} content items.")        
    