import secrets

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

from .models import ApiToken, UserProfile


class ApiTokenAuthTests(TestCase):
    """Credential auth: 'Authorization: Bearer <username> <token>'."""

    def setUp(self):
        User = get_user_model()
        self.alice = User.objects.create_user(username='Alice', password='pw')
        UserProfile.objects.get_or_create(user=self.alice)
        self.bob = User.objects.create_user(username='Bob', password='pw')
        UserProfile.objects.get_or_create(user=self.bob)
        self.token = secrets.token_urlsafe(48)
        ApiToken.objects.create(
            user=self.alice,
            token=self.token,
            key_prefix=self.token[:8],
            label='test',
        )

    def _auth(self, credential=None, username=None, token=None):
        if credential is None:
            credential = f'{username or ""} {token or ""}'.strip()
        return self.client.get(
            '/home/',
            HTTP_AUTHORIZATION=f'Bearer {credential}',
            HTTP_ACCEPT='application/json',
        )

    def test_valid_credentials_pass(self):
        resp = self._auth(username='Alice', token=self.token)
        self.assertEqual(resp.status_code, 200)

    def test_wrong_username_rejected(self):
        # A valid token presented under another user's name must not work.
        resp = self._auth(username='Bob', token=self.token)
        self.assertEqual(resp.status_code, 401)

    def test_wrong_token_rejected(self):
        resp = self._auth(username='Alice', token='x' * 64)
        self.assertEqual(resp.status_code, 401)

    def test_revoked_token_rejected(self):
        ApiToken.objects.all().delete()
        resp = self._auth(username='Alice', token=self.token)
        self.assertEqual(resp.status_code, 401)

    def test_missing_username_rejected(self):
        resp = self._auth(token=self.token)
        self.assertEqual(resp.status_code, 401)

    def test_missing_credentials_rejected(self):
        resp = self._auth()
        self.assertEqual(resp.status_code, 401)

    def test_username_case_insensitive(self):
        resp = self._auth(username='aLiCe', token=self.token)
        self.assertEqual(resp.status_code, 200)

    def test_unknown_user_rejected(self):
        resp = self._auth(username='Nobody', token=self.token)
        self.assertEqual(resp.status_code, 401)

    def test_authenticates_as_owner(self):
        self.client.get(
            '/home/',
            HTTP_AUTHORIZATION=f'Bearer Alice {self.token}',
            HTTP_ACCEPT='application/json',
        )
        token = ApiToken.objects.get(token=self.token)
        self.assertIsNotNone(token.last_used)
