# 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.
"""Tests for the base view code."""

from typing import Any, Union

from django.http import HttpResponse
from django.test import Client
from django.urls import reverse
from rest_framework import status

from debusine.db.context import context
from debusine.db.models import Scope, Token, Workspace
from debusine.server.exceptions import DebusineAPIException
from debusine.server.views.base import BaseAPIView
from debusine.test.django import (
    AllowAll,
    DenyAll,
    TestCase,
    TestResponseType,
    override_permission,
)

TestResponseType  # Fake use for vulture


class TestBaseAPIView(TestCase):
    """Test the BaseAPIView view."""

    def test_enforce(self) -> None:
        """Test the enforce method."""
        scope = self.playground.get_default_scope()
        user = self.playground.get_default_user()
        context.set_scope(scope)
        context.set_user(user)
        view = BaseAPIView()
        with override_permission(Scope, "can_display", AllowAll):
            view.enforce(scope.can_display)
        with (
            override_permission(Scope, "can_display", DenyAll),
            self.assertRaises(DebusineAPIException) as exc,
        ):
            view.enforce(scope.can_display)
        e = exc.exception
        self.assertEqual(
            e.debusine_title, "playground cannot display scope debusine"
        )
        self.assertIsNone(e.debusine_detail)
        self.assertIsNone(e.debusine_validation_errors)
        self.assertEqual(e.debusine_status_code, status.HTTP_403_FORBIDDEN)

    def test_set_current_workspace(self) -> None:
        """Test the set_current_workspace method."""
        workspace: Workspace | str
        error: str | None
        with context.disable_permission_checks():
            workspace = self.playground.get_default_workspace()
            private_workspace = self.playground.create_workspace(name="private")
        scope = self.playground.get_default_scope()
        user = self.playground.get_default_user()
        context.set_scope(scope)
        context.set_user(user)
        view = BaseAPIView()
        for workspace, error in (
            (workspace, None),
            (workspace.name, None),
            (
                private_workspace,
                "Workspace private not found in scope debusine",
            ),
            (
                private_workspace.name,
                "Workspace private not found in scope debusine",
            ),
            (
                "does-not-exist",
                "Workspace does-not-exist not found in scope debusine",
            ),
        ):
            with self.subTest(workspace=workspace), context.local():
                if not error:
                    view.set_current_workspace(workspace)
                else:
                    with self.assertRaises(DebusineAPIException) as exc:
                        view.set_current_workspace(workspace)
                    self.assertEqual(exc.exception.debusine_detail, error)


class TestWhoami(TestCase):
    """Test authentication using the whoami view."""

    def whoami(
        self, token: Token | str | None = None, client: Client | None = None
    ) -> "Union[dict[str, Any], TestResponseType]":
        """Call the whoami API view."""
        if client is None:
            client = self.client

        headers = {}
        match token:
            case None:
                pass
            case str():
                headers["token"] = token
            case _:
                headers["token"] = token.key

        response = client.get(reverse("api:whoami"), headers=headers)
        if hasattr(response, "data"):
            data = getattr(response, "data")
            assert isinstance(data, dict)
            return data
        else:
            return response

    def test_bare_token(self) -> None:
        """Try a bare token."""
        # Non-worker, non-user token
        self.assertEqual(
            self.whoami(self.playground.create_bare_token()),
            {'auth': True, 'user': "<anonymous>", 'worker_token': False},
        )

    def test_worker_token(self) -> None:
        """Try a worker token."""
        self.assertEqual(
            self.whoami(self.playground.create_worker_token()),
            {'auth': True, 'user': "<anonymous>", 'worker_token': True},
        )

    def test_user_token(self) -> None:
        """Try a user token."""
        self.assertEqual(
            self.whoami(self.playground.create_user_token()),
            {'auth': True, 'user': "playground", 'worker_token': False},
        )

    def test_both_token(self) -> None:
        """Try a user and worker token."""
        # Worker and user token
        token = self.playground.create_worker_token()
        token.user = self.playground.get_default_user()
        token.save()
        response = self.whoami(token)
        assert isinstance(response, HttpResponse)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertEqual(
            response.content,
            b"a token cannot be both a user and a worker token",
        )

    def test_no_token(self) -> None:
        """Try without a token."""
        self.assertEqual(
            self.whoami(),
            {'auth': False, 'user': "<anonymous>", 'worker_token': False},
        )

    def test_session_auth(self) -> None:
        """Try django auth."""
        user = self.playground.get_default_user()
        client = Client()
        client.force_login(user)
        self.assertEqual(
            self.whoami(client=client),
            {'auth': False, 'user': user.username, 'worker_token': False},
        )

    def test_token_key_does_not_exist(self) -> None:
        """Try a Token key that does not exist."""
        self.assertEqual(
            self.whoami("does-not-exist"),
            {'auth': False, 'user': "<anonymous>", 'worker_token': False},
        )

    def test_token_disabled(self) -> None:
        """Try a disabled Token."""
        # Worker and user token
        token = self.playground.create_user_token(enabled=False)
        self.assertEqual(
            self.whoami(token),
            {'auth': False, 'user': "<anonymous>", 'worker_token': False},
        )
