# 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.artifacts.models import ArtifactCategory
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: ArtifactCategory
    ) -> 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 == ArtifactCategory.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 == ArtifactCategory.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", ArtifactCategory.SOURCE_PACKAGE
        )

    @classmethod
    def create_artifact_binary(cls, package_name: str) -> int:
        """Create a binary artifact."""
        return cls._create_artifact(
            package_name, "download", ArtifactCategory.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 = ArtifactCategory.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"] == ArtifactCategory.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 create_system_images_mmdebstrap(
        suites: list[str], backend: str = "unshare"
    ) -> None:
        """
        Create debian:system-image artifacts and add them to a collection.

        :param suites: suites of the system images
        """
        template_data = {
            "vendor": "debian",
            "targets": [
                {
                    "codenames": suites,
                    "backends": [backend],
                    "architectures": [
                        subprocess.check_output(
                            ["dpkg", "--print-architecture"], text=True
                        ).strip()
                    ],
                    "mmdebstrap_template": {
                        "bootstrap_options": {"variant": "apt"},
                        "bootstrap_repositories": [
                            {
                                "mirror": "http://deb.debian.org/debian",
                                "components": ["main"],
                            }
                        ],
                    },
                }
            ],
        }

        workflow_template_result = Client.execute_command(
            "create-workflow-template",
            f"mmdebstrap-environment-{'-'.join(suites)}",
            "update_environments",
            stdin=yaml.safe_dump(template_data),
        )
        assert workflow_template_result.returncode == 0

        workflow_id = Client.execute_command(
            "create-workflow",
            f"mmdebstrap-environment-{'-'.join(suites)}",
            stdin=yaml.safe_dump({}),
        )["workflow_id"]

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