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

"""
Task to create a child workspace.

See docs/reference/devel-blueprints/experiment-workspaces.rst
"""

import datetime as dt
from typing import override

from debusine.artifacts.models import (
    SINGLETON_COLLECTION_CATEGORIES,
    WorkRequestResults,
)
from debusine.db.context import context
from debusine.db.models import Collection, Group, WorkflowTemplate, Workspace
from debusine.server.tasks import BaseServerTask
from debusine.server.tasks.models import CreateChildWorkspaceData


class CannotCreate(Exception):
    """Creation of the new workspace failed."""


class CreateChildWorkspace(BaseServerTask[CreateChildWorkspaceData]):
    """Task that creates a workspace."""

    TASK_VERSION = 1
    TASK_NAME = "create_child_workspace"

    def get_child_workspace_name(self) -> str:
        """Build the name of the child workspace."""
        return f"{self.data.prefix}-{self.data.suffix}"

    @override
    def _execute(self) -> WorkRequestResults:
        """Execute the task."""
        child_workspace_name = self.get_child_workspace_name()
        base_workspace = self.work_request.workspace

        # We need to override permissions later, so we need to check here
        # that the user has the permissions needed to run this server task
        self.enforce(base_workspace.can_create_child_workspace)

        # Lookup workflow templates to copy
        workflow_templates: list[WorkflowTemplate] = []
        for template_name in self.data.workflow_template_names:
            try:
                workflow_templates.append(
                    WorkflowTemplate.objects.get(
                        workspace=base_workspace, name=template_name
                    )
                )
            except WorkflowTemplate.DoesNotExist:
                raise CannotCreate(
                    f"workflow template {base_workspace.name}/{template_name}"
                    " does not exist"
                )

        # Create the new workspace
        with context.disable_permission_checks():
            workspace = Workspace.objects.create(
                scope=base_workspace.scope,
                name=child_workspace_name,
                expiration_delay=(
                    None
                    if self.data.expiration_delay is None
                    else dt.timedelta(days=self.data.expiration_delay)
                ),
                public=self.data.public,
            )

        # Set the new workspace as inheriting an existing workspace
        workspace.set_inheritance([base_workspace])

        # Create the usual singleton collections.  (This could be made
        # optional if somebody has a use case for creating child workspaces
        # without them.)
        for category in SINGLETON_COLLECTION_CATEGORIES:
            Collection.objects.get_or_create_singleton(category, workspace)

        # Lookup or create the owner group
        if self.data.owner_group is None:
            owner_group = Group.objects.create_ephemeral(
                workspace,
                name="Owners",
                owner=self.work_request.created_by,
            )
        else:
            try:
                owner_group = Group.objects.get(
                    name=self.data.owner_group, scope=base_workspace.scope
                )
            except Group.DoesNotExist:
                raise CannotCreate(
                    f"owner group {self.data.owner_group} not found"
                )

        # Set the new group as OWNER of the new workspace
        owner_group.assign_role(workspace, Workspace.Roles.OWNER)

        # Copy WorkflowTemplates to the new workspace
        for workflow_template in workflow_templates:
            # See https://docs.djangoproject.com/en/5.1/topics/db/queries/#copying-model-instances  # noqa: E501
            workflow_template.pk = None
            workflow_template._state.adding = True
            workflow_template.workspace = workspace
            workflow_template.save()

        return WorkRequestResults.SUCCESS

    @override
    def get_task_configuration_subject_context(
        self,
    ) -> tuple[str | None, str | None]:
        # Compute dynamic task to extract subject and
        # configuration_context
        # TODO: these values can be computed in a separate methods if the rest
        # TODO: of compute_dynamic_data becomes too expensive to run twice
        early_dynamic_task_data = self.compute_dynamic_data()
        return (
            early_dynamic_task_data.subject,
            early_dynamic_task_data.configuration_context,
        )

    @override
    def get_label(self) -> str:
        """Return the task label."""
        return f"create child workspace {self.get_child_workspace_name()!r}"
