# Copyright 2023 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.
"""Helper mixin for the integration tests."""
import subprocess
import tempfile
from pathlib import Path

import requests

from utils.client import Client

import yaml

from debusine.artifacts.local_artifact import BinaryPackage, SourcePackage
from debusine.test import TestHelpersMixin


class IntegrationTestHelpersMixin(TestHelpersMixin):
    """Utility methods for the integration tests."""

    @staticmethod
    def _create_artifact(
        package_name: str, apt_get_command: str, artifact_type: str
    ) -> int:
        """
        Create an artifact.

        :param package_name: package contained in the artifact.
        :param apt_get_command: e.g. "source", "download".
        :param artifact_type: type of artifact (e.g. debian:source-package,
          debian:binary-packages).
        """
        with tempfile.TemporaryDirectory(
            prefix="debusine-integration-tests-"
        ) as temp_directory:
            temp_path = Path(temp_directory)

            subprocess.check_call(
                ["apt-get", apt_get_command, "--download-only", package_name],
                cwd=temp_path,
            )

            files = list(temp_path.iterdir())

            data_yaml = temp_path / "artifact-data.yaml"
            if artifact_type == "debian:source-package":
                [dsc_file] = [
                    file for file in files if file.name.endswith(".dsc")
                ]
                version = dsc_file.name.split("_")[1]
                src_artifact = SourcePackage.create(
                    name=package_name, version=version, files=files
                )
                data_yaml.write_text(yaml.safe_dump(src_artifact.data.dict()))
            elif artifact_type == "debian:binary-package":
                bin_artifact = BinaryPackage.create(file=files[0])
                data_yaml.write_text(yaml.safe_dump(bin_artifact.data.dict()))
            else:
                raise NotImplementedError(
                    f"cannot generate artifact data for {artifact_type}"
                )

            artifact_id = Client.execute_command(
                "create-artifact",
                artifact_type,
                "--workspace",
                "System",
                "--data",
                data_yaml,
                "--upload",
                *files,
            )["artifact_id"]

            return artifact_id

    @classmethod
    def create_artifact_source(cls, package_name: str) -> int:
        """Create a source artifact with hello files."""
        return cls._create_artifact(
            package_name, "source", "debian:source-package"
        )

    @classmethod
    def create_artifact_binary(cls, package_name: str) -> int:
        """Create a binary artifact."""
        return cls._create_artifact(
            package_name, "download", "debian:binary-packages"
        )

    @classmethod
    def create_artifact_upload(cls, package_names: list[str]) -> int:
        """Create an upload artifact containing some binary packages."""
        with tempfile.TemporaryDirectory(
            prefix="debusine-integration-tests-"
        ) as temp_directory:
            temp_path = Path(temp_directory)

            for package_name in package_names:
                subprocess.check_call(
                    ["apt-get", "download", "--download-only", package_name],
                    cwd=temp_path,
                )

            files = sorted(temp_path.iterdir())
            changes_file = temp_path / f"{package_names[0]}.changes"
            cls.write_changes_file(changes_file, files)

            return Client.execute_command(
                "import-debian-artifact",
                "--workspace",
                "System",
                changes_file,
            )["artifact_id"]

    @staticmethod
    def create_artifact_build_logs(filename: str, contents: str) -> int:
        """
        Create a build-log artifact.

        :param filename: filename that is being created
        :param contents: contents of the file being created
        """
        with tempfile.TemporaryDirectory(
            prefix="debusine-integration-tests-"
        ) as temp_directory:
            artifact_type = "debian:package-build-log"

            temp_path = Path(temp_directory)
            (temp_path / filename).write_text(contents)
            files = list(temp_path.iterdir())

            data_yaml = temp_path / "artifact-data.yaml"
            data_yaml.write_text(
                yaml.safe_dump(
                    {
                        "source": "test",
                        "version": "1.0",
                        "filename": filename,
                    }
                )
            )

            artifact_id = Client.execute_command(
                "create-artifact",
                artifact_type,
                "--workspace",
                "System",
                "--data",
                data_yaml,
                "--upload",
                *files,
            )["artifact_id"]

            return artifact_id

    @staticmethod
    def print_work_request_debug_logs(work_request_id: int):
        """Print work request's log's - used for debugging."""
        work_request = Client.execute_command(
            "show-work-request", work_request_id
        )

        for artifact in work_request["artifacts"]:
            if artifact["category"] == "debusine:work-request-debug-logs":
                for path, file_information in artifact["files"].items():
                    print("------ FILE:", path)
                    print(">>>>>>>>>>>")
                    print(requests.get(file_information["url"]).text)
                    print("<<<<<<<<<<<")

    @staticmethod
    def find_system_image(
        *, suite: str, category: str = "debian:system-tarball"
    ) -> int:
        """
        Find the most recent image of suite to build with.

        Return the artifact ID.
        """
        work_requests = Client.execute_command("list-work-requests")["results"]
        for work_request in work_requests:
            task_data = work_request["task_data"]
            if (
                work_request["status"] == "completed"
                and work_request["result"] == "success"
                and work_request["task_name"] == "mmdebstrap"
                and task_data["bootstrap_options"]["variant"] == "apt"
                and len(task_data["bootstrap_repositories"]) == 1
                and task_data["bootstrap_repositories"][0]["suite"] == suite
            ):
                # Load the artifacts
                work_request = Client.execute_command(
                    "show-work-request", work_request["id"]
                )
                for artifact in work_request["artifacts"]:
                    if artifact["category"] == category:
                        return artifact["id"]
        raise Exception(f"No {suite} {category} found")

    @staticmethod
    def create_system_image_mmdebstrap(suite: str) -> list[int]:
        """
        Create a debian:system-image, return artifact ids of system-tarballs.

        :param suite: suite of the system image
        :return: artifact ids of category debian:system-tarball created by
          the work request..
        """
        task_data = {
            "bootstrap_options": {
                "architecture": subprocess.check_output(
                    ["dpkg", "--print-architecture"], text=True
                ).strip(),
                "variant": "apt",
            },
            "bootstrap_repositories": [
                {
                    "mirror": "http://deb.debian.org/debian",
                    "suite": suite,
                    "components": ["main"],
                }
            ],
        }

        work_request_id = Client.execute_command(
            "create-work-request",
            "mmdebstrap",
            stdin=yaml.safe_dump(task_data),
        )["work_request_id"]

        # The worker should get the new work request and start executing it
        Client.wait_for_work_request_completed(work_request_id, "success")

        work_request = Client.execute_command(
            "show-work-request", work_request_id
        )

        system_tarballs: list[int] = []

        for artifact in work_request["artifacts"]:
            if artifact["category"] == "debian:system-tarball":
                system_tarballs.append(artifact["id"])

        return system_tarballs
