"""
TODO:
    * Add a list option to only list defined webfont in settings;
"""
import os
import shutil
import tempfile
import zipfile

from django.conf import settings
from django.core.management.base import CommandError, BaseCommand
from django.template.loader import render_to_string

from ...store import WebfontStore
from ...utils import IcomoonSettingsError, extend_webfont_settings


class Command(BaseCommand):
    help = (
        "Icomoon CLI to deploy an Icomoon webfont from downloaded ZIP archive."
    )

    def add_arguments(self, parser):
        parser.add_argument(
            'webfont_name',
            nargs='?',
            default='Default',
            help=(
                "Webfont key name from 'settings.ICOMOON_MANIFEST_FILEPATH'. "
                "(default: %(default)s)"
            )
        )
        parser.add_argument(
            'archive_path',
            nargs='?',
            default='icomoon.zip',
            help="ZIP archive from icomoon to deploy. (default: %(default)s)"
        )

    def handle(self, *args, **options):
        self.webfont_name = options['webfont_name']
        self.archive_path = options['archive_path']
        self.css_templatepath = options.get('css_template',
                                            settings.ICOMOON_CSS_TEMPLATE)
        self.requirements = options.get('requirements',
                                        settings.ICOMOON_ARCHIVE_REQUIREMENT)
        self.verbosity = int(options.get('verbosity'))

        self._info("* Using webfont name: {}", self.webfont_name)

        # Get the current manifest for given webfont name
        if self.webfont_name not in settings.ICOMOON_WEBFONTS:
            choices = "', '".join(settings.ICOMOON_WEBFONTS.keys())
            msg = (
                "There is no defined webfont name '{name}' in your settings, "
                "choices are: '{choices}'"
            )
            self._error(msg, name=self.webfont_name, choices=choices)

        try:
            self.webfont_settings = extend_webfont_settings(
                settings.ICOMOON_WEBFONTS[self.webfont_name]
            )
        except IcomoonSettingsError as e:
            msg = "Invalid webfont settings for '{}': {}"
            self._error(msg, self.webfont_name, e.value)

        msg = "* Using webfont font dir: {}"
        self._info(msg, self.webfont_settings['fontdir_path'])

        self.deploy()
        self._info("Finished !")

    def print_output(self, msg, *args, **kwargs):
        self.stdout.write(msg.format(*args, **kwargs))

    def _error(self, msg, *args, **kwargs):
        raise CommandError(msg.format(*args, **kwargs))

    def _debug(self, msg, *args, **kwargs):
        if self.verbosity >= 3:
            self.print_output(msg, *args, **kwargs)

    def _warning(self, msg, *args, **kwargs):
        if self.verbosity >= 2:
            self.print_output(msg, *args, **kwargs)

    def _info(self, msg, *args, **kwargs):
        if self.verbosity >= 1:
            self.print_output(msg, *args, **kwargs)

    def deploy(self):
        """
        Open a ZIP archive, validate requirements then deploy the webfont into
        project static files
        """
        self._info("* Opening archive: {}", self.archive_path)
        if not os.path.exists(self.archive_path):
            self._error("Given path does not exists: {}", self.archive_path)

        with zipfile.ZipFile(self.archive_path, 'r') as zip_archive:
            font_dir = self.requirements['font_dir']+'/'
            allowed_extensions = [
                '.' + item for item in self.requirements['extensions']
            ]
            members = [member for member in zip_archive.namelist()]

            if settings.ICOMOON_MANIFEST_FILENAME not in members:
                msg = "Icomoon archive must contain a JSON manifest '{}'"
                raise self._error(msg, settings.ICOMOON_MANIFEST_FILENAME)

            if font_dir not in members:
                msg = "Icomoon archive must contain the font directory '{}'"
                raise self._error(msg, font_dir)

            # Scan for supported font files
            font_files = []
            for item in members:
                # Dont catch the font_dir itself nor sub directories, just
                # files with allowed extensions
                if (item.startswith(font_dir)
                    and not item.endswith('/')
                        and os.path.splitext(item)[-1] in allowed_extensions):
                    font_files.append(item)

            if not font_files:
                msg = "Font dir does not contain any supported format: {}"
                self._error(msg, ', '.join(allowed_extensions))
            else:
                msg = "* Finded font files in archive: {}"
                self._debug(msg, ', '.join(font_files))

            # Extract files from archive
            tmp_container, css_content = self.extract(zip_archive, font_files)

        # Install files
        self.install(tmp_container, font_dir, css_content)

    def extract(self, zip_archive, font_files):
        """
        Extract files to install
        """
        # Get a temp directory
        tmp_container = tempfile.mkdtemp(prefix='icomoon-tmp')
        self._debug("* Temporary dir for extracted archive: {}", tmp_container)

        # Extract manifest to temp directory
        zip_archive.extract(settings.ICOMOON_MANIFEST_FILENAME, tmp_container)
        # Then the font files
        for item in font_files:
            zip_archive.extract(item, tmp_container)

        # Get manifest for icon map
        webfont_store = WebfontStore(settings.ICOMOON_MANIFEST_FILENAME)
        webfont_store.get(self.webfont_name, {
            'fontdir_path': tmp_container,
        })
        icons = webfont_store.get_manifests()[self.webfont_name]

        # Render CSS icon part
        css_content = self.render_css(self.css_templatepath, icons)

        return tmp_container, css_content

    def render_css(self, template_path, icons):
        """
        Render CSS template to contain the icons stylesheet map
        """
        return render_to_string(template_path, {'icons': icons})

    def install(self, tmp_container, font_tmpdir, css_content):
        """
        Install extracted files and builded css
        """
        # Write builded css file to its destination
        with open(self.webfont_settings['csspart_path'], 'w') as css_file:
            css_file.write(css_content)

        # Clean previous font dir
        if os.path.exists(self.webfont_settings['fontdir_path']):
            shutil.rmtree(self.webfont_settings['fontdir_path'])
        font_srcdir = os.path.join(tmp_container, font_tmpdir)
        self._debug("* Installing font")
        self._debug("  - From: {}", font_srcdir)
        self._debug("  - To: {}", self.webfont_settings['fontdir_path'])

        shutil.copytree(font_srcdir, self.webfont_settings['fontdir_path'])

        # Copy new manifest into font dir
        manifest_src = os.path.join(tmp_container,
                                    settings.ICOMOON_MANIFEST_FILENAME)
        self._debug("* Installing manifest")
        self._debug("  - From: {}", manifest_src)
        self._debug("  - To: {}", self.webfont_settings['fontdir_path'])
        shutil.copy(manifest_src, self.webfont_settings['fontdir_path'])

        # Remove temp directory when all is done
        self._debug("* Removing temporary dir")
        shutil.rmtree(tmp_container)
