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

"""Piuparts workflow."""
from debusine.server.workflows import Workflow
from debusine.server.workflows.models import (
    PiupartsWorkflowData,
    WorkRequestWorkflowData,
)
from debusine.tasks.models import (
    BaseDynamicTaskData,
    PiupartsData,
    PiupartsDataInput,
)


class PiupartsWorkflow(Workflow[PiupartsWorkflowData, BaseDynamicTaskData]):
    """
    Checks binary packages for all architectures of a target distribution.

    Creates piuparts tasks corresponding to the output of a set of
    build tasks for multiple architectures. This is meant to be part
    of a larger QA workflow such as Debian Pipeline.
    """

    # Workflow name (used by create-workflow-template)
    TASK_NAME = "piuparts"

    def populate(self) -> None:
        """Create a Piuparts task for each concrete architecture."""
        # piuparts will be run on the intersection of the provided
        # list of architectures (if any) and the architectures
        # provided in binary_artifacts
        architectures = self.get_architectures(self.data.binary_artifacts)
        #  architectures: if set, only run on any of these architecture names
        if self.data.architectures is not None:
            architectures.intersection_update(self.data.architectures)

        if architectures == {"all"}:
            # If only Architecture: all binary packages are provided
            # in binary_artifacts, then piuparts will be run once for
            # arch-all on {arch_all_host_architecture}.
            pass
        else:
            # piuparts will be run grouping arch-all + arch-any
            # together. Not running "all" separately.
            architectures.discard("all")

        # Define piuparts environment
        environment = f"{self.data.vendor}/match:codename={self.data.codename}"
        base_tgz = environment
        backend = self.data.backend

        for architecture in sorted(architectures):
            # group arch-all + arch-any
            arch_subset_binary_artifacts = self.filter_artifact_lookup(
                self.data.binary_artifacts,
                (architecture, "all"),
            )

            # The concrete architecture, or
            # {arch_all_host_architecture} if only Architecture: all
            # binary packages are being checked by this task
            host_architecture = (
                self.data.arch_all_host_architecture
                if architecture == "all"
                else architecture
            )

            # Create work-request (idempotent)
            task_data = PiupartsData(
                backend=backend,
                environment=environment,
                input=PiupartsDataInput(
                    binary_artifacts=arch_subset_binary_artifacts,
                ),
                host_architecture=host_architecture,
                base_tgz=base_tgz,
            )
            wr = self.work_request_ensure_child(
                task_name="piuparts",
                task_data=task_data.dict(),
                workflow_data=WorkRequestWorkflowData(
                    display_name=f"Piuparts {architecture}",
                    step=f"piuparts-{architecture}",
                ),
            )

            # Any of the lookups in input.binary_artifacts may result
            # in promises, and in that case the workflow adds
            # corresponding dependencies. Binary promises must include
            # an architecture field in their data.
            self.requires_artifact(wr, arch_subset_binary_artifacts)

    def get_label(self) -> str:
        """Return the task label."""
        return "run piuparts"
