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

"""Unit tests for server task input fields."""

from types import SimpleNamespace
from typing import ClassVar, override

from debusine.artifacts.models import ArtifactCategory, CollectionCategory
from debusine.client.models import LookupChildType
from debusine.db.models import (
    Artifact,
    Collection,
    CollectionItem,
    WorkRequest,
    Workspace,
)
from debusine.db.models.lookups import LookupResultArtifact
from debusine.db.tasks.tests.test_inputs import InputTests
from debusine.server.tasks.inputs import CopyCollectionItemsCopiesInput
from debusine.server.tasks.models import CopyCollectionItemsCopies
from debusine.tasks import TaskConfigError


class CopyCollectionItemsCopiesInputTests(
    InputTests[CopyCollectionItemsCopiesInput]
):
    """Tests for :py:class:`CopyCollectionItemsCopiesInput`."""

    source_workspace: ClassVar[Workspace]
    source_build_logs: ClassVar[Collection]
    target_build_logs: ClassVar[Collection]
    work_requests: ClassVar[list[WorkRequest]]
    build_log_artifacts: ClassVar[list[Artifact]]

    @override
    @classmethod
    def setUpTestData(cls) -> None:
        super().setUpTestData()
        cls.source_workspace = cls.playground.create_workspace(
            name="source", public=True
        )
        cls.source_build_logs = cls.playground.create_singleton_collection(
            CollectionCategory.PACKAGE_BUILD_LOGS,
            workspace=cls.source_workspace,
        )
        cls.target_build_logs = cls.playground.create_singleton_collection(
            CollectionCategory.PACKAGE_BUILD_LOGS,
        )
        cls.work_requests = [
            cls.playground.create_worker_task(workspace=cls.source_workspace)
            for _ in range(2)
        ]
        cls.build_log_artifacts = []
        for work_request, version in zip(cls.work_requests, ("1.0-1", "1.0-2")):
            build_log = cls.playground.create_build_log_artifact(
                version=version,
                workspace=cls.source_workspace,
                work_request=work_request,
            )
            cls.source_build_logs.manager.add_artifact(
                build_log,
                user=cls.playground.get_default_user(),
                variables={
                    "work_request_id": work_request.id,
                    "vendor": "debian",
                    "codename": "sid",
                    "architecture": "amd64",
                },
            )
            cls.build_log_artifacts.append(build_log)

    @override
    def setUp(self) -> None:
        super().setUp()
        self.field = self.field_class(field="field")

    def test_none(self) -> None:
        self.task.data = SimpleNamespace(field=None)
        with self.assertRaises(AssertionError):
            self.resolve()

    def test_empty_list(self) -> None:
        self.task.data = SimpleNamespace(field=[])
        self.assertEqual(self.resolve(), [])

    def test_valid(self) -> None:
        self.task.data = SimpleNamespace(
            field=[
                CopyCollectionItemsCopies(
                    source_items={
                        "collection": self.source_build_logs.pk,
                        "category": ArtifactCategory.PACKAGE_BUILD_LOG,
                    },
                    target_collection=self.target_build_logs.pk,
                )
            ]
        )
        info = self.resolve()
        self.assertEqual(len(info), 1)
        self.assertEqual(info[0].label, "field[0]")
        self.assertEqual(
            len(info[0].source_items), len(self.build_log_artifacts)
        )
        for source_item, build_log_artifact in zip(
            info[0].source_items, self.build_log_artifacts
        ):
            self.assertIsDBInputLookupResult(
                source_item,
                LookupResultArtifact(
                    lookup=self.task.data.field[0].source_items.root[0],
                    parent_collection_lookup=(
                        f"{self.source_build_logs.pk}@collections"
                    ),
                    collection_item=self.source_build_logs.child_items.get(
                        artifact=build_log_artifact
                    ),
                    artifact=build_log_artifact,
                ),
                label="field[0].source_items",
            )
        self.assertIsDBInputCollectionSingle(
            info[0].target_collection,
            self.target_build_logs,
            lookup=self.task.data.field[0].target_collection,
            label="field[0].target_collection",
        )
        self.assertFalse(info[0].unembargo)
        self.assertFalse(info[0].replace)
        self.assertIsNone(info[0].name_template)
        self.assertIsNone(info[0].variables)

    def test_source_items_lookup_fails(self) -> None:
        nonexistent_lookup = f"nonexistent@{CollectionCategory.TEST}"
        self.task.data = SimpleNamespace(
            field=[
                CopyCollectionItemsCopies(
                    source_items={"collection": nonexistent_lookup},
                    target_collection=self.target_build_logs.pk,
                )
            ]
        )
        with self.assertRaisesRegex(
            TaskConfigError,
            rf"Lookup for field 'field\[0\].source_items' in"
            rf" {self.task.workspace} failed: {nonexistent_lookup!r} does not"
            rf" exist or is hidden",
        ):
            self.resolve()

    def test_source_items_contain_collection(self) -> None:
        sub_collection = self.playground.create_collection(
            name="test", category=CollectionCategory.TEST
        )
        CollectionItem.objects.create_from_collection(
            sub_collection,
            parent_collection=self.source_build_logs,
            name="test",
            data={},
            created_by_user=self.playground.get_default_user(),
        )
        self.task.data = SimpleNamespace(
            field=[
                CopyCollectionItemsCopies(
                    source_items={
                        "collection": self.source_build_logs.pk,
                        "child_type": LookupChildType.COLLECTION,
                    },
                    target_collection=self.target_build_logs.pk,
                )
            ]
        )
        with self.assertRaisesRegex(
            TaskConfigError,
            rf"Bad lookup result from field 'field\[0\].source_items' in"
            rf" {self.task.workspace}: Cannot copy entire collections",
        ):
            self.resolve()

    def test_target_collection_lookup_fails(self) -> None:
        nonexistent_lookup = f"nonexistent@{CollectionCategory.TEST}"
        self.task.data = SimpleNamespace(
            field=[
                CopyCollectionItemsCopies(
                    source_items={"collection": self.source_build_logs.pk},
                    target_collection=nonexistent_lookup,
                )
            ]
        )
        with self.assertRaisesRegex(
            TaskConfigError,
            rf"Lookup for field 'field\[0\].target_collection' in"
            rf" {self.task.workspace} failed: {nonexistent_lookup!r} does not"
            rf" exist or is hidden",
        ):
            self.resolve()

    def test_get_artifact_ids(self) -> None:
        self.task.data = SimpleNamespace(
            field=[
                CopyCollectionItemsCopies(
                    source_items={"collection": self.source_build_logs.pk},
                    target_collection=self.target_build_logs.pk,
                )
            ]
        )
        self.assertEqual(
            self.field.get_artifact_ids(self.resolve()),
            [artifact.id for artifact in self.build_log_artifacts],
        )
