# 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.workflows.models import WorkRequestWorkflowData
from debusine.tasks.models import Confirmation, OutputData
from debusine.test.django import TestCase


class ConfirmTaskTests(TestCase):
    """Test the Confirm task."""

    scenario = scenarios.DefaultScopeUser()

    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:
        task = self.playground.create_wait_task(
            task_name="confirm",
            task_data={},
            workflow_data=WorkRequestWorkflowData(needs_input=True),
        ).get_task()
        self.assertEqual(task.get_label(), "wait for confirmation")

    def test_respond_confirm(self) -> None:
        work_request = self.playground.create_wait_task(
            task_name="confirm",
            workflow_data=WorkRequestWorkflowData(needs_input=True),
            mark_running=True,
        )
        task = work_request.get_task()
        assert isinstance(task, Confirm)

        task.respond(user=self.scenario.user, confirm=True)

        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:
        work_request = self.playground.create_wait_task(
            task_name="confirm",
            task_data=ConfirmData(deny_action=DenyAction.FAIL),
            workflow_data=WorkRequestWorkflowData(needs_input=True),
            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.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)
