from werkzeug.utils import cached_property

from app import format_notification_type


class TemplateList:
    def __init__(
        self,
        service,
        template_type="all",
        template_folder_id=None,
        user=None,
    ):
        self.service = service
        self.template_type = template_type
        self.template_folder_id = template_folder_id
        self.user = user

    def __iter__(self):
        yield from self.items

    @cached_property
    def items(self):
        return list(
            self.get_templates_and_folders(
                self.template_type, self.template_folder_id, ancestors=[]
            )
        )

    def get_templates_and_folders(self, template_type, template_folder_id, ancestors):
        for item in self.get_template_folders(
            template_type,
            template_folder_id,
        ):
            yield TemplateListFolder(
                item,
                folders=self.get_template_folders(
                    template_type,
                    item["id"],
                ),
                templates=self.get_templates(template_type, item["id"]),
                ancestors=ancestors,
                service_id=self.service.id,
            )
            for sub_item in self.get_templates_and_folders(
                template_type, item["id"], ancestors + [item]
            ):
                yield sub_item

        for item in self.get_templates(
            template_type,
            template_folder_id,
        ):
            yield TemplateListTemplate(
                item,
                ancestors=ancestors,
                service_id=self.service.id,
            )

    def get_templates(self, template_type="all", template_folder_id=None):
        if self.user and template_folder_id:
            folder = self.service.get_template_folder(template_folder_id)
            if not self.user.has_template_folder_permission(folder):
                return []

        if isinstance(template_type, str):
            template_type = [template_type]
        if template_folder_id:
            template_folder_id = str(template_folder_id)
        return [
            template
            for template in self.service.all_templates
            if (set(template_type) & {"all", template["template_type"]})
            and template.get("folder") == template_folder_id
        ]

    @cached_property
    def user_template_folders(self):
        """Returns a modified list of folders a user has permission to view

        For each folder, we do the following:
        - if user has no permission to view the folder, skip it
        - if folder is visible and its parent is visible, we add it to the list of folders
        we later return without modifying anything
        - if folder is visible, but the parent is not, we iterate through the parent until we
        either find a visible parent or reach root folder. On each iteration we concatenate
        invisible parent folder name to the front of our folder name, modifying the name, and we
        change parent_folder_id attribute to a higher level parent. This flattens the path to the
        folder making sure it displays in the closest visible parent.

        """
        user_folders = []
        for folder in self.service.all_template_folders:
            if not self.user.has_template_folder_permission(
                folder, service=self.service
            ):
                continue
            parent = self.service.get_template_folder(folder["parent_id"])
            if self.user.has_template_folder_permission(parent, service=self.service):
                user_folders.append(folder)
            else:
                folder_attrs = {
                    "id": folder["id"],
                    "name": folder["name"],
                    "parent_id": folder["parent_id"],
                    "users_with_permission": folder["users_with_permission"],
                }
                while folder_attrs["parent_id"] is not None:
                    folder_attrs["name"] = [
                        parent["name"],
                        folder_attrs["name"],
                    ]
                    if parent["parent_id"] is None:
                        folder_attrs["parent_id"] = None
                    else:
                        parent = self.service.get_template_folder(parent["parent_id"])
                        folder_attrs["parent_id"] = parent.get("id", None)
                        if self.user.has_template_folder_permission(
                            parent, service=self.service
                        ):
                            break
                user_folders.append(folder_attrs)
        return user_folders

    def get_template_folders(self, template_type="all", parent_folder_id=None):
        if self.user:
            folders = self.user_template_folders
        else:
            folders = self.service.all_template_folders
        if parent_folder_id:
            parent_folder_id = str(parent_folder_id)

        return [
            folder
            for folder in folders
            if (
                folder["parent_id"] == parent_folder_id
                and self.is_folder_visible(folder["id"], template_type)
            )
        ]

    def is_folder_visible(self, template_folder_id, template_type="all"):
        if template_type == "all":
            return True

        if self.get_templates(template_type, template_folder_id):
            return True

        if any(
            self.is_folder_visible(child_folder["id"], template_type)
            for child_folder in self.get_template_folders(
                template_type, template_folder_id
            )
        ):
            return True

        return False

    @property
    def as_id_and_name(self):
        return [(item.id, item.name) for item in self]

    @property
    def templates_to_show(self):
        return any(self)

    @property
    def folder_is_empty(self):
        return not any(
            self.get_templates_and_folders("all", self.template_folder_id, [])
        )


class ServiceTemplateList(TemplateList):
    def __iter__(self):
        template_list_service = TemplateListService(
            self.service,
            templates=self.get_templates(
                template_folder_id=None,
            ),
            folders=self.get_template_folders(
                parent_folder_id=None,
            ),
        )

        yield template_list_service

        yield from self.get_templates_and_folders(
            self.template_type,
            self.template_folder_id,
            ancestors=[template_list_service],
        )


class TemplateLists:
    def __init__(self, user):
        self.services = sorted(
            user.services,
            key=lambda service: service.name.lower(),
        )
        self.user = user

    def __iter__(self):
        if len(self.services) == 1:
            yield from TemplateList(
                service=self.services[0],
                user=self.user,
            )
            return

        for service in self.services:
            yield from ServiceTemplateList(
                service=service,
                user=self.user,
            )

    @property
    def templates_to_show(self):
        return bool(self.services)


class TemplateListItem:
    is_service = False

    def __init__(
        self,
        template_or_folder,
        ancestors,
    ):
        self.id = template_or_folder["id"]
        self.name = template_or_folder["name"]
        self.ancestors = ancestors


class TemplateListTemplate(TemplateListItem):
    is_folder = False

    def __init__(
        self,
        template,
        ancestors,
        service_id,
    ):
        super().__init__(template, ancestors)
        self.service_id = service_id
        self.template_type = template["template_type"]
        self.content = template.get("content")

    @property
    def hint(self):
        return format_notification_type(self.template_type) + " template"


class TemplateListFolder(TemplateListItem):
    is_folder = True

    def __init__(
        self,
        folder,
        templates,
        folders,
        ancestors,
        service_id,
    ):
        super().__init__(folder, ancestors)
        self.folder = folder
        self.service_id = service_id
        self.number_of_templates = len(templates)
        self.number_of_folders = len(folders)

    @property
    def _hint_parts(self):
        if self.number_of_folders == self.number_of_templates == 0:
            yield "Empty"

        if self.number_of_templates == 1:
            yield "1 template"
        elif self.number_of_templates > 1:
            yield "{} templates".format(self.number_of_templates)

        if self.number_of_folders == 1:
            yield "1 folder"
        elif self.number_of_folders > 1:
            yield "{} folders".format(self.number_of_folders)

    @property
    def hint(self):
        return ", ".join(self._hint_parts)


class TemplateListService(TemplateListFolder):
    is_service = True

    def __init__(
        self,
        service,
        templates,
        folders,
    ):
        super().__init__(
            folder=service._dict,
            templates=templates,
            folders=folders,
            ancestors=[],
            service_id=service.id,
        )
