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 .models import Notification, NotificationPreference, UserNotificationPreference, CategoryNotificationPreference, FollowRequest
from follows.models import FollowUser, FollowCategory
from categories.models import Category
from posts.models import Post
from comments.models import Comment

User = get_user_model()


class NotificationModelTestCase(TestCase):
    def setUp(self):
        self.user1 = User.objects.create_user(
            username='user1',
            email='user1@example.com',
            password='pass123',
            first_name='User',
            last_name='One'
        )
        self.user2 = User.objects.create_user(
            username='user2',
            email='user2@example.com',
            password='pass123',
            first_name='User',
            last_name='Two'
        )

    def test_create_notification(self):
        """Test creating a notification"""
        notification = Notification.objects.create(
            recipient=self.user1,
            sender=self.user2,
            notification_type='follow',
            content='User Two started following you'
        )
        self.assertEqual(notification.recipient, self.user1)
        self.assertEqual(notification.sender, self.user2)
        self.assertFalse(notification.is_read)

    def test_notification_preference_defaults(self):
        """Test notification preference defaults"""
        pref = NotificationPreference.objects.create(user=self.user1)
        self.assertTrue(pref.notify_on_follow)
        self.assertTrue(pref.notify_on_like)
        self.assertTrue(pref.notify_on_comment)
        self.assertFalse(pref.notify_on_followed_user_post)
        self.assertFalse(pref.notify_on_followed_category_post)


class FollowRequestModelTestCase(TestCase):
    def setUp(self):
        self.user1 = User.objects.create_user(
            username='user1',
            email='user1@example.com',
            password='pass123'
        )
        self.user2 = User.objects.create_user(
            username='user2',
            email='user2@example.com',
            password='pass123',
            is_private=True
        )

    def test_create_follow_request(self):
        """Test creating a follow request"""
        follow_request = FollowRequest.objects.create(
            requester=self.user1,
            target=self.user2
        )
        self.assertEqual(follow_request.status, 'pending')
        self.assertEqual(follow_request.requester, self.user1)
        self.assertEqual(follow_request.target, self.user2)

    def test_follow_request_unique_constraint(self):
        """Test that a user can only send one follow request to another user"""
        FollowRequest.objects.create(requester=self.user1, target=self.user2)
        with self.assertRaises(Exception):
            FollowRequest.objects.create(requester=self.user1, target=self.user2)

    def test_follow_request_self_validation(self):
        """Test that a user cannot request to follow themselves"""
        from django.core.exceptions import ValidationError
        with self.assertRaises(ValidationError):
            FollowRequest.objects.create(requester=self.user1, target=self.user1)


class NotificationAPITestCase(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.user1 = User.objects.create_user(
            username='user1',
            email='user1@example.com',
            password='pass123',
            first_name='User',
            last_name='One'
        )
        self.user2 = User.objects.create_user(
            username='user2',
            email='user2@example.com',
            password='pass123',
            first_name='User',
            last_name='Two'
        )

    def test_list_notifications(self):
        """Test listing notifications"""
        self.client.force_authenticate(user=self.user1)

        # Create a notification
        Notification.objects.create(
            recipient=self.user1,
            sender=self.user2,
            notification_type='follow',
            content='User Two started following you'
        )

        response = self.client.get(reverse('list_notifications'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['count'], 1)

    def test_get_unread_count(self):
        """Test getting unread notification count"""
        self.client.force_authenticate(user=self.user1)

        # Create notifications
        Notification.objects.create(
            recipient=self.user1,
            sender=self.user2,
            notification_type='follow',
            content='Test'
        )
        Notification.objects.create(
            recipient=self.user1,
            sender=self.user2,
            notification_type='like_post',
            content='Test',
            is_read=True
        )

        response = self.client.get(reverse('get_unread_count'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['unread_count'], 1)

    def test_mark_notification_as_read(self):
        """Test marking a notification as read"""
        self.client.force_authenticate(user=self.user1)

        notification = Notification.objects.create(
            recipient=self.user1,
            sender=self.user2,
            notification_type='follow',
            content='Test'
        )

        response = self.client.put(reverse('mark_notification_as_read', args=[notification.id]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        notification.refresh_from_db()
        self.assertTrue(notification.is_read)

    def test_delete_notification(self):
        """Test deleting a notification"""
        self.client.force_authenticate(user=self.user1)

        notification = Notification.objects.create(
            recipient=self.user1,
            sender=self.user2,
            notification_type='follow',
            content='Test'
        )

        response = self.client.delete(reverse('delete_notification', args=[notification.id]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        self.assertFalse(Notification.objects.filter(id=notification.id).exists())

    def test_notification_preferences(self):
        """Test getting and updating notification preferences"""
        self.client.force_authenticate(user=self.user1)

        # Get preferences (should create if not exists)
        response = self.client.get(reverse('notification_preferences'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # Update preferences
        response = self.client.put(
            reverse('notification_preferences'),
            {'notify_on_follow': False, 'notify_on_like': True}
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertFalse(response.data['notify_on_follow'])


class FollowRequestAPITestCase(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.user1 = User.objects.create_user(
            username='user1',
            email='user1@example.com',
            password='pass123',
            first_name='User',
            last_name='One'
        )
        self.user2 = User.objects.create_user(
            username='user2',
            email='user2@example.com',
            password='pass123',
            first_name='User',
            last_name='Two',
            is_private=True
        )

    def test_list_follow_requests(self):
        """Test listing follow requests"""
        self.client.force_authenticate(user=self.user2)

        # Create a follow request
        FollowRequest.objects.create(requester=self.user1, target=self.user2)

        response = self.client.get(reverse('list_follow_requests'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['count'], 1)

    def test_accept_follow_request(self):
        """Test accepting a follow request"""
        self.client.force_authenticate(user=self.user2)

        follow_request = FollowRequest.objects.create(requester=self.user1, target=self.user2)

        response = self.client.post(reverse('accept_follow_request', args=[follow_request.id]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # Check that FollowUser was created
        self.assertTrue(FollowUser.objects.filter(follower=self.user1, followed=self.user2).exists())

        # Check that follow request status was updated
        follow_request.refresh_from_db()
        self.assertEqual(follow_request.status, 'accepted')

    def test_reject_follow_request(self):
        """Test rejecting a follow request"""
        self.client.force_authenticate(user=self.user2)

        follow_request = FollowRequest.objects.create(requester=self.user1, target=self.user2)

        response = self.client.post(reverse('reject_follow_request', args=[follow_request.id]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # Check that FollowUser was NOT created
        self.assertFalse(FollowUser.objects.filter(follower=self.user1, followed=self.user2).exists())

        # Check that follow request status was updated
        follow_request.refresh_from_db()
        self.assertEqual(follow_request.status, 'rejected')

    def test_cancel_follow_request(self):
        """Test canceling a sent follow request"""
        self.client.force_authenticate(user=self.user1)

        follow_request = FollowRequest.objects.create(requester=self.user1, target=self.user2)

        response = self.client.delete(reverse('cancel_follow_request', args=[follow_request.id]))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # Check that follow request was deleted
        self.assertFalse(FollowRequest.objects.filter(id=follow_request.id).exists())


class UserNotificationPreferenceTestCase(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.user1 = User.objects.create_user(
            username='user1',
            email='user1@example.com',
            password='pass123'
        )
        self.user2 = User.objects.create_user(
            username='user2',
            email='user2@example.com',
            password='pass123'
        )
        # User1 follows User2
        FollowUser.objects.create(follower=self.user1, followed=self.user2)

    def test_set_user_notification_preference(self):
        """Test setting notification preference for a specific user"""
        self.client.force_authenticate(user=self.user1)

        response = self.client.put(
            reverse('user_notification_preference', args=[self.user2.id]),
            {'notify_on_new_post': True}
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # Check that preference was created
        pref = UserNotificationPreference.objects.get(user=self.user1, target_user=self.user2)
        self.assertTrue(pref.notify_on_new_post)

    def test_cannot_set_preference_without_following(self):
        """Test that you must follow a user to set notification preference"""
        self.client.force_authenticate(user=self.user2)

        user3 = User.objects.create_user(
            username='user3',
            email='user3@example.com',
            password='pass123'
        )

        response = self.client.put(
            reverse('user_notification_preference', args=[user3.id]),
            {'notify_on_new_post': True}
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
