"""Tests for the library CRUD HTML views.

Currently covers ``library_list``'s app-managed scope filter. The view
loads every ``Library`` node from Neo4j and narrows it in Python by a
``scope`` GET param (``all`` / ``unmanaged`` / ``managed``, with legacy
``global`` / ``daedalus`` aliases). These tests stub out Neo4j entirely —
patching ``neo4j_available`` and injecting a fake ``Library`` class via
``sys.modules`` — so they assert on the filtering the view does and the
context it renders, not on real graph behaviour. Mirrors the mocking
style in ``test_search_views_admin_scope.py``.
"""

from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import MagicMock, patch

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse

User = get_user_model()


def _lib(name, managed_by_display):
    return SimpleNamespace(
        uid=f"uid-{name}",
        name=name,
        library_type="technical",
        description="",
        workspace_id=None,
        managed_by_display=managed_by_display,
    )


class LibraryListScopeFilterTests(TestCase):
    """Cover the ``scope`` filter branches of ``library_list``."""

    def setUp(self):
        self.user = User.objects.create_user(
            username="op", email="op@example.com", password="pw"
        )
        self.client.force_login(self.user)
        self.url = reverse("library:library-list")
        self.managed = _lib("Docs", "Spelunker")
        self.unmanaged = _lib("Notes", "")

    def _fake_library_cls(self):
        """Return a Library stub whose ``nodes.order_by`` yields two libraries."""
        fake_nodes = MagicMock()
        fake_nodes.order_by.return_value = [self.managed, self.unmanaged]
        return SimpleNamespace(nodes=fake_nodes)

    def _get(self, **params):
        with patch("library.views.neo4j_available", return_value=True), \
             patch.dict(
                 "sys.modules",
                 {"library.models": SimpleNamespace(Library=self._fake_library_cls())},
             ):
            return self.client.get(self.url, params)

    def test_default_scope_is_all_and_returns_everything(self):
        response = self._get()

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.context["scope"], "all")
        self.assertEqual(
            list(response.context["libraries"]), [self.managed, self.unmanaged]
        )

    def test_managed_scope_keeps_only_managed(self):
        response = self._get(scope="managed")

        self.assertEqual(response.context["scope"], "managed")
        self.assertEqual(list(response.context["libraries"]), [self.managed])

    def test_unmanaged_scope_keeps_only_unmanaged(self):
        response = self._get(scope="unmanaged")

        self.assertEqual(response.context["scope"], "unmanaged")
        self.assertEqual(list(response.context["libraries"]), [self.unmanaged])

    def test_legacy_daedalus_scope_aliases_to_managed(self):
        response = self._get(scope="daedalus")

        self.assertEqual(response.context["scope"], "managed")
        self.assertEqual(list(response.context["libraries"]), [self.managed])

    def test_legacy_global_scope_aliases_to_unmanaged(self):
        response = self._get(scope="global")

        self.assertEqual(response.context["scope"], "unmanaged")
        self.assertEqual(list(response.context["libraries"]), [self.unmanaged])

    def test_unknown_scope_does_not_filter(self):
        """An unexpected scope value degrades to the unfiltered list."""
        response = self._get(scope="bogus")

        self.assertEqual(response.context["scope"], "bogus")
        self.assertEqual(
            list(response.context["libraries"]), [self.managed, self.unmanaged]
        )

    def test_neo4j_unavailable_sets_error_and_empty_list(self):
        with patch("library.views.neo4j_available", return_value=False):
            response = self.client.get(self.url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(list(response.context["libraries"]), [])
        self.assertEqual(response.context["error"], "Neo4j is not available.")
