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

"""Workflow to update metadata for a single suite."""

import datetime as dt
from functools import cached_property
from typing import override

from django.db import connection

from debusine.artifacts.models import ArtifactCategory, CollectionCategory
from debusine.client.models import LookupChildType
from debusine.db.models import Collection, WorkRequest
from debusine.server.tasks.models import GenerateSuiteIndexesData
from debusine.server.workflows.base import Workflow
from debusine.server.workflows.models import (
    UpdateSuiteData,
    WorkRequestWorkflowData,
)
from debusine.signing.models import SigningMode
from debusine.signing.tasks.models import SignRepositoryIndexData
from debusine.tasks import ensure_collection_category
from debusine.tasks.models import (
    ActionUpdateCollectionWithArtifacts,
    BaseDynamicTaskData,
)


class UpdateSuiteWorkflow(Workflow[UpdateSuiteData]):
    """Update metadata for a single suite."""

    TASK_NAME = "update_suite"

    def _get_current_transaction_timestamp(self) -> dt.datetime:
        with connection.cursor() as cursor:
            cursor.execute("SELECT CURRENT_TIMESTAMP")
            [now] = cursor.fetchone()
        assert isinstance(now, dt.datetime)
        return now

    @cached_property
    def suite_collection(self) -> Collection:
        """The suite whose indexes should be generated."""
        suite = self.work_request.lookup_single(
            self.data.suite_collection, expect_type=LookupChildType.COLLECTION
        ).collection
        ensure_collection_category(
            configuration_key="suite_collection",
            category=suite.category,
            expected=CollectionCategory.SUITE,
        )
        return suite

    @override
    def compute_dynamic_data(self) -> BaseDynamicTaskData:
        """
        Compute dynamic data for this workflow.

        :subject: name of ``suite_collection``
        """
        return BaseDynamicTaskData(subject=self.suite_collection.name)

    @override
    def populate(self) -> None:
        """Create child work requests."""
        # TODO: It would be useful to be able to run this workflow for a
        # given timestamp in order to backfill history.
        now = self._get_current_transaction_timestamp()
        generate_task, _ = self.work_request_ensure_child_server(
            task_name="generatesuiteindexes",
            task_data=GenerateSuiteIndexesData(
                suite_collection=self.data.suite_collection, generate_at=now
            ),
            workflow_data=WorkRequestWorkflowData(
                display_name=(
                    f"Generate indexes for {self.suite_collection.name}"
                ),
                step=f"generate-suite-indexes-{self.suite_collection.name}",
            ),
        )
        generated_release_name = (
            f"generated-release-{self.suite_collection.name}"
        )
        self.provides_artifact(
            generate_task,
            ArtifactCategory.REPOSITORY_INDEX,
            generated_release_name,
            artifact_filters={"data__path": "Release"},
        )
        generated_release = (
            f"internal@collections/name:{generated_release_name}"
        )

        sign_tasks: dict[str, WorkRequest] = {}
        for mode, signed_name in (
            (SigningMode.DETACHED, "Release.gpg"),
            (SigningMode.CLEAR, "InRelease"),
        ):
            sign_tasks[signed_name], _ = self.work_request_ensure_child_signing(
                task_name="signrepositoryindex",
                task_data=SignRepositoryIndexData(
                    suite_collection=self.data.suite_collection,
                    unsigned=generated_release,
                    mode=mode,
                    signed_name=signed_name,
                ),
                workflow_data=WorkRequestWorkflowData(
                    display_name=(
                        f"Create signed {signed_name} for "
                        f"{self.suite_collection.name}"
                    ),
                    step=f"sign-{mode}-{self.suite_collection.name}",
                ),
            )
            self.requires_artifact(sign_tasks[signed_name], generated_release)
            sign_tasks[signed_name].add_event_reaction(
                "on_success",
                ActionUpdateCollectionWithArtifacts(
                    collection=self.data.suite_collection,
                    variables={"path": signed_name},
                    artifact_filters={
                        "category": ArtifactCategory.REPOSITORY_INDEX
                    },
                    created_at=now,
                ),
            )
        # Make sure InRelease is generated last, in order that the "current"
        # view of a suite can arrange to only show a snapshot once an
        # InRelease file exists for it.
        sign_tasks["InRelease"].add_dependency(sign_tasks["Release.gpg"])
