# Copyright © 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 workflows views."""
from typing import Any, cast

from django.db.models import Q, QuerySet

from debusine.db.context import context
from debusine.db.models import WorkRequest
from debusine.db.models.work_requests import WorkRequestQuerySet
from debusine.tasks.models import TaskTypes
from debusine.web.forms import WorkflowFilterForm
from debusine.web.views.base import ListViewBase, WorkspaceView
from debusine.web.views.view_utils import PaginationMixin


class WorkflowsListView(
    WorkspaceView, PaginationMixin, ListViewBase[WorkRequest]
):
    """List workflows."""

    model = WorkRequest
    template_name = "web/workflow-list.html"
    context_object_name = "workflow_list"
    ordering = ["created_at"]
    paginate_by = 50

    form: WorkflowFilterForm

    # Maps URL query parameter names to model field names for sorting
    ORDERING_FIELDS = {
        "id": ["id"],
        "workflow_template": ["task_name"],
        "status": ["status", "workflow_runtime_status"],
        "result": ["result"],
        "started_at": ["started_at"],
        "completed_at": ["completed_at"],
        "started_by": ["created_by__username"],
        "last_activity": ["workflow_last_activity_at"],
    }

    def get_queryset(self) -> QuerySet[WorkRequest]:
        """Filter workflows by current workspace."""
        queryset = (
            cast(WorkRequestQuerySet[WorkRequest], super().get_queryset())
            .in_current_workspace()
            .can_display(context.user)
        )

        queryset = queryset.filter(
            task_type=TaskTypes.WORKFLOW, parent__isnull=True
        )

        # Apply filters from the form if present
        self.form = WorkflowFilterForm(self.request.GET)

        if self.form.is_valid():
            if task_names := self.form.cleaned_data.get("workflow_templates"):
                queryset = queryset.filter(
                    workflow_data_json__workflow_template_name__in=task_names
                )

            if statuses := self.form.cleaned_data.get("statuses"):
                q_objects = Q()

                for status in statuses:
                    if status == "running__any":
                        q_objects |= Q(status=WorkRequest.Statuses.RUNNING)
                    elif status.startswith("running__"):
                        q_objects |= Q(
                            workflow_runtime_status=status[len("running__") :]
                        )
                    else:
                        q_objects |= Q(status=status)

                queryset = queryset.filter(q_objects)

            if results := self.form.cleaned_data.get("results"):
                queryset = queryset.filter(result__in=results)

            if started_by := self.form.cleaned_data.get("started_by"):
                queryset = queryset.filter(created_by__username__in=started_by)

            if self.form.cleaned_data.get("with_failed_work_requests"):
                # This filter calls workflow_work_requests_failure
                # for all the workflows. This might need to be changed
                # (cached in the DB?)
                work_requests_ids_relevant = []

                for work_request in queryset:
                    if work_request.workflow_work_requests_failure > 0:
                        work_requests_ids_relevant.append(work_request.id)

                queryset = queryset.filter(id__in=work_requests_ids_relevant)

        return queryset

    def get_ordering(self) -> list[str]:
        """Fields to use for sorting based on the "order" query parameter."""
        order = self.request.GET.get("order", "id")
        order_fields = self.ORDERING_FIELDS.get(order, ["id"])

        if self.request.GET.get("asc", "0") == "0":
            return [f"-{field}" for field in order_fields]

        return order_fields

    def get_ordering_query_parameter(self) -> str:
        """Return sorting's GET parameter. Default is "id"."""
        if (
            param := self.request.GET.get("order", "id")
        ) in self.ORDERING_FIELDS.keys():
            return param

        return "id"

    def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
        """Add the form to the context."""
        context = super().get_context_data(**kwargs)
        context["filter_form"] = self.form

        context["order"] = self.get_ordering_query_parameter()
        context["asc"] = self.request.GET.get("asc", "0")

        return context
