from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase, APIClient
from django.contrib.auth import get_user_model
from rest_framework_simplejwt.tokens import RefreshToken, AccessToken
from django.core.cache import cache
from .models import User
from .serializers import UserSerializer


class UserViewTests(APITestCase):
    ## Test toutes les vues de user sauf generic-user
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            email='testuser@example.com',
            password='testpassword'
        )
        self.accessToken = str(AccessToken.for_user(self.user))
        self.refreshToken = str(RefreshToken.for_user(self.user))

    def test_get_user(self):
        url = reverse('user_detail', kwargs={'pid': self.user.publicIdentifier})
        print('Bearer ' + self.accessToken)
        response = self.client.get(url, format='json', HTTP_AUTHORIZATION='Bearer ' + self.accessToken)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['email'], self.user.email)
        self.assertEqual(response.data['publicIdentifier'], str(self.user.publicIdentifier))

    def test_put_user(self):
        url = reverse('user_detail', kwargs={'pid': self.user.publicIdentifier})
        data = {
            'lastName': 'testFamName',
            'firstName': 'testFrtName',
            'username': 'newUsername',
        }
        response = self.client.put(url, data, format='json', HTTP_AUTHORIZATION='Bearer ' + self.accessToken)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.user.refresh_from_db()
        self.assertEqual(self.user.username, data['username'])
    
    def test_cant_put_user(self):
        url = reverse('user_detail', kwargs={'pid': self.user.publicIdentifier})
        data = {'email': 'newemail@test.com'}
        response = self.client.put(url, data, format='json', HTTP_AUTHORIZATION='Bearer ' + self.accessToken)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.user.refresh_from_db()
        print(data['email'])
        self.assertNotEqual(self.user.email, data['email'])

    # def test_delete_user(self):
    #     response = self.client.delete(self.url)
    #     self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
    #     self.assertFalse(User.objects.filter(publicIdentifier=self.user.publicIdentifier).exists())

    # def test_get_user_not_found(self):
    #     url = reverse('user-detail', kwargs={'pid': 'nonexistentpid'})
    #     response = self.client.get(url)
    #     self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

    # def test_put_user_invalid_data(self):
    #     data = {'email': ''}
    #     response = self.client.put(self.url, data, format='json')
    #     self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    # def test_delete_user_not_found(self):
    #     url = reverse('user-detail', kwargs={'pid': 'nonexistentpid'})
    #     response = self.client.delete(url)
    #     self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

class AuthenticationTests(APITestCase):
    def setUp(self):
        self.client = APIClient()
        # Création d'un utilisateur de test
        self.user = User.objects.create_user(
            email='test@example.com',
            password='testpass123',
            firstName='Test',
            lastName='User'
        )
        # Création d'un second utilisateur pour les tests d'accès
        self.other_user = User.objects.create_user(
            email='other@example.com',
            password='otherpass123',
            firstName='Other',
            lastName='User'
        )
        # Obtention des tokens JWT
        refresh = RefreshToken.for_user(self.user)
        self.access_token = str(refresh.access_token)

    def test_user_authentication(self):
        """Test l'authentification de l'utilisateur avec JWT"""
        # Test sans token
        response = self.client.get(
            reverse('user_detail', kwargs={'pid': self.user.publicIdentifier})
        )
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

        # Test avec token invalide
        self.client.credentials(HTTP_AUTHORIZATION='Bearer invalid_token')
        response = self.client.get(
            reverse('user_detail', kwargs={'pid': self.user.publicIdentifier})
        )
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

        # Test avec token valide
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
        response = self.client.get(
            reverse('user_detail', kwargs={'pid': self.user.publicIdentifier})
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_user_permissions(self):
        """Test les permissions d'accès aux données utilisateur"""
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
        
        # Test accès à ses propres données
        response = self.client.get(
            reverse('user_detail', kwargs={'pid': self.user.publicIdentifier})
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        
        # Test accès aux données d'un autre utilisateur
        response = self.client.get(
            reverse('user_detail', kwargs={'pid': self.other_user.publicIdentifier})
        )
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_user_modification(self):
        """Test la modification des données utilisateur"""
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
        
        # Test modification de ses propres données
        update_data = {
            'firstName': 'UpdatedName',
            'lastName': 'UpdatedLastName'
        }
        response = self.client.put(
            reverse('user_detail', kwargs={'pid': self.user.publicIdentifier}),
            update_data
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['firstName'], 'UpdatedName')
        
        # Test modification des données d'un autre utilisateur
        response = self.client.put(
            reverse('user_detail', kwargs={'pid': self.other_user.publicIdentifier}),
            update_data
        )
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_inactive_user(self):
        """Test l'accès avec un utilisateur inactif"""
        self.user.is_active = False
        self.user.save()
        
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
        response = self.client.get(
            reverse('user_detail', kwargs={'pid': self.user.publicIdentifier})
        )
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

class EmailVerificationTests(APITestCase):
    def setUp(self):
        self.client = APIClient()
        self.test_email = 'newuser@example.com'
        self.test_password = 'newpass123'
        # Création d'un utilisateur authentifié pour le test de l'envoi de code
        self.user = User.objects.create_user(
            email='existing@example.com',
            password='existingpass123'
        )
        refresh = RefreshToken.for_user(self.user)
        self.access_token = str(refresh.access_token)

    def test_email_verification_flow(self):
        """Test le processus complet de vérification par email"""
        # 1. Demande de code de vérification (avec authentification)
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
        response = self.client.post(
            reverse('send_email_send_code'),
            {'email': self.test_email}
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('codeID', response.data)
        code_id = response.data['codeID']

        # 2. Récupération du code depuis le cache
        cached_data = cache.get(code_id)
        self.assertIsNotNone(cached_data)
        email, code = cached_data

        # 3. Création d'un compte avec le code de vérification
        # Retrait des credentials pour simuler un nouvel utilisateur
        self.client.credentials()
        user_data = {
            'email': self.test_email,
            'password': self.test_password,
            'firstName': 'New',
            'lastName': 'User',
            'codeID': code_id,
            'code': code
        }
        response = self.client.post(reverse('generic_user'), user_data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_invalid_verification_code(self):
        """Test avec un code de vérification invalide"""
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
        
        # Tentative avec un code invalide
        user_data = {
            'email': self.test_email,
            'password': self.test_password,
            'firstName': 'New',
            'lastName': 'User',
            'codeID': 'invalid_code_id',
            'code': '000000'
        }
        response = self.client.post(reverse('generic_user'), user_data)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_expired_verification_code(self):
        """Test avec un code de vérification expiré"""
        # 1. Obtention d'un code
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
        response = self.client.post(
            reverse('send_email_send_code'),
            {'email': self.test_email}
        )
        code_id = response.data['codeID']
        
        # 2. Suppression manuelle du code du cache pour simuler l'expiration
        cache.delete(code_id)
        
        # 3. Tentative d'utilisation du code expiré
        user_data = {
            'email': self.test_email,
            'password': self.test_password,
            'codeID': code_id,
            'code': '123456'
        }
        response = self.client.post(reverse('generic_user'), user_data)
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

class UserAuthenticationViewTests(APITestCase):
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            email='test@example.com',
            password='testpass123'
        )
        self.login_url = reverse('authentication')

    def test_successful_login(self):
        """Test la connexion réussie avec génération de tokens"""
        response = self.client.post(self.login_url, {
            'email': 'test@example.com',
            'password': 'testpass123'
        })
        self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED)
        self.assertIn('tokens', response.data)
        self.assertIn('user', response.data)
        self.assertIn('access', response.data['tokens'])
        self.assertIn('refresh', response.data['tokens'])

    def test_failed_login(self):
        """Test la connexion avec des identifiants invalides"""
        response = self.client.post(self.login_url, {
            'email': 'test@example.com',
            'password': 'wrongpass'
        })
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_nonexistent_user(self):
        """Test la connexion avec un email inexistant"""
        response = self.client.post(self.login_url, {
            'email': 'nonexistent@example.com',
            'password': 'testpass123'
        })
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_refresh_token(self):
        """Test le rafraîchissement du token d'accès"""
        # 1. Connexion initiale
        response = self.client.post(self.login_url, {
            'email': 'test@example.com',
            'password': 'testpass123'
        })
        refresh_token = response.data['tokens']['refresh']
        
        # 2. Rafraîchissement du token
        response = self.client.post(reverse('refresh'), {
            'refresh': refresh_token
        })
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('access', response.data)

    def test_invalid_refresh_token(self):
        """Test le rafraîchissement avec un token invalide"""
        response = self.client.post(reverse('refresh'), {
            'refresh': 'invalid_refresh_token'
        })
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)