# Copyright 2024 The Debusine Developers
# See the AUTHORS file at the top-level directory of this distribution
#
# This file is part of Debusine. It is subject to the license terms
# in the LICENSE file found in the top-level directory of this
# distribution. No part of Debusine, including this file, may be copied,
# modified, propagated, or distributed except according to the terms
# contained in the LICENSE file.

"""Base infrastructure for web views."""

import abc
from functools import cached_property
from typing import TYPE_CHECKING

from django.template.context import Context
from django.views.generic import (
    CreateView,
    DeleteView,
    DetailView,
    ListView,
    UpdateView,
)
from django.views.generic.base import ContextMixin
from django.views.generic.edit import FormMixin

from debusine.db.models import Workspace

if TYPE_CHECKING:
    CreateViewBase = CreateView
    DeleteViewBase = DeleteView
    DetailViewBase = DetailView
    ListViewBase = ListView
    UpdateViewBase = UpdateView
    FormMixinBase = FormMixin
else:
    # Django's generic views don't support generic types at run-time yet.
    class _CreateViewBase:
        def __class_getitem__(*args):
            return CreateView

    class _DeleteViewBase:
        def __class_getitem__(*args):
            return DeleteView

    class _DetailViewBase:
        def __class_getitem__(*args):
            return DetailView

    class _ListViewBase:
        def __class_getitem__(*args):
            return ListView

    class _UpdateViewBase:
        def __class_getitem__(*args):
            return UpdateView

    class _FormMixinBase:
        def __class_getitem__(*args):
            return FormMixin

    CreateViewBase = _CreateViewBase
    DeleteViewBase = _DeleteViewBase
    DetailViewBase = _DetailViewBase
    ListViewBase = _ListViewBase
    UpdateViewBase = _UpdateViewBase
    FormMixinBase = _FormMixinBase


class Widget(abc.ABC):
    """Base class for template-renderable elements."""

    @abc.abstractmethod
    def render(self, context: Context) -> str:
        """Render the element."""


class BaseUIView(ContextMixin):
    """Base class for Debusine web views."""

    base_template = "web/_base.html"
    title = ""

    def get_title(self) -> str:
        """Get the title for the page."""
        return self.title

    def get_base_template(self) -> str:
        """Return the name of the base template to use with this view."""
        return self.base_template

    def get_workspace(self) -> Workspace | None:
        """Return the current workspace, if any."""
        # By default, try self.object.workspace
        if obj := getattr(self, "object", None):
            return getattr(obj, "workspace", None)
        return None

    @cached_property
    def workspace(self) -> Workspace | None:
        """Return the current workspace, if any."""
        return self.get_workspace()

    def get_context_data(self, **kwargs):
        """
        Add base template information to the template context.

        Added elements:

        * base_template: name of the base template to load
        * title: string to use as default page title and header
        """
        context = super().get_context_data(**kwargs)
        context["base_template"] = self.get_base_template()
        context["title"] = self.get_title()
        context["workspace"] = self.workspace
        return context
