import logging
import os
from contextlib import ContextDecorator

from jinja2 import Environment, PackageLoader, Template
from medikit import settings
from medikit.events import attach_subscriptions
from medikit.file import File
from medikit.settings import DEFAULT_FEATURES
from medikit.utils import is_identifier, format_file_content
from yapf import yapf_api

from mondrian import term

ABSOLUTE_PRIORITY = -100
HIGH_PRIORITY = -80
MEDIUM_PRIORITY = -60
LOW_PRIORITY = -60
SUPPORT_PRIORITY = -20
LAST_PRIORITY = 100


class Feature(object):
    _jinja_environment = None

    requires = set()
    conflicts = set()

    file_type = staticmethod(File)

    __usage__ = None

    class Config(ContextDecorator):
        def __enter__(self):
            return self

        def __exit__(self, *exc):
            return False

    def __init__(self, dispatcher):
        """

        :param LoggingDispatcher dispatcher:
        """
        self.dispatcher = dispatcher
        self.configure()
        attach_subscriptions(self, self.dispatcher)

    def configure(self):
        pass

    def file(self, *args, **kwargs):
        return self.file_type(self.dispatcher, *args, **kwargs)

    @property
    def __name__(self):
        return type(self).__name__

    @property
    def __shortname__(self):
        return self.__name__.replace("Feature", "").lower()

    @property
    def jinja(self):
        if type(self)._jinja_environment is None:
            type(self)._jinja_environment = Environment(loader=PackageLoader(__name__, "template"))
        return type(self)._jinja_environment

    def _log_file(self, target, override, content=()):
        self.dispatcher.info(
            term.bold(term.red("W!") if override else term.green("W?")), target, "({} bytes)".format(len(content))
        )

    def render(self, template, context=None):
        context = context or {}
        os.path.join(os.path.dirname(__file__), "template")
        return self.jinja.get_template(template).render(**(context or {}))

    def render_file(self, target, template, context=None, *, executable=False, override=False, force_python=False):
        with self.file(target, executable=executable, override=override) as f:
            content = format_file_content(self.render(template, context))
            if force_python or target.endswith(".py"):
                content, modified = yapf_api.FormatCode(content, filename=target)
            f.write(content)
            self._log_file(target, override, content)

    def render_file_inline(self, target, template_string, context=None, override=False, force_python=False):
        with self.file(target, override=override) as f:
            content = format_file_content(Template(template_string).render(**(context or {})))
            if force_python or target.endswith(".py"):
                content, modified = yapf_api.FormatCode(
                    content, filename=target, style_config=settings.YAPF_STYLE_CONFIG
                )
            f.write(content)
            self._log_file(target, override, content)

    def render_empty_files(self, *targets, **kwargs):
        override = kwargs.pop("override", False)
        for target in targets:
            with self.file(target, override=override) as f:
                self._log_file(target, override)


class ProjectInitializer(Feature):
    def __init__(self, dispatcher, options):
        super().__init__(dispatcher)
        self.options = options

    def execute(self):
        context = {}

        if self.options.get("name"):
            if not is_identifier(self.options["name"]):
                raise RuntimeError("Invalid package name {!r}.".format(self.options["name"]))
            context["name"] = self.options["name"]
            logging.info("name = %s", context["name"])
        else:
            context["name"] = input("Name: ")
            while not is_identifier(context["name"]):
                logging.error("Invalid name. Please only use valid python identifiers.")
                context["name"] = input("Name: ")

        if self.options.get("description"):
            context["description"] = self.options["description"]
        else:
            context["description"] = input("Description: ")

        if self.options.get("license"):
            context["license"] = self.options["license"]
        else:
            context["license"] = (
                input("License [Apache License, Version 2.0]: ").strip() or "Apache License, Version 2.0"
            )

        context["url"] = ""
        context["download_url"] = ""
        context["author"] = ""
        context["author_email"] = ""

        context["features"] = DEFAULT_FEATURES
        if self.options.get("features"):
            context["features"] = context["features"].union(self.options["features"])

        context["requirements"] = self.options.get("requirements", [])

        self.render_file("Projectfile", "Projectfile.j2", context, override=True, force_python=True)
