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

"""View for Autopkgtest work request."""

from typing import Any, Optional

from debusine.artifacts import AutopkgtestArtifact
from debusine.db.models import Artifact, WorkRequest
from debusine.web.views.work_request import WorkRequestPlugin


class AutopkgtestView(WorkRequestPlugin):
    """View for Autopkgtest work request."""

    template_name = "web/autopkgtest-detail.html"
    task_name = "autopkgtest"

    @staticmethod
    def _get_fail_on_scenarios(fail_on: dict[str, bool]) -> list[str]:
        """Process fail_on task data into a list of strings for viewing."""
        return [
            key.replace("_test", "")
            for key, value in fail_on.items()
            if key.endswith("_test") and value
        ]

    @staticmethod
    def _list_files(artifact_id: int) -> list[str]:
        return [
            file.path
            for file in Artifact.objects.get(
                id=artifact_id
            ).fileinartifact_set.order_by("path")
        ]

    @classmethod
    def _get_request_data(cls, work_request: WorkRequest) -> dict[str, Any]:
        """Return data about the request."""
        task_data = work_request.task_data

        # TODO: It would be useful to show the request data after default
        # values have been set, but unfortunately these are only set by the
        # task after it loads data from the database and aren't saved
        # anywhere else.
        request_data = {
            "source_name": None,
            "source_version": None,
            "host_architecture": task_data["host_architecture"],
            "vendor": None,
            "codename": None,
            "backend": task_data.get("backend", "auto"),
            "include_tests": task_data.get("include_tests"),
            "exclude_tests": task_data.get("exclude_tests"),
            "debug_level": task_data.get("debug_level"),
            "extra_apt_sources": task_data.get("extra_apt_sources"),
            "use_packages_from_base_repository": task_data.get(
                "use_packages_from_base_repository"
            ),
            "environment": task_data.get("environment"),
            "needs_internet": task_data.get("needs_internet"),
            "fail_on_scenarios": cls._get_fail_on_scenarios(
                task_data.get("fail_on", {})
            ),
            "timeout": task_data.get("timeout"),
        }

        # This information is in the result artifact's data if it exists,
        # but it won't necessarily exist - for example, a testbed failure
        # will leave us without a result artifact.
        try:
            environment_artifact = Artifact.objects.get(
                id=task_data["environment_id"]
            )
            if environment_artifact.category in {
                "debian:system-tarball",
                "debian:system-image",
            }:
                request_data["vendor"] = environment_artifact.data.get("vendor")
                request_data["codename"] = environment_artifact.data.get(
                    "codename"
                )
        except Artifact.DoesNotExist:
            pass

        task_data_input = task_data["input"]

        source_artifact_id = task_data_input["source_artifact_id"]
        request_data["source_artifact"] = {
            "id": source_artifact_id,
            "files": cls._list_files(source_artifact_id),
        }

        source_artifact = Artifact.objects.get(id=source_artifact_id)
        if source_artifact.category == "debian:source-package":
            request_data["source_name"] = source_artifact.data.get("name")
            request_data["source_version"] = source_artifact.data.get("version")
        request_data["task_description"] = (
            f"{request_data['source_name'] or 'UNKNOWN'}_"
            f"{request_data['source_version'] or 'UNKNOWN'}_"
            f"{request_data['host_architecture']}"
        )
        if request_data["vendor"] and request_data["codename"]:
            request_data[
                "task_description"
            ] += f" in {request_data['vendor']}:{request_data['codename']}"

        binary_artifacts = []
        for binary_artifact_id in task_data_input["binary_artifacts_ids"]:
            binary_artifacts.append(
                {
                    "id": binary_artifact_id,
                    "files": cls._list_files(binary_artifact_id),
                }
            )
        request_data["binary_artifacts"] = binary_artifacts

        context_artifacts = []
        for context_artifact_id in task_data_input.get(
            "context_artifacts_ids", []
        ):
            context_artifacts.append(
                {
                    "id": context_artifact_id,
                    "files": cls._list_files(context_artifact_id),
                }
            )
        request_data["context_artifacts"] = context_artifacts

        return request_data

    @staticmethod
    def _get_result_data(
        artifact: Artifact,
    ) -> list[tuple[str, str, Optional[str]]]:
        """Return a list of tuples describing individual test results."""
        result_data = []
        for name, result in sorted(artifact.data.get("results", {}).items()):
            result_data.append((name, result["status"], result.get("details")))
        return result_data

    def get_context_data(self, work_request: WorkRequest) -> dict[str, Any]:
        """Return the context."""
        result_artifact = (
            work_request.artifact_set.filter(
                category=AutopkgtestArtifact._category
            )
            .order_by("id")
            .first()
        )

        context_data: dict[str, Any] = {
            "request_data": self._get_request_data(work_request),
            "result": work_request.result,
        }
        if result_artifact:
            context_data["result_artifact"] = result_artifact
            context_data["result_data"] = self._get_result_data(result_artifact)
        return context_data
