from django.test import TestCase
from django.contrib.auth import get_user_model
from rest_framework.test import APIClient
from rest_framework import status
from django.urls import reverse
from likes.models import LikePost
from posts.models import Post
from categories.models import Category
from follows.models import FollowUser, FollowCategory
from images.models import Image
from io import BytesIO
from PIL import Image as PILImage
from django.core.files.uploadedfile import SimpleUploadedFile

User = get_user_model()


class PostTestCase(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.user2 = User.objects.create_user(
            username='user2',
            email='user2@example.com',
            password='user2pass123'
        )
        # Corrigé : enlever la category None
        self.post_data = {
            'content': 'This is a test post'
        }

    def create_test_image(self):
        """Helper method to create a test image"""
        # Créer une image en mémoire avec PIL
        image = PILImage.new('RGB', (100, 100), 'red')
        image_io = BytesIO()
        image.save(image_io, format='JPEG')
        image_io.seek(0)

        # Créer un SimpleUploadedFile avec le contenu de l'image
        return SimpleUploadedFile(
            'test_image.jpg',
            image_io.getvalue(),
            content_type='image/jpeg'
        )

    def test_create_post(self):
        """Test creating a post"""
        self.client.force_authenticate(user=self.user)
        response = self.client.post(reverse('create_post'), self.post_data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['content'], 'This is a test post')

    def test_create_post_unauthenticated(self):
        """Test creating a post without authentication"""
        response = self.client.post(reverse('create_post'), self.post_data)
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_list_posts(self):
        """Test listing posts"""
        Post.objects.create(
            content='Test post',
            created_by=self.user
        )
        self.client.force_authenticate(user=self.user)
        response = self.client.get(reverse('list_posts'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_post_detail(self):
        """Test viewing post details"""
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )
        self.client.force_authenticate(user=self.user)
        response = self.client.get(reverse('post_detail', args=[post.id]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['content'], 'Test post')

    def test_update_post(self):
        """Test updating a post"""
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )
        self.client.force_authenticate(user=self.user)
        update_data = {'content': 'Updated post'}
        response = self.client.put(reverse('update_post', args=[post.id]), update_data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['content'], 'Updated post')
        self.assertTrue(response.data['is_updated'])

    def test_update_post_unauthorized(self):
        """Test updating a post by non-creator"""
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )
        self.client.force_authenticate(user=self.user2)
        update_data = {'content': 'Updated post'}
        response = self.client.put(reverse('update_post', args=[post.id]), update_data)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_delete_post(self):
        """Test deleting a post (soft delete)"""
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )
        self.client.force_authenticate(user=self.user)
        response = self.client.delete(reverse('delete_post', args=[post.id]))
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        post.refresh_from_db()
        self.assertFalse(post.is_active)

    def test_delete_post_unauthorized(self):
        """Test deleting a post by non-creator"""
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )
        self.client.force_authenticate(user=self.user2)
        response = self.client.delete(reverse('delete_post', args=[post.id]))
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_like_post(self):
        """Test liking a post"""
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )
        self.client.force_authenticate(user=self.user2)
        response = self.client.post(reverse('like_post', args=[post.id]))
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertTrue(LikePost.objects.filter(user=self.user2, post=post).exists())

    def test_like_post_already_liked(self):
        """Test liking a post already liked"""
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )
        LikePost.objects.create(user=self.user2, post=post)
        self.client.force_authenticate(user=self.user2)
        response = self.client.post(reverse('like_post', args=[post.id]))
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_unlike_post(self):
        """Test unliking a post"""
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )
        LikePost.objects.create(user=self.user2, post=post)
        self.client.force_authenticate(user=self.user2)
        response = self.client.delete(reverse('unlike_post', args=[post.id]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertFalse(LikePost.objects.filter(user=self.user2, post=post).exists())

    def test_unlike_post_not_liked(self):
        """Test unliking a post not liked"""
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )
        self.client.force_authenticate(user=self.user2)
        response = self.client.delete(reverse('unlike_post', args=[post.id]))
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_user_posts(self):
        """Test listing posts by a specific user"""
        Post.objects.create(
            content='Test post',
            created_by=self.user
        )
        self.client.force_authenticate(user=self.user)
        response = self.client.get(reverse('user_posts', args=[self.user.id]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_category_posts(self):
        """Test listing posts by category"""
        category = Category.objects.create(
            title='Test Category',
            created_by=self.user
        )
        Post.objects.create(
            content='Test post',
            created_by=self.user,
            category=category
        )
        self.client.force_authenticate(user=self.user)
        response = self.client.get(reverse('category_posts', args=[category.id]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_create_post_with_images(self):
        self.client.force_authenticate(user=self.user)

        # Créer des images factices avec la méthode helper
        image1 = self.create_test_image()
        image2 = self.create_test_image()

        post_data = {
            'content': 'Test post with images',
            'images': [image1, image2]
        }

        response = self.client.post(reverse('create_post'), post_data, format='multipart')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(len(response.data['images']), 2)

    def test_create_post_without_images(self):
        """Test creating a post without images"""
        self.client.force_authenticate(user=self.user)
        post_data = {
            'content': 'Post without images'
        }

        response = self.client.post(reverse('create_post'), post_data, format='multipart')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(len(response.data['images']), 0)
        self.assertEqual(response.data['content'], 'Post without images')

    def test_update_post_with_images(self):
        """Test updating a post with new images"""
        # Créer un post sans images
        post = Post.objects.create(
            content='Original post',
            created_by=self.user
        )

        # Créer des images de test avec la méthode helper
        test_images = []
        for i in range(2):
            test_images.append(self.create_test_image())

        self.client.force_authenticate(user=self.user)

        # Mettre à jour le post avec images
        post_data = {
            'content': 'Updated post with images',
            'images': test_images
        }

        response = self.client.put(reverse('update_post', args=[post.id]), post_data, format='multipart')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['content'], 'Updated post with images')
        self.assertEqual(len(response.data['images']), 2)
        self.assertTrue(response.data['is_updated'])

    def test_update_post_content_only(self):
        """Test updating a post content without changing images"""
        post = Post.objects.create(
            content='Original post',
            created_by=self.user
        )

        self.client.force_authenticate(user=self.user)
        update_data = {'content': 'Updated content only'}

        response = self.client.put(reverse('update_post', args=[post.id]), update_data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['content'], 'Updated content only')
        self.assertTrue(response.data['is_updated'])


class ImagePostTestCase(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.user2 = User.objects.create_user(
            username='user2',
            email='user2@example.com',
            password='user2pass123'
        )

        self.category = Category.objects.create(
            title='Test Category',
            created_by=self.user
        )

    def create_test_image(self):
        """Helper method to create a test image"""
        # Créer une image en mémoire avec PIL
        image = PILImage.new('RGB', (100, 100), 'red')
        image_io = BytesIO()
        image.save(image_io, format='JPEG')
        image_io.seek(0)

        # Créer un SimpleUploadedFile avec le contenu de l'image
        return SimpleUploadedFile(
            'test_image.jpg',
            image_io.getvalue(),
            content_type='image/jpeg'
        )

    def test_create_post_with_images(self):
        """Test creating a post with multiple images"""
        # Créer des images de test avec la méthode helper
        test_images = []
        for i in range(3):
            test_images.append(self.create_test_image())

        self.client.force_authenticate(user=self.user)

        # Créer le post avec images
        post_data = {
            'content': 'Post with multiple images',
            'category': self.category.id,
            'images': test_images
        }

        response = self.client.post(reverse('create_post'), post_data, format='multipart')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(len(response.data['images']), 3)
        self.assertEqual(response.data['content'], 'Post with multiple images')

    def test_add_images_to_existing_post(self):
        """Test adding images to an existing post"""
        # Créer un post sans images
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )

        # Créer des images de test
        test_images = []
        for i in range(2):
            test_images.append(self.create_test_image())

        self.client.force_authenticate(user=self.user)

        # Ajouter les images au post
        response = self.client.post(
            reverse('add_images_to_post', args=[post.id]),
            {'images': test_images},
            format='multipart'
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(len(response.data), 2)

        # Vérifier que les images sont bien associées au post
        post.refresh_from_db()
        self.assertEqual(post.images.count(), 2)

    def test_add_images_to_post_unauthorized(self):
        """Test adding images to post by non-creator"""
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )

        # Créer des images de test
        test_images = []
        for i in range(2):
            test_images.append(self.create_test_image())

        self.client.force_authenticate(user=self.user2)

        # Ajouter les images au post
        response = self.client.post(
            reverse('add_images_to_post', args=[post.id]),
            {'images': test_images},
            format='multipart'
        )
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_add_images_to_nonexistent_post(self):
        """Test adding images to non-existent post"""
        # Créer des images de test
        test_images = []
        for i in range(2):
            test_images.append(self.create_test_image())

        self.client.force_authenticate(user=self.user)

        # Ajouter les images au post inexistant
        response = self.client.post(
            reverse('add_images_to_post', args=[99999]),
            {'images': test_images},
            format='multipart'
        )
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

    def test_remove_image_from_post(self):
        """Test removing an image from a post"""
        # Créer un post avec des images
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )

        # Créer des images et les associer au post
        images = []
        for i in range(2):
            image = Image.objects.create(
                image=f'test_image_{i}.jpg',
                created_by=self.user
            )
            from posts.models import PostImage
            PostImage.objects.create(post=post, image=image, order=i)
            images.append(image)

        self.client.force_authenticate(user=self.user)

        # Retirer la première image
        response = self.client.delete(reverse('remove_image_from_post', args=[images[0].id]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # Vérifier que l'image a bien été retirée
        post.refresh_from_db()
        self.assertEqual(post.images.count(), 1)
        self.assertNotIn(images[0], post.images.all())

    def test_remove_image_from_post_unauthorized(self):
        """Test removing an image from post by non-creator"""
        # Créer un post avec des images
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )

        # Créer une image et l'associer au post
        image = Image.objects.create(
            image=f'test_image.jpg',
            created_by=self.user
        )
        from posts.models import PostImage
        PostImage.objects.create(post=post, image=image, order=0)

        self.client.force_authenticate(user=self.user2)

        # Retirer l'image
        response = self.client.delete(reverse('remove_image_from_post', args=[image.id]))
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_remove_nonexistent_image_from_post(self):
        """Test removing a non-existent image from post"""
        self.client.force_authenticate(user=self.user)

        # Retirer une image qui n'existe pas
        response = self.client.delete(reverse('remove_image_from_post', args=[99999]))
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

    def test_post_images_endpoint(self):
        """Test getting images for a specific post"""
        # Créer un post avec des images
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )

        # Créer des images et les associer au post
        images = []
        for i in range(3):
            image = Image.objects.create(
                image=f'test_image_{i}.jpg',
                created_by=self.user
            )
            from posts.models import PostImage
            PostImage.objects.create(post=post, image=image, order=i)
            images.append(image)

        self.client.force_authenticate(user=self.user)

        # Obtenir les images du post
        response = self.client.get(reverse('post_images', args=[post.id]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 3)

    def test_post_images_empty(self):
        """Test getting images for a post with no images"""
        post = Post.objects.create(
            content='Test post',
            created_by=self.user
        )

        self.client.force_authenticate(user=self.user)

        # Obtenir les images du post
        response = self.client.get(reverse('post_images', args=[post.id]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 0)

    def test_post_images_nonexistent_post(self):
        """Test getting images for a non-existent post"""
        self.client.force_authenticate(user=self.user)

        # Obtenir les images d'un post qui n'existe pas
        response = self.client.get(reverse('post_images', args=[99999]))
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)