from typing import TYPE_CHECKING

from plain.auth.views import AuthViewMixin
from plain.urls import reverse
from plain.utils import timezone
from plain.utils.text import slugify
from plain.views import (
    TemplateView,
)

from .registry import registry
from .types import Img

if TYPE_CHECKING:
    from ..cards import Card


URL_NAMESPACE = "staff"


class StaffView(AuthViewMixin, TemplateView):
    staff_required = True

    title: str = ""
    slug: str = ""
    path: str = ""
    description: str = ""
    image: Img | None = None

    # Leave empty to hide from nav
    #
    # An explicit disabling of showing this url/page in the nav
    # which importantly effects the (future) recent pages list
    # so you can also use this for pages that can never be bookmarked
    nav_section = "App"
    nav_title = ""

    links: dict[str] = {}

    parent_view_class: "StaffView" = None

    template_name = "staff/page.html"
    cards: list["Card"] = []

    def get_template_context(self):
        context = super().get_template_context()
        context["title"] = self.get_title()
        context["image"] = self.get_image()
        context["slug"] = self.get_slug()
        context["description"] = self.get_description()
        context["links"] = self.get_links()
        context["parent_view_classes"] = self.get_parent_view_classes()
        context["admin_registry"] = registry
        context["cards"] = self.get_cards()
        context["render_card"] = lambda card: card().render(self, self.request)
        context["time_zone"] = timezone.get_current_timezone_name()
        return context

    @classmethod
    def view_name(cls) -> str:
        return f"view_{cls.get_slug()}"

    @classmethod
    def get_slug(cls) -> str:
        if cls.slug:
            return cls.slug

        if cls.title:
            return slugify(cls.title)

        raise NotImplementedError(
            f"Please set a slug on the {cls} class or implement get_slug()."
        )

    # Can actually use @classmethod, @staticmethod or regular method for these?
    def get_title(self) -> str:
        return self.title

    def get_image(self) -> Img | None:
        return self.image

    def get_description(self) -> str:
        return self.description

    @classmethod
    def get_path(cls) -> str:
        if cls.path:
            return cls.path

        if slug := cls.get_slug():
            return slug

        raise NotImplementedError(
            f"Please set a path on the {cls} class or implement get_slug() or get_path()."
        )

    @classmethod
    def get_parent_view_classes(cls) -> list["StaffView"]:
        parents = []
        parent = cls.parent_view_class
        while parent:
            parents.append(parent)
            parent = parent.parent_view_class
        return parents

    @classmethod
    def get_nav_section(cls) -> bool:
        if not cls.nav_section:
            return ""

        if cls.parent_view_class:
            # Don't show child views by default
            return ""

        return cls.nav_section

    @classmethod
    def get_nav_title(cls) -> str:
        if cls.nav_title:
            return cls.nav_title

        if cls.title:
            return cls.title

        raise NotImplementedError(
            f"Please set a title or nav_title on the {cls} class or implement get_nav_title()."
        )

    @classmethod
    def get_view_url(cls, obj=None) -> str:
        if obj:
            return reverse(f"{URL_NAMESPACE}:" + cls.view_name(), kwargs={"pk": obj.pk})
        else:
            return reverse(f"{URL_NAMESPACE}:" + cls.view_name())

    def get_links(self) -> dict[str]:
        return self.links.copy()

    def get_cards(self):
        return self.cards.copy()
