# 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 for task status view."""
from django.template.loader import get_template
from django.test import RequestFactory, override_settings
from django.urls import reverse

from debusine.db.models import WorkRequest
from debusine.server.status import TaskQueueSummary, WorkerSummary
from debusine.tasks.models import WorkerType
from debusine.test.django import TestCase
from debusine.web.views.tests.utils import ViewTestMixin


class TaskStatusViewTests(ViewTestMixin, TestCase):
    """Tests for TaskStatusView."""

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

    def test_template_no_work_requests(self):
        """Test template display the "No work requests..." message."""
        response = self.client.get(reverse("task-status"))

        self.assertContains(
            response, "<p>There are no pending work requests.</p>", html=True
        )

    def test_template_simple(self):
        """Test template row for arch-None without any tasks."""
        self.playground.create_worker(
            worker_type=WorkerType.EXTERNAL,
            extra_dynamic_metadata={
                "system:host_architecture": "amd64",
                "system:architectures": ["amd64", "i386"],
            },
        ).mark_connected()
        response = self.client.get(reverse("task-status"))
        tree = self.assertHTMLValid(response)
        arch_table = tree.xpath("//table[@id='task-arch-queue']")[0]
        row = arch_table.tbody.xpath("tr[@id='arch-_none_']")[0]
        self.assertIsNotNone(row)

    @override_settings(DISABLE_AUTOMATIC_SCHEDULING=True)
    def test_template(self) -> None:
        """Test template contains expected output."""
        worker = self.playground.create_worker(
            worker_type=WorkerType.EXTERNAL,
            extra_dynamic_metadata={
                "system:host_architecture": "amd64",
                "system:architectures": ["amd64", "i386"],
            },
        )
        worker.mark_connected()

        self.playground.create_work_request(
            task_name="noop",
            task_data={"host_architecture": "amd64"},
            status=WorkRequest.Statuses.PENDING,
            worker=worker,
        )
        self.playground.create_work_request(
            task_name="noop",
            task_data={"host_architecture": "i386"},
            status=WorkRequest.Statuses.RUNNING,
        )
        self.playground.create_work_request(
            task_name="noop",
            status=WorkRequest.Statuses.PENDING,
        )
        self.playground.create_work_request(task_name="sbuild")

        response = self.client.get(reverse("task-status"))

        tree = self.assertHTMLValid(response)

        type_table = tree.xpath("//table[@id='task-queue']")[0]
        row = type_table.tbody.xpath("tr[@id='worker_type-external']")[0]
        # Columns: type, registered, connected, busy, idle, running, pending
        self.assertHTMLContentsEquivalent(row.td[0], "<td>External</td>")
        self.assertHTMLContentsEquivalent(row.td[1], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[2], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[3], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[4], "<td>0</td>")
        self.assertHTMLContentsEquivalent(row.td[5], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[6], "<td>1</td>")

        arch_table = tree.xpath("//table[@id='task-arch-queue']")[0]

        # First row: architecture amd64, 1 connected worker, 1 work request
        row = arch_table.tbody.xpath("tr[@id='arch-amd64']")[0]
        work_request_list_amd64 = (
            reverse("work_requests:list") + "?status=pending&amp;arch=amd64"
        )
        self.assertHTMLContentsEquivalent(
            row.td[0], f'<td><a href="{work_request_list_amd64}">amd64</a></td>'
        )
        # Columns: registered, connected, busy, idle, running, pending
        self.assertHTMLContentsEquivalent(row.td[1], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[2], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[3], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[4], "<td>0</td>")
        self.assertHTMLContentsEquivalent(row.td[5], "<td>0</td>")
        self.assertHTMLContentsEquivalent(row.td[6], "<td>1</td>")

        # Second row: architecture i386, 1 connected worker, 0 work requests
        row = arch_table.tbody.xpath("tr[@id='arch-i386']")[0]
        work_request_list_i386 = (
            reverse("work_requests:list") + "?status=pending&amp;arch=i386"
        )
        self.assertHTMLContentsEquivalent(
            row.td[0], f'<td><a href="{work_request_list_i386}">i386</a></td>'
        )
        self.assertHTMLContentsEquivalent(row.td[1], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[2], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[3], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[4], "<td>0</td>")
        self.assertHTMLContentsEquivalent(row.td[5], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[6], "<td>0</td>")

        # Third row: arch unspecified, 1 connected worker, 2 work requests
        row = arch_table.tbody.xpath("tr[@id='arch-_none_']")[0]
        self.assertHTMLContentsEquivalent(row.td[0], "<td>Not Specified</td>")
        self.assertHTMLContentsEquivalent(row.td[1], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[2], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[3], "<td>1</td>")
        self.assertHTMLContentsEquivalent(row.td[4], "<td>0</td>")
        self.assertHTMLContentsEquivalent(row.td[5], "<td>0</td>")
        self.assertHTMLContentsEquivalent(row.td[6], "<td>2</td>")

        # Three rows for two architectures: amd64 and i386, then None
        self.assertEqual(len(arch_table.tbody.tr), 3)

    def test_task_queue_sorted_in_template(self) -> None:
        """task_queue is sorted by architecture in the template."""
        worker_status = {
            WorkerType.EXTERNAL: {
                "worker_tasks": TaskQueueSummary(),
                "workers": WorkerSummary(),
            },
        }
        # Create an unsorted arch_status dictionary
        arch_status = {
            "i386": {
                "external_workers_arch": WorkerSummary(connected=3),
                "worker_tasks_arch": TaskQueueSummary(pending=5),
            },
            "amd64": {
                "external_workers_arch": WorkerSummary(connected=2),
                "worker_tasks_arch": TaskQueueSummary(pending=3),
            },
        }
        template = get_template("web/task_status.html")
        rendered_html = template.render(
            {"arch_status": arch_status, "worker_status": worker_status}
        )

        i386_index = rendered_html.find("i386")
        amd64_index = rendered_html.find("amd64")

        self.assertLess(amd64_index, i386_index)
