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

"""Concrete but mock version of FieldResolver used for tests."""

from collections.abc import Collection as AbcCollection
from typing import Any, overload, override

from debusine.artifacts.models import (
    ArtifactCategory,
    ArtifactData,
    CollectionCategory,
)
from debusine.client.models import RelationType
from debusine.tasks.executors import ExecutorImageCategory
from debusine.tasks.inputs import inputs
from debusine.tasks.inputs.resolver import FieldResolver
from debusine.tasks.models import (
    ArtifactInfo,
    BackendType,
    BaseTaskInputArtifact,
    ExtraExternalRepository,
    InputArtifactMultiple,
    InputCollectionSingle,
    LookupMultiple,
    LookupSingle,
)


class ConcreteFieldResolver(FieldResolver):
    """
    Concrete FieldResolver implementation.

    All abstract methods are overridden with versions that raise
    NotImplementedError.

    This is useful as a base for mocking specific methods.
    """

    @overload
    def lookup_single_artifact(
        self,
        lookup: LookupSingle,
        *,
        default_category: CollectionCategory | None = None,
        label: str,
    ) -> ArtifactInfo: ...

    @overload
    def lookup_single_artifact(
        self,
        lookup: None,
        *,
        default_category: CollectionCategory | None = None,
        label: str,
    ) -> None: ...

    @override
    def lookup_single_artifact(
        self,
        lookup: LookupSingle | None,
        *,
        default_category: CollectionCategory | None = None,
        label: str,
    ) -> ArtifactInfo | None:
        raise NotImplementedError("lookup_single_artifact")

    @override
    def lookup_multiple_artifacts(
        self,
        lookup: LookupMultiple,
        *,
        default_category: CollectionCategory | None = None,
        label: str,
    ) -> InputArtifactMultiple:
        raise NotImplementedError("lookup_multiple_artifact")

    @override
    def find_related_artifacts(
        self,
        artifact_ids: AbcCollection[int],
        *,
        target_category: ArtifactCategory,
        relation_type: RelationType = RelationType.RELATES_TO,
        label: str,
    ) -> InputArtifactMultiple:
        raise NotImplementedError("find_related_artifacts")

    @override
    def get_server_setting(self, setting: str) -> str:
        raise NotImplementedError("get_server_setting")

    @override
    def get_fallback_architecture(self) -> str:
        raise NotImplementedError("get_fallback_architecture")

    @override
    def export_suite_signing_keys(self, suite_id: int) -> str:
        raise NotImplementedError("export_suite_signing_keys")

    @override
    def lookup_environment(
        self,
        lookup: LookupSingle,
        *,
        architecture: str | None = None,
        backend: BackendType | None = None,
        default_category: CollectionCategory | None = None,
        image_category: "ExecutorImageCategory | None" = None,
        try_variant: str | None = None,
        label: str,
    ) -> ArtifactInfo:
        raise NotImplementedError("lookup_environment")

    @override
    def resolve_environmentinput(
        self, field: inputs.EnvironmentInput
    ) -> ArtifactInfo:
        raise NotImplementedError("resolve_environmentinput")

    @override
    def resolve_optionalenvironmentinput(
        self, field: inputs.OptionalEnvironmentInput
    ) -> ArtifactInfo | None:
        raise NotImplementedError("resolve_optionalenvironmentinput")

    @override
    def resolve_suitearchiveinput(
        self, field: inputs.SuiteArchiveInput
    ) -> tuple[InputCollectionSingle, InputCollectionSingle | None]:
        raise NotImplementedError("resolve_suitearchiveinput")

    @override
    def resolve_extrarepositoriesinput(
        self, field: inputs.ExtraRepositoriesInput
    ) -> list[ExtraExternalRepository] | None:
        raise NotImplementedError("resolve_extrarepositoriesinput")

    @override
    def get_field[T](self, field: "inputs.DataFieldInput[T, Any]") -> T | None:
        raise NotImplementedError("get_field")


class MockFieldResolver(ConcreteFieldResolver):
    """FieldResolver that fill inputs with a set of known values."""

    def __init__(self, **field_values: Any) -> None:
        """Construct a :py:class:`MockTaskDatabase`."""
        self.field_values: dict[str, Any] = {
            "build_architecture": "amd64",
            "task_configuration": None,
            **field_values,
        }

    def set(self, **field_values: Any) -> None:
        """Set field values from keyword arguments."""
        self.field_values.update(field_values)

    def set_field_artifact(
        self,
        name: str,
        *,
        id: int,  # noqa: A002
        category: ArtifactCategory,
        data: ArtifactData,
    ) -> None:
        """Set the value of an input field for an artifact lookup."""
        self.field_values[name] = ArtifactInfo(
            artifact_id=id, category=category, data=data, label=name, lookup=id
        )

    def set_field_artifacts(
        self,
        name: str,
        *artifacts: BaseTaskInputArtifact,
    ) -> None:
        """Set the value of an input field for a multiple artifact lookup."""
        self.field_values[name] = InputArtifactMultiple(
            label=name, lookup=["test"], artifacts=artifacts
        )

    def set_field_artifact_list(
        self,
        name: str,
        *artifacts: BaseTaskInputArtifact,
    ) -> None:
        """Set the value for a list of single artifact lookups."""
        self.field_values[name] = list(artifacts)

    def set_field_artifact_difflist(
        self,
        name: str,
        *lists: list[BaseTaskInputArtifact],
    ) -> None:
        """Set a list of two multiple lookups for debdiff."""
        self.field_values[name] = [
            InputArtifactMultiple(label=name, lookup=["test"], artifacts=lst)
            for lst in lists
        ]

    @override
    def resolve_inputs(
        self, task: inputs.TaskFieldContainer, stage: inputs.Stage | None = None
    ) -> None:
        for cont, inp in task.iter_all_fields(stage):
            assert inp.name in self.field_values, (
                f"Value for field {cont.__class__.__name__}.{inp.name}"
                " was not provided"
            )
            cont.__dict__[inp.name] = self.field_values[inp.name]
