# 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.

"""debusine collection views."""

from functools import cached_property
from typing import Any

from django.db.models import Count
from django.http import Http404

import yaml

from debusine.db.models import Collection, CollectionItem, Workspace
from debusine.web.forms import CollectionSearchForm
from debusine.web.utils import ui_prototype
from debusine.web.views.base import (
    DetailViewBase,
    FormMixinBase,
    ListViewBase,
)
from debusine.web.views.mixins import PaginationMixin


class CollectionViewMixin:
    """Common functions for collection views."""

    @cached_property
    def workspace(self):
        """Workspace for this request."""
        if self.request.user.is_authenticated:
            workspaces = Workspace.objects.all()
        else:
            workspaces = Workspace.objects.filter(public=True)
        try:
            return workspaces.get(name=self.kwargs["wname"])
        except Workspace.DoesNotExist:
            raise Http404(f"Workspace {self.kwargs['wname']} not found")


class CollectionListView(CollectionViewMixin, ListViewBase[Collection]):
    """List collections."""

    model = Collection
    template_name = "web/collection-list.html"
    context_object_name = "collection_list"
    ordering = ["category", "name"]

    def get_queryset(self):
        """Filter collection by accessible workspace."""
        queryset = super().get_queryset()
        return queryset.filter(workspace=self.workspace)


class CollectionDetailView(CollectionViewMixin, DetailViewBase[Collection]):
    """Show a collection detail."""

    model = Collection
    template_name = "web/collection-detail.html"
    context_object_name = "collection"

    def get_object(self, queryset=None) -> Collection:
        """Return the collection object to show."""
        if queryset is None:
            queryset = self.get_queryset()  # pragma: no cover
        try:
            return queryset.get(
                name=self.kwargs["cname"], category=self.kwargs["ccat"]
            )
        except queryset.model.DoesNotExist:
            raise Http404(
                f"{self.kwargs['ccat']} collection"
                f" {self.kwargs['cname']} not found"
            )

    def get_queryset(self):
        """All collections for authenticated users or only public ones."""
        queryset = super().get_queryset()
        return queryset.filter(workspace=self.workspace)

    @ui_prototype
    def get_context_data(self, *args, **kwargs):
        """Return context_data with work_request_list and workspace_list."""
        context = super().get_context_data(**kwargs)
        if self.object.data:
            context["data_yaml"] = yaml.safe_dump(self.object.data)

        context["artifacts"] = (
            CollectionItem.active_objects.filter(
                parent_collection=self.object, artifact__isnull=False
            )
            .values("category")
            .annotate(count=Count("category"))
            .order_by("category")
        )
        context["collections"] = (
            CollectionItem.active_objects.filter(
                parent_collection=self.object, collection__isnull=False
            )
            .values("category")
            .annotate(count=Count("category"))
            .order_by("category")
        )

        return context


class CollectionSearchView(
    CollectionViewMixin,
    FormMixinBase[CollectionSearchForm],
    PaginationMixin,
    ListViewBase[CollectionItem],
):
    """Search a collection contents."""

    model = CollectionItem
    template_name = "web/collection-search.html"
    context_object_name = "item_list"
    paginate_by = 50
    form_class = CollectionSearchForm

    @cached_property
    def collection(self) -> Collection:
        """Collection for this request."""
        collections = Collection.objects.filter(workspace=self.workspace)
        try:
            return collections.get(
                name=self.kwargs["cname"], category=self.kwargs["ccat"]
            )
        except Collection.DoesNotExist:
            raise Http404(
                f"{self.kwargs['ccat']} collection"
                f" {self.kwargs['cname']} not found"
            )

    @ui_prototype
    def get_queryset(self):
        """All items belonging to the current collection."""
        queryset = (
            super().get_queryset().filter(parent_collection=self.collection)
        )
        # FIXME: this builds the form twice: if it becomes a problem we can
        # override get_form to cache its result
        form = self.get_form()
        if form.is_valid():
            if name := form.cleaned_data["name"]:
                queryset = queryset.filter(name__startswith=name)
            if category := form.cleaned_data["category"]:
                queryset = queryset.filter(category=category)
        return queryset

    @ui_prototype
    def get_form_kwargs(self) -> dict[str, Any]:
        """Get arguments used to instantiate the form."""
        kwargs = super().get_form_kwargs()
        kwargs["instance"] = self.collection
        kwargs["data"] = self.request.GET
        return kwargs

    @ui_prototype
    def get_ordering(self):
        """Return field used for sorting."""
        order = self.request.GET.get("order")
        if order in ("name", "category"):
            if self.request.GET["asc"] == "0":
                return "-" + order
            else:
                return order

        return "category"

    @ui_prototype
    def get_context_data(self, *args, **kwargs):
        """Return context_data with work_request_list and workspace_list."""
        context = super().get_context_data(**kwargs)
        context["collection"] = self.collection
        context["order"] = self.get_ordering().removeprefix("-")
        context["asc"] = self.request.GET.get("asc", "0")
        return context
