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

"""Models used by debusine signing tasks."""

from typing import Annotated, override

import pydantic

from debusine.artifacts.models import WorkRequestResults
from debusine.assets import KeyPurpose
from debusine.signing.models import SigningMode
from debusine.tasks.models import (
    BaseDynamicTaskData,
    BaseTaskData,
    LookupMultiple,
    LookupSingle,
)


class SigningNoopData(BaseTaskData):
    """In-memory task data for the SigningNoop task."""

    result: WorkRequestResults = WorkRequestResults.FAILURE


class GenerateKeyData(BaseTaskData):
    """In-memory task data for the GenerateKey task."""

    purpose: KeyPurpose
    description: str


class SignData(BaseTaskData):
    """In-memory task data for the Sign task."""

    purpose: KeyPurpose
    unsigned: LookupMultiple
    key: str

    @pydantic.field_validator("purpose", mode="after")
    @classmethod
    def validate_purpose(cls, purpose: KeyPurpose) -> KeyPurpose:
        """Don't allow OpenPGP repository signing keys."""
        if purpose == KeyPurpose.OPENPGP_REPOSITORY:
            raise ValueError(
                f"Key purpose may not be '{KeyPurpose.OPENPGP_REPOSITORY}'"
                f" here (handled by SignRepositoryIndex task instead)"
            )
        return purpose


class SignDynamicData(BaseDynamicTaskData):
    """Dynamic data for the Sign task."""

    unsigned_ids: list[int]
    unsigned_binary_package_names: list[str | None] = []

    @override
    def get_input_artifacts_ids(self) -> list[int]:
        return self.unsigned_ids


class DebsignData(BaseTaskData):
    """In-memory task data for the Debsign task."""

    unsigned: LookupSingle
    key: str


class DebsignDynamicData(BaseDynamicTaskData):
    """Dynamic data for the Debsign task."""

    unsigned_id: int

    @override
    def get_input_artifacts_ids(self) -> list[int]:
        return [self.unsigned_id]


class SignRepositoryIndexData(BaseTaskData):
    """In-memory task data for the SignRepositoryIndex task."""

    suite_collection: LookupSingle
    unsigned: LookupSingle
    mode: SigningMode
    signed_name: Annotated[str, pydantic.Field(pattern=r"^[^/]+$")]

    @pydantic.field_validator("mode", mode="after")
    @classmethod
    def validate_mode(cls, mode: SigningMode) -> SigningMode:
        """Only allow detached or clear signatures."""
        if mode not in {SigningMode.DETACHED, SigningMode.CLEAR}:
            raise ValueError(
                f"Signing mode must be 'detached' or 'clear'; got '{mode}'"
            )
        return mode


class SignRepositoryIndexDynamicData(BaseDynamicTaskData):
    """Dynamic data for the SignRepositoryIndex task."""

    signing_keys: list[str]
    unsigned_id: int

    @override
    def get_input_artifacts_ids(self) -> list[int]:
        return [self.unsigned_id]
