# Copyright 2024 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 the base Workflow classes."""
import abc

from django.test import TestCase

from debusine.db.models import WorkRequest, default_workspace
from debusine.server.workflows import (
    Workflow,
)
from debusine.server.workflows.models import BaseWorkflowData
from debusine.server.workflows.noop import NoopWorkflow
from debusine.tasks import BaseTask
from debusine.tasks.models import BaseDynamicTaskData, TaskTypes
from debusine.tasks.tests.helper_mixin import TestTaskMixin
from debusine.test.utils import preserve_task_registry


class WorkflowTests(TestCase):
    """Unit tests for Workflow class."""

    def test_create(self):
        """Test instantiating a Workflow."""
        with preserve_task_registry():

            class ExampleWorkflow(
                TestTaskMixin, Workflow[BaseWorkflowData, BaseDynamicTaskData]
            ):
                """Concrete workflow instance to use for tests."""

                def populate(self) -> None:
                    """Unused abstract method from Workflow."""
                    raise NotImplementedError()

            wr = WorkRequest(task_data={}, workspace=default_workspace())
            wf = ExampleWorkflow(wr)
            self.assertEqual(wf.data, BaseWorkflowData())
            self.assertEqual(wf.work_request, wr)
            self.assertEqual(wf.workspace, default_workspace())
            self.assertEqual(wf.work_request_id, wr.id)
            self.assertEqual(wf.workspace_name, wr.workspace.name)

    def test_registration(self):
        """Test class subclass registry."""
        with preserve_task_registry():

            class ExampleWorkflow(
                TestTaskMixin, Workflow[BaseWorkflowData, BaseDynamicTaskData]
            ):
                """Concrete workflow instance to use for tests."""

                def populate(self) -> None:
                    """Unused abstract method from Workflow."""
                    raise NotImplementedError()

            class ExampleWorkflowABC(
                TestTaskMixin,
                Workflow[BaseWorkflowData, BaseDynamicTaskData],
                abc.ABC,
            ):
                """Abstract workflow subclass to use for tests."""

            class ExampleWorkflowName(
                TestTaskMixin, Workflow[BaseWorkflowData, BaseDynamicTaskData]
            ):
                """Workflow subclass with a custom name."""

                TASK_NAME = "examplename"

                def populate(self) -> None:
                    """Unused abstract method from Workflow."""
                    raise NotImplementedError()

            # name gets filled
            self.assertEqual(ExampleWorkflow.name, "exampleworkflow")
            self.assertEqual(ExampleWorkflowABC.name, "exampleworkflowabc")
            self.assertEqual(ExampleWorkflowName.name, "examplename")

            # Subclasses that are not ABC get registered
            self.assertIn(
                "exampleworkflow", BaseTask._sub_tasks[TaskTypes.WORKFLOW]
            )
            self.assertNotIn(
                "exampleworkflowabc", BaseTask._sub_tasks[TaskTypes.WORKFLOW]
            )
            self.assertIn(
                "examplename", BaseTask._sub_tasks[TaskTypes.WORKFLOW]
            )

    def test_lookup(self):
        """Test lookup of Workflow orchestrators."""
        self.assertEqual(
            BaseTask.class_from_name(TaskTypes.WORKFLOW, "noop"), NoopWorkflow
        )
        self.assertEqual(Workflow.from_name("noop"), NoopWorkflow)
