from collections.abc import Mapping
from typing import Any

from django import forms
from django.urls import reverse
from django.utils.functional import cached_property

from wagtail.admin.staticfiles import versioned_static
from wagtail.admin.telepath import Adapter, adapter


class BaseSidebarAdapter(Adapter):
    @cached_property
    def media(self):
        return forms.Media(
            js=[
                versioned_static("wagtailadmin/js/sidebar.js"),
            ]
        )


# Main menu


class MenuItem:
    def __init__(
        self,
        name: str,
        label: str,
        icon_name: str = "",
        classname: str = "",
        attrs: Mapping[str, Any] = None,
    ):
        self.name = name
        self.label = label
        self.icon_name = icon_name
        self.classname = classname
        self.attrs = attrs or {}

    def js_args(self):
        return [
            {
                "name": self.name,
                "label": self.label,
                "icon_name": self.icon_name,
                "classname": self.classname,
                "attrs": self.attrs,
            }
        ]


@adapter("wagtail.sidebar.LinkMenuItem", base=BaseSidebarAdapter)
class LinkMenuItem(MenuItem):
    def __init__(
        self,
        name: str,
        label: str,
        url: str,
        icon_name: str = "",
        classname: str = "",
        attrs: Mapping[str, Any] = None,
    ):
        super().__init__(
            name,
            label,
            icon_name=icon_name,
            classname=classname,
            attrs=attrs,
        )
        self.url = url

    def js_args(self):
        args = super().js_args()
        args[0]["url"] = self.url
        return args

    def __eq__(self, other):
        return (
            self.__class__ == other.__class__
            and self.name == other.name
            and self.label == other.label
            and self.url == other.url
            and self.icon_name == other.icon_name
            and self.classname == other.classname
            and self.attrs == other.attrs
        )


@adapter("wagtail.sidebar.ActionMenuItem", base=BaseSidebarAdapter)
class ActionMenuItem(MenuItem):
    def __init__(
        self,
        name: str,
        label: str,
        action: str,
        icon_name: str = "",
        classname: str = "",
        method: str = "POST",
        attrs: Mapping[str, Any] = None,
    ):
        super().__init__(
            name,
            label,
            icon_name=icon_name,
            classname=classname,
            attrs=attrs,
        )
        self.action = action
        self.method = method

    def js_args(self):
        args = super().js_args()
        args[0]["action"] = self.action
        args[0]["method"] = self.method
        return args

    def __eq__(self, other):
        return (
            self.__class__ == other.__class__
            and self.name == other.name
            and self.label == other.label
            and self.action == other.action
            and self.method == other.method
            and self.icon_name == other.icon_name
            and self.classname == other.classname
            and self.attrs == other.attrs
        )


@adapter("wagtail.sidebar.SubMenuItem", base=BaseSidebarAdapter)
class SubMenuItem(MenuItem):
    def __init__(
        self,
        name: str,
        label: str,
        menu_items: list[MenuItem],
        icon_name: str = "",
        classname: str = "",
        footer_text: str = "",
        attrs: Mapping[str, Any] = None,
    ):
        super().__init__(
            name,
            label,
            icon_name=icon_name,
            classname=classname,
            attrs=attrs,
        )
        self.menu_items = menu_items
        self.footer_text = footer_text

    def js_args(self):
        args = super().js_args()
        args[0]["footer_text"] = self.footer_text
        args.append(self.menu_items)
        return args

    def __eq__(self, other):
        return (
            self.__class__ == other.__class__
            and self.name == other.name
            and self.label == other.label
            and self.menu_items == other.menu_items
            and self.icon_name == other.icon_name
            and self.classname == other.classname
            and self.footer_text == other.footer_text
            and self.attrs == other.attrs
        )


@adapter("wagtail.sidebar.PageExplorerMenuItem", base=BaseSidebarAdapter)
class PageExplorerMenuItem(LinkMenuItem):
    def __init__(
        self,
        name: str,
        label: str,
        url: str,
        start_page_id: int,
        icon_name: str = "",
        classname: str = "",
        attrs: Mapping[str, Any] = None,
    ):
        super().__init__(
            name,
            label,
            url,
            icon_name=icon_name,
            classname=classname,
            attrs=attrs,
        )
        self.start_page_id = start_page_id

    def js_args(self):
        args = super().js_args()
        args.append(self.start_page_id)
        return args

    def __eq__(self, other):
        return (
            self.__class__ == other.__class__
            and self.name == other.name
            and self.label == other.label
            and self.url == other.url
            and self.start_page_id == other.start_page_id
            and self.icon_name == other.icon_name
            and self.classname == other.classname
            and self.attrs == other.attrs
        )


# Modules


@adapter("wagtail.sidebar.WagtailBrandingModule", base=BaseSidebarAdapter)
class WagtailBrandingModule:
    def js_args(self):
        return [
            reverse("wagtailadmin_home"),
        ]


@adapter("wagtail.sidebar.SearchModule", base=BaseSidebarAdapter)
class SearchModule:
    def __init__(self, search_area):
        self.search_area = search_area

    def js_args(self):
        return [self.search_area.url]


@adapter("wagtail.sidebar.MainMenuModule", base=BaseSidebarAdapter)
class MainMenuModule:
    def __init__(
        self, menu_items: list[MenuItem], account_menu_items: list[MenuItem], user
    ):
        self.menu_items = menu_items
        self.account_menu_items = account_menu_items
        self.user = user

    def js_args(self):
        from wagtail.admin.templatetags.wagtailadmin_tags import avatar_url

        try:
            first_name = self.user.first_name
        except AttributeError:
            first_name = None

        return [
            self.menu_items,
            self.account_menu_items,
            {
                "name": first_name or self.user.get_username(),
                "avatarUrl": avatar_url(self.user, size=50),
            },
        ]
