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

"""Unit tests for the Confirm task."""

from django.core.exceptions import ValidationError

from debusine.db.models import WorkRequest
from debusine.db.playground import scenarios
from debusine.server.tasks.wait import Confirm
from debusine.server.tasks.wait.models import ConfirmData, DenyAction
from debusine.server.tasks.wait.tests.helpers import WaitTaskTestCase
from debusine.server.workflows.models import WorkRequestWorkflowData
from debusine.tasks.models import Confirmation, OutputData


class ConfirmTaskTests(WaitTaskTestCase[Confirm]):
    """Test the Confirm task."""

    scenario = scenarios.DefaultScopeUser()

    def test_compute_scheduler_tags(self) -> None:
        self.assertComputesSchedulerTags()

    def test_no_additional_properties(self) -> None:
        error_msg = "Extra inputs are not permitted"
        with self.assertRaisesRegex(ValidationError, error_msg):
            self.playground.create_wait_task(
                task_name="confirm",
                task_data={"input": {}},
                workflow_data=WorkRequestWorkflowData(needs_input=True),
            )

    def test_label(self) -> None:
        self.assertEqual(self.task.get_label(), "wait for confirmation")

    def test_respond_confirm(self) -> None:
        self.mark_running = True
        self.task.respond(user=self.scenario.user, confirm=True)

        work_request = self.work_request
        work_request.refresh_from_db()
        self.assertEqual(work_request.status, WorkRequest.Statuses.COMPLETED)
        self.assertEqual(work_request.result, WorkRequest.Results.SUCCESS)
        self.assertEqual(work_request.completed_by, self.scenario.user)
        self.assertEqual(
            work_request.output_data,
            OutputData(confirmation=Confirmation(confirmed=True)),
        )

    def test_respond_deny_fail(self) -> None:
        self.mark_running = True
        self.task_data["deny_action"] = DenyAction.FAIL
        self.task.respond(
            user=self.scenario.user, confirm=False, comment="Broken"
        )

        work_request = self.work_request
        work_request.refresh_from_db()
        self.assertEqual(work_request.status, WorkRequest.Statuses.COMPLETED)
        self.assertEqual(work_request.result, WorkRequest.Results.FAILURE)
        self.assertEqual(work_request.completed_by, self.scenario.user)
        self.assertEqual(
            work_request.output_data,
            OutputData(
                confirmation=Confirmation(confirmed=False, comment="Broken")
            ),
        )

    def test_respond_deny_abort(self) -> None:
        self.scenario.set_current()
        workflow = self.playground.create_workflow(mark_running=True)
        work_request = workflow.create_child_wait(
            task_name="confirm",
            task_data=ConfirmData(deny_action=DenyAction.ABORT),
            workflow_data=WorkRequestWorkflowData(needs_input=True),
        )
        workflow.create_child_worker(task_name="noop")
        self.playground.advance_work_request(work_request, mark_running=True)
        task = work_request.get_task()
        assert isinstance(task, Confirm)

        task.respond(user=self.scenario.user, confirm=False, comment="Broken")

        work_request.refresh_from_db()
        self.assertEqual(work_request.status, WorkRequest.Statuses.ABORTED)
        self.assertEqual(work_request.aborted_by, self.scenario.user)
        self.assertEqual(
            work_request.output_data,
            OutputData(
                confirmation=Confirmation(confirmed=False, comment="Broken")
            ),
        )
        workflow.refresh_from_db()
        self.assertEqual(workflow.status, WorkRequest.Statuses.RUNNING)

    def test_respond_deny_abort_workflow(self) -> None:
        self.scenario.set_current()
        workflow = self.playground.create_workflow(mark_running=True)
        work_request = workflow.create_child_wait(
            task_name="confirm",
            task_data=ConfirmData(deny_action=DenyAction.ABORT_WORKFLOW),
            workflow_data=WorkRequestWorkflowData(needs_input=True),
        )
        workflow.create_child_worker(task_name="noop")
        self.playground.advance_work_request(work_request, mark_running=True)
        task = work_request.get_task()
        assert isinstance(task, Confirm)

        task.respond(user=self.scenario.user, confirm=False, comment="Broken")

        work_request.refresh_from_db()
        self.assertEqual(work_request.status, WorkRequest.Statuses.ABORTED)
        self.assertEqual(work_request.aborted_by, self.scenario.user)
        self.assertEqual(
            work_request.output_data,
            OutputData(
                confirmation=Confirmation(confirmed=False, comment="Broken")
            ),
        )
        workflow.refresh_from_db()
        self.assertEqual(workflow.status, WorkRequest.Statuses.ABORTED)
