# 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 the workspace views."""

from datetime import timedelta
from typing import ClassVar

from django.test import TestCase
from django.urls import reverse

import lxml.objectify

from rest_framework import status

from debusine.artifacts.models import CollectionCategory
from debusine.db.models import Collection, Workspace
from debusine.test import TestHelpersMixin
from debusine.web.views.tests.utils import ViewTestMixin


class WorkspaceViewsTestsEmptyDB(ViewTestMixin, TestHelpersMixin, TestCase):
    """Tests for Workspace views with no workspaces."""

    def test_list_no_workspaces(self):
        """No workspaces: 'No workspaces' in response."""
        response = self.client.get(reverse("workspaces:list"))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertContains(response, "No workspaces.", html=True)


class WorkspaceViewsTests(ViewTestMixin, TestHelpersMixin, TestCase):
    """Tests for Workspace views."""

    only_public_workspaces_message = (
        "Not authenticated. Only public workspaces are listed."
    )

    workspace_public: ClassVar[Workspace]
    workspace_private: ClassVar[Workspace]
    collection: ClassVar[Collection]

    @classmethod
    def setUpTestData(cls) -> None:
        """Set up a database layout for views."""
        super().setUpTestData()
        cls.workspace_public = cls.create_workspace(name="Public", public=True)
        cls.workspace_private = cls.create_workspace(name="Private")

        cls.collection = cls.create_collection(
            "Public",
            CollectionCategory.WORKFLOW_INTERNAL,
            workspace=cls.workspace_public,
        )

    def assertWorkspaceRow(
        self, tr: lxml.objectify.ObjectifiedElement, name: str
    ) -> None:
        """Check that a tr element is a workspace list table row."""
        self.assertEqual(tr.tag, "tr")
        a = tr.td[0].a
        self.assertEqual(
            a.get("href"), reverse("workspaces:detail", kwargs={"wname": name})
        )
        self.assertEqual(a.text, name)

    def test_list(self) -> None:
        """Test workspace list view."""
        response = self.client.get(reverse("workspaces:list"))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertHTMLValid(response)

    def test_list_authenticated_all_workspaces(self):
        """All Workspaces are listed (request without a user)."""
        self.client.force_login(self.get_test_user())

        response = self.client.get(reverse("workspaces:list"))
        tree = self.assertHTMLValid(response)
        rows = self.workspace_list_table_rows(tree)
        self.assertWorkspaceRow(rows[0], "Private")
        self.assertWorkspaceRow(rows[1], "Public")
        self.assertWorkspaceRow(rows[2], "System")
        self.assertEqual(len(rows), 3)
        self.assertNotContains(response, self.only_public_workspaces_message)

    def test_list_workspace_expiration(self):
        """Render public workspace's non-NULL default expiration delay."""
        self.workspace_public.default_expiration_delay = timedelta(400)
        self.workspace_public.save()

        self.client.force_login(self.get_test_user())
        response = self.client.get(reverse("workspaces:list"))
        tree = self.assertHTMLValid(response)
        rows = self.workspace_list_table_rows(tree)
        self.assertWorkspaceRow(rows[0], "Private")
        self.assertWorkspaceRow(rows[1], "Public")
        self.assertWorkspaceRow(rows[2], "System")
        self.assertEqual(len(rows), 3)

        self.assertNodeTextEqual(rows[0].td[2], "Never")
        self.assertNodeTextEqual(rows[1].td[2], "400")

    def test_list_not_authenticated_only_public_workspaces(self):
        """Only public Workspaces are listed (request without a user)."""
        response = self.client.get(reverse("workspaces:list"))
        tree = self.assertHTMLValid(response)
        rows = self.workspace_list_table_rows(tree)
        self.assertWorkspaceRow(rows[0], "Public")
        self.assertEqual(len(rows), 1)
        self.assertContains(response, self.only_public_workspaces_message)

    def test_detail(self):
        """Test workspace detail view."""
        response = self.client.get(
            reverse("workspaces:detail", kwargs={"wname": "Public"})
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertHTMLValid(response)

    def test_detail_private(self):
        """Unauthenticated detail view cannot access private workspaces."""
        response = self.client.get(
            reverse("workspaces:detail", kwargs={"wname": "Private"})
        )
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

        self.client.force_login(self.get_test_user())
        response = self.client.get(
            reverse("workspaces:detail", kwargs={"wname": "Private"})
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
