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

"""Tests related to workers views."""
from django.test import RequestFactory
from django.urls import reverse
from rest_framework import status

from debusine.db.context import context
from debusine.tasks.models import WorkerType
from debusine.test.django import TestCase
from debusine.web.views.tests.utils import ViewTestMixin
from debusine.web.views.workers import WorkersListView


class WorkersListViewTests(ViewTestMixin, TestCase):
    """Tests for WorkersListView."""

    def setUp(self):
        """Set up objects."""
        self.factory = RequestFactory()

    def test_template_with_workers(self):
        """Test template contains expected information."""
        with context.disable_permission_checks():
            workspace = self.playground.create_workspace(
                name="Public", public=True
            )

        worker_external = self.playground.create_worker()
        worker_external.name = "a-first-one"
        worker_external.concurrency = 3
        worker_external.save()

        worker_external.mark_connected()

        worker_celery = self.playground.create_worker(
            worker_type=WorkerType.CELERY
        )
        worker_celery.name = "b-second-one"
        worker_celery.save()

        worker_signing = self.playground.create_worker(
            worker_type=WorkerType.SIGNING
        )
        worker_signing.name = "c-third-one"
        worker_signing.save()

        work_request_1 = self.playground.create_work_request(
            task_name="noop",
            worker=worker_external,
            mark_running=True,
            workspace=workspace,
        )
        work_request_2 = self.playground.create_work_request(
            task_name="noop",
            worker=worker_external,
            mark_running=True,
            workspace=workspace,
        )

        response = self.client.get(reverse("workers:list"))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        tree = self.assertHTMLValid(response)

        table = tree.xpath("//table[@id='worker-list']")[0]

        # Assert names
        self.assertHTMLContentsEquivalent(
            table.tbody.tr[0].td[0],
            f'<td>{worker_external.name} '
            f'(<abbr title="External">E</abbr>)</td>',
        )
        self.assertHTMLContentsEquivalent(
            table.tbody.tr[1].td[0],
            f'<td>{worker_celery.name} '
            f'(<abbr title="Celery">C</abbr>)</td>',
        )
        self.assertHTMLContentsEquivalent(
            table.tbody.tr[2].td[0],
            f'<td>{worker_signing.name} '
            f'(<abbr title="Signing">S</abbr>)</td>',
        )

        # Assert status for external worker: "Running ..."
        path_1 = reverse(
            "work_requests:detail", kwargs={"pk": work_request_1.pk}
        )
        path_2 = reverse(
            "work_requests:detail", kwargs={"pk": work_request_2.pk}
        )
        external_running = (
            f'<td> '
            f'<a href="{path_1}">noop</a><br/>'
            f'<a href="{path_2}">noop</a><br/>'
            f'</td>'
        )
        self.assertHTMLContentsEquivalent(
            table.tbody.tr[0].td[2], external_running
        )

    def test_status_disabled(self):
        """View shows "Disabled" (worker's token is disabled)."""
        worker = self.playground.create_worker()
        worker.token.enabled = False
        worker.token.save()

        response = self.client.get(reverse("workers:list"))

        tree = self.assertHTMLValid(response)
        table = tree.xpath("//table[@id='worker-list']")[0]

        self.assertHTMLContentsEquivalent(
            table.tbody.tr[0].td[2],
            '<td><span class="badge text-bg-danger">Disabled</span></td>',
        )

    def test_status_idle(self):
        """View shows "Idle" (enabled worker, not running any work request)."""
        worker = self.playground.create_worker()
        worker.mark_connected()

        response = self.client.get(reverse("workers:list"))

        tree = self.assertHTMLValid(response)
        table = tree.xpath("//table[@id='worker-list']")[0]

        self.assertHTMLContentsEquivalent(
            table.tbody.tr[0].td[2],
            '<td> <span class="badge text-bg-success">Idle</span> </td>',
        )

    def test_status_disconnected(self):
        """View shows "Disconnected" (enabled worker, not connected)."""
        self.playground.create_worker()

        response = self.client.get(reverse("workers:list"))

        tree = self.assertHTMLValid(response)
        table = tree.xpath("//table[@id='worker-list']")[0]

        self.assertHTMLContentsEquivalent(
            table.tbody.tr[0].td[2],
            '<td> <span class="badge text-bg-warning">'
            'Disconnected</span> </td>',
        )

    def test_template_no_workers(self):
        """Test template renders "No workers registered in this..."."""
        response = self.client.get(reverse("workers:list"))

        self.assertContains(
            response,
            "<p>No workers registered in this debusine instance.</p>",
            html=True,
        )

    def test_template_with_private_work_request_anonymous(self):
        """Test template hides private work requests from anonymous viewers."""
        with context.disable_permission_checks():
            workspace = self.playground.create_workspace(name="Private")

        worker_external = self.playground.create_worker()
        worker_external.name = "a-first-one"
        worker_external.concurrency = 3
        worker_external.save()

        worker_external.mark_connected()

        self.playground.create_work_request(
            task_name="noop",
            worker=worker_external,
            mark_running=True,
            workspace=workspace,
        )

        response = self.client.get(reverse("workers:list"))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        tree = self.assertHTMLValid(response)

        table = tree.xpath("//table[@id='worker-list']")[0]

        # Assert status for external worker: "Running ..."
        external_running = '<td>Private Task<br/></td>'
        self.assertHTMLContentsEquivalent(
            table.tbody.tr[0].td[2], external_running
        )

    def test_template_with_private_work_request_authenticated(self):
        """Test template shows private work requests to the authenticated."""
        with context.disable_permission_checks():
            workspace = self.playground.create_workspace(name="Private")

        worker_external = self.playground.create_worker()
        worker_external.name = "a-first-one"
        worker_external.concurrency = 3
        worker_external.save()

        worker_external.mark_connected()

        work_request = self.playground.create_work_request(
            task_name="noop",
            worker=worker_external,
            mark_running=True,
            workspace=workspace,
        )

        self.client.force_login(self.playground.get_default_user())
        response = self.client.get(reverse("workers:list"))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        tree = self.assertHTMLValid(response)

        table = tree.xpath("//table[@id='worker-list']")[0]

        # Assert status for external worker: "Running ..."
        path = reverse("work_requests:detail", kwargs={"pk": work_request.pk})
        external_running = f'<td><a href="{path}">noop</a><br/></td>'
        self.assertHTMLContentsEquivalent(
            table.tbody.tr[0].td[2], external_running
        )

    def test_get_ordering_ascending(self):
        """WorkersListView.get_ordering return correct ordering: "name"."""
        request = self.factory.get("/workers", {"order": "name", "asc": "1"})

        view = WorkersListView()
        view.request = request

        self.assertEqual(view.get_ordering(), "name")

    def test_get_ordering_descending(self):
        """WorkersListView.get_ordering return correct ordering: "-name"."""
        request = self.factory.get("/workers", {"order": "name", "asc": "0"})

        view = WorkersListView()
        view.request = request

        self.assertEqual(view.get_ordering(), "-name")

    def test_get_ordering_not_in_list(self):
        """WorkersListView.get_ordering return correct: "-name"."""
        request = self.factory.get("/workers", {"order": "a_field", "asc": "1"})

        view = WorkersListView()
        view.request = request

        self.assertEqual(view.get_ordering(), "name")
