# 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 QA work request plugin."""

from typing import ClassVar

from debusine.db.models import WorkRequest
from debusine.db.playground import scenarios
from debusine.test.django import TestCase
from debusine.web.views.qa import QATab, QAViewWorkRequestPlugin
from debusine.web.views.work_request import Tab


class QAViewWorkRequestPluginTests(TestCase):
    """Tests for :py:class:`QAViewWorkRequestPlugin`."""

    scenario = scenarios.UIRegressionTesting(simulate=False)

    work_request: ClassVar[WorkRequest]

    def plugin(self) -> QAViewWorkRequestPlugin:
        return QAViewWorkRequestPlugin(self.scenario.workflow_qa)

    def get_tab(self) -> QATab:
        """Return the tab for a work request or a plugin."""
        work_request = self.scenario.workflow_qa
        plugin = QAViewWorkRequestPlugin(work_request)
        tabs: list[Tab] = []
        plugin.add_tabs(tabs)
        self.assertEqual(len(tabs), 1)
        tab = tabs[0]
        assert isinstance(tab, QATab)
        self.assertEqual(tab.name, "qa")
        self.assertEqual(tab.label, "QA")
        return tab

    def test_is_enabled(self) -> None:
        plugin = self.plugin()
        self.assertTrue(plugin.is_enabled())

    def test_is_enabled_requires_output_data(self) -> None:
        self.scenario.workflow_qa.output_data = None
        self.scenario.workflow_qa.save()
        plugin = self.plugin()
        self.assertFalse(plugin.is_enabled())

    def test_is_enabled_requires_regression_analysis(self) -> None:
        od = self.scenario.workflow_qa.output_data
        assert od is not None
        od.regression_analysis = None
        self.scenario.workflow_qa.output_data = od
        self.scenario.workflow_qa.save()
        plugin = self.plugin()
        self.assertFalse(plugin.is_enabled())

    def test_is_enabled_requires_regression_analysis_summary(self) -> None:
        od = self.scenario.workflow_qa.output_data
        assert od is not None
        ra = od.regression_analysis
        assert ra is not None
        del ra[""]
        self.scenario.workflow_qa.output_data = od
        self.scenario.workflow_qa.save()
        plugin = self.plugin()
        self.assertFalse(plugin.is_enabled())

    def test_get_context_data_only_summary(self) -> None:
        od = self.scenario.workflow_qa.output_data
        assert od is not None
        ra = od.regression_analysis
        assert ra is not None
        ctx = self.get_tab().get_context_data()
        self.assertEqual(ctx["summary"], ra[""])
        self.assertEqual(ctx["orig"], self.scenario.reference_source)
        self.assertEqual(ctx["new"], self.scenario.new_source)
        self.assertEqual(ctx["rows"], [])
        self.assertEqual(ctx["warnings"], [])

    def test_get_context_data_rows(self) -> None:
        self.scenario.simulate_autopkgtest()
        od = self.scenario.workflow_qa.output_data
        assert od is not None
        ra = od.regression_analysis
        assert ra is not None
        ctx = self.get_tab().get_context_data()
        self.assertEqual(ctx["summary"], ra[""])
        self.assertEqual(ctx["orig"], self.scenario.reference_source)
        self.assertEqual(ctx["new"], self.scenario.new_source)
        self.assertEqual(
            ctx["rows"],
            [
                {
                    "name": "autopkgtest:libfoo:amd64",
                    "orig": None,
                    "new": self.scenario.autopkgtest_libfoo_new_amd64,
                    "analysis": self.scenario.ra_autopkgtest_amd64,
                },
                {
                    "name": "autopkgtest:libfoo:i386",
                    "orig": self.scenario.autopkgtest_libfoo_ref_i386,
                    "new": self.scenario.autopkgtest_libfoo_new_i386,
                    "analysis": self.scenario.ra_autopkgtest_i386,
                },
            ],
        )
        self.assertEqual(ctx["warnings"], [])

    def test_get_context_data_rows_hide_boring_revdep(self) -> None:
        self.scenario.simulate_rev_dep_autopkgtest()
        od = self.scenario.workflow_qa.output_data
        assert od is not None
        ra = od.regression_analysis
        assert ra is not None
        ctx = self.get_tab().get_context_data()
        self.assertEqual(ctx["summary"], ra[""])
        self.assertEqual(ctx["orig"], self.scenario.reference_source)
        self.assertEqual(ctx["new"], self.scenario.new_source)
        self.assertEqual(
            ctx["rows"],
            [
                # autopkgtest:foo:amd64 is hidden because it is STABLE
                {
                    "name": "autopkgtest:foo:i386",
                    "orig": self.scenario.autopkgtest_foo_ref_i386,
                    "new": self.scenario.autopkgtest_foo_new_i386,
                    "analysis": self.scenario.ra_autopkgtest_foo_i386,
                },
            ],
        )
        self.assertEqual(ctx["warnings"], [])

    def test_get_context_data_missing_artifacts(self) -> None:
        self.scenario.simulate_autopkgtest()
        od = self.scenario.workflow_qa.output_data
        assert od is not None
        ra = od.regression_analysis
        assert ra is not None
        ra[""].original_artifact_id = None
        ra[""].new_artifact_id = None
        self.scenario.workflow_qa.output_data = od
        self.scenario.workflow_qa.save()
        ctx = self.get_tab().get_context_data()
        self.assertEqual(ctx["summary"], ra[""])
        self.assertIsNone(ctx["orig"])
        self.assertIsNone(ctx["new"])
        self.assertEqual(ctx["warnings"], [])

    def test_render_no_rows(self) -> None:
        response = self.client.get(self.scenario.workflow_qa.get_absolute_url())
        tree = self.assertResponseHTML(response, dump_on_error=True)
        tab = self.assertHasElement(tree, "//div[@id='nav-qa']")
        self.assertTextContentEqual(tab, "No regression analysis results")

    def test_render(self) -> None:
        self.scenario.simulate_autopkgtest()
        od = self.scenario.workflow_qa.output_data
        assert od is not None
        ra = od.regression_analysis
        assert ra is not None
        response = self.client.get(self.scenario.workflow_qa.get_absolute_url())
        tree = self.assertResponseHTML(response, dump_on_error=True)
        tab = self.assertHasElement(tree, "//div[@id='nav-qa']")
        thead = self.assertHasElement(tab, "table/thead")
        self.assertTextContentEqual(
            thead.tr[0].th[1],
            f"Original result for {ra[""].original_source_version}",
        )
        self.assertTextContentEqual(
            thead.tr[0].th[2], f"New result for {ra[""].new_source_version}"
        )
        tbody = self.assertHasElement(tab, "table/tbody")
        self.assertTextContentEqual(
            tbody.tr[0].td[0], "autopkgtest:libfoo:amd64"
        )
        self.assertFalse(tbody.tr[0].td[1].xpath("a"))
        self.assertTextContentEqual(tbody.tr[0].td[1], "-")
        a = self.assertHasElement(tbody.tr[0].td[2], "a")
        self.assertEqual(
            a.get("href"),
            self.scenario.autopkgtest_libfoo_new_amd64.get_absolute_url(),
        )
        self.assertTextContentEqual(tbody.tr[0].td[2], "Success")
        self.assertTextContentEqual(tbody.tr[0].td[3], "? no-result")
