"""
API tests for notifications app endpoints.
"""

import json
from unittest.mock import patch, MagicMock
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework.test import APIClient
from rest_framework import status

from apps.notifications.models import (
    NotificationChannel, NotificationTemplate, Notification, NotificationRule
)

User = get_user_model()


class NotificationChannelAPITest(TestCase):
    """Test cases for NotificationChannel API endpoints."""
    
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.client.force_authenticate(user=self.user)
        
        self.channel = NotificationChannel.objects.create(
            name='Test Channel',
            channel_type='telegram',
            configuration={'bot_token': 'token', 'chat_id': '123'},
            created_by=self.user
        )
    
    def test_list_channels(self):
        """Test listing notification channels."""
        url = reverse('notificationchannel-list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['name'], 'Test Channel')
    
    def test_create_telegram_channel(self):
        """Test creating a Telegram channel."""
        url = reverse('notificationchannel-list')
        data = {
            'name': 'New Telegram Channel',
            'channel_type': 'telegram',
            'configuration': {
                'bot_token': 'new_token',
                'chat_id': '987654321'
            }
        }
        
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['name'], 'New Telegram Channel')
        self.assertEqual(response.data['channel_type'], 'telegram')
    
    def test_create_email_channel(self):
        """Test creating an email channel."""
        url = reverse('notificationchannel-list')
        data = {
            'name': 'Email Channel',
            'channel_type': 'email',
            'configuration': {
                'smtp_host': 'smtp.gmail.com',
                'smtp_port': 587,
                'username': 'test@gmail.com',
                'password': 'app_password',
                'use_tls': True
            }
        }
        
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['channel_type'], 'email')
    
    def test_create_webhook_channel(self):
        """Test creating a webhook channel."""
        url = reverse('notificationchannel-list')
        data = {
            'name': 'Webhook Channel',
            'channel_type': 'webhook',
            'configuration': {
                'url': 'https://api.example.com/webhook',
                'method': 'POST',
                'headers': {'Authorization': 'Bearer token'}
            }
        }
        
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['channel_type'], 'webhook')
    
    def test_invalid_channel_configuration(self):
        """Test creating channel with invalid configuration."""
        url = reverse('notificationchannel-list')
        data = {
            'name': 'Invalid Channel',
            'channel_type': 'telegram',
            'configuration': {
                'invalid_field': 'value'
                # Missing required bot_token and chat_id
            }
        }
        
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
    
    @patch('apps.notifications.services.TelegramService.send_test_message')
    def test_test_channel_action(self, mock_send_test):
        """Test testing a notification channel."""
        mock_send_test.return_value = True
        
        url = reverse('notificationchannel-test', kwargs={'pk': self.channel.pk})
        response = self.client.post(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTrue(response.data['success'])
        mock_send_test.assert_called_once()


class NotificationTemplateAPITest(TestCase):
    """Test cases for NotificationTemplate API endpoints."""
    
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.client.force_authenticate(user=self.user)
        
        self.template = NotificationTemplate.objects.create(
            name='Test Template',
            template_type='stream_started',
            subject_template='Stream {{channel_name}} Started',
            body_template='Stream {{channel_name}} has started',
            created_by=self.user
        )
    
    def test_list_templates(self):
        """Test listing notification templates."""
        url = reverse('notificationtemplate-list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['name'], 'Test Template')
    
    def test_create_template(self):
        """Test creating a notification template."""
        url = reverse('notificationtemplate-list')
        data = {
            'name': 'New Template',
            'template_type': 'ad_break_detected',
            'subject_template': 'Ad Break in {{channel_name}}',
            'body_template': 'Ad break detected in {{channel_name}} at {{detection_time}}'
        }
        
        response = self.client.post(url, data)
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['name'], 'New Template')
        self.assertEqual(response.data['template_type'], 'ad_break_detected')
    
    def test_template_preview_action(self):
        """Test template preview with context."""
        url = reverse('notificationtemplate-preview', kwargs={'pk': self.template.pk})
        data = {
            'context': {
                'channel_name': 'Test Channel',
                'start_time': '2024-01-01 12:00:00'
            }
        }
        
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['subject'], 'Stream Test Channel Started')
        self.assertIn('Test Channel', response.data['body'])


class NotificationAPITest(TestCase):
    """Test cases for Notification API endpoints."""
    
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.client.force_authenticate(user=self.user)
        
        self.channel = NotificationChannel.objects.create(
            name='Test Channel',
            channel_type='telegram',
            configuration={'bot_token': 'token', 'chat_id': '123'},
            created_by=self.user
        )
        
        self.template = NotificationTemplate.objects.create(
            name='Test Template',
            template_type='test',
            subject_template='Test Subject',
            body_template='Test Body',
            created_by=self.user
        )
        
        self.notification = Notification.objects.create(
            channel=self.channel,
            template=self.template,
            recipient='test@example.com',
            subject='Test Subject',
            message='Test Message'
        )
    
    def test_list_notifications(self):
        """Test listing notifications."""
        url = reverse('notification-list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['subject'], 'Test Subject')
    
    def test_filter_notifications_by_status(self):
        """Test filtering notifications by status."""
        # Create notification with different status
        Notification.objects.create(
            channel=self.channel,
            template=self.template,
            recipient='test2@example.com',
            subject='Test Subject 2',
            message='Test Message 2',
            status='completed'
        )
        
        url = reverse('notification-list')
        response = self.client.get(url, {'status': 'pending'})
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['status'], 'pending')
    
    def test_filter_notifications_by_channel(self):
        """Test filtering notifications by channel."""
        # Create another channel and notification
        other_channel = NotificationChannel.objects.create(
            name='Other Channel',
            channel_type='email',
            configuration={'smtp_host': 'smtp.test.com'},
            created_by=self.user
        )
        
        Notification.objects.create(
            channel=other_channel,
            template=self.template,
            recipient='test2@example.com',
            subject='Other Subject',
            message='Other Message'
        )
        
        url = reverse('notification-list')
        response = self.client.get(url, {'channel': self.channel.id})
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['channel'], self.channel.id)
    
    @patch('apps.notifications.tasks.send_notification.delay')
    def test_retry_notification_action(self, mock_task):
        """Test retrying a failed notification."""
        # Mark notification as failed
        self.notification.status = 'failed'
        self.notification.error_message = 'Test error'
        self.notification.save()
        
        mock_task.return_value = MagicMock(id='test-task-id')
        
        url = reverse('notification-retry', kwargs={'pk': self.notification.pk})
        response = self.client.post(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('task_id', response.data)
        mock_task.assert_called_once()
    
    def test_retry_non_failed_notification(self):
        """Test retrying a non-failed notification."""
        url = reverse('notification-retry', kwargs={'pk': self.notification.pk})
        response = self.client.post(url)
        
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertIn('can only retry failed', response.data['error'].lower())


class NotificationRuleAPITest(TestCase):
    """Test cases for NotificationRule API endpoints."""
    
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.client.force_authenticate(user=self.user)
        
        self.channel = NotificationChannel.objects.create(
            name='Test Channel',
            channel_type='telegram',
            configuration={'bot_token': 'token', 'chat_id': '123'},
            created_by=self.user
        )
        
        self.template = NotificationTemplate.objects.create(
            name='Test Template',
            template_type='stream_started',
            subject_template='Stream Started',
            body_template='Stream has started',
            created_by=self.user
        )
        
        self.rule = NotificationRule.objects.create(
            name='Test Rule',
            event_type='stream_started',
            template=self.template,
            created_by=self.user
        )
        self.rule.channels.add(self.channel)
    
    def test_list_rules(self):
        """Test listing notification rules."""
        url = reverse('notificationrule-list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['name'], 'Test Rule')
    
    def test_create_rule(self):
        """Test creating a notification rule."""
        url = reverse('notificationrule-list')
        data = {
            'name': 'New Rule',
            'event_type': 'ad_break_detected',
            'template': self.template.id,
            'channels': [self.channel.id],
            'conditions': {'channel_name': 'Specific Channel'},
            'priority': 5
        }
        
        response = self.client.post(url, data, format='json')
        
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['name'], 'New Rule')
        self.assertEqual(response.data['event_type'], 'ad_break_detected')
    
    def test_filter_rules_by_event_type(self):
        """Test filtering rules by event type."""
        # Create rule with different event type
        NotificationRule.objects.create(
            name='Ad Break Rule',
            event_type='ad_break_detected',
            template=self.template,
            created_by=self.user
        )
        
        url = reverse('notificationrule-list')
        response = self.client.get(url, {'event_type': 'stream_started'})
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        self.assertEqual(response.data['results'][0]['event_type'], 'stream_started')


class NotificationStatisticsAPITest(TestCase):
    """Test cases for notification statistics endpoints."""
    
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.client.force_authenticate(user=self.user)
        
        self.channel = NotificationChannel.objects.create(
            name='Test Channel',
            channel_type='telegram',
            configuration={'bot_token': 'token', 'chat_id': '123'},
            created_by=self.user
        )
        
        self.template = NotificationTemplate.objects.create(
            name='Test Template',
            template_type='test',
            subject_template='Test Subject',
            body_template='Test Body',
            created_by=self.user
        )
        
        # Create test notifications with different statuses
        Notification.objects.create(
            channel=self.channel,
            template=self.template,
            recipient='user1@example.com',
            subject='Test 1',
            message='Message 1',
            status='completed'
        )
        
        Notification.objects.create(
            channel=self.channel,
            template=self.template,
            recipient='user2@example.com',
            subject='Test 2',
            message='Message 2',
            status='failed'
        )
        
        Notification.objects.create(
            channel=self.channel,
            template=self.template,
            recipient='user3@example.com',
            subject='Test 3',
            message='Message 3',
            status='pending'
        )
    
    def test_notification_statistics(self):
        """Test notification statistics endpoint."""
        url = reverse('notification-statistics')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        
        stats = response.data
        self.assertEqual(stats['total_notifications'], 3)
        self.assertEqual(stats['completed_notifications'], 1)
        self.assertEqual(stats['failed_notifications'], 1)
        self.assertEqual(stats['pending_notifications'], 1)
        self.assertEqual(stats['success_rate'], 33.33)  # 1/3 * 100
    
    def test_channel_statistics(self):
        """Test channel-specific statistics."""
        url = reverse('notificationchannel-statistics', kwargs={'pk': self.channel.pk})
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        
        stats = response.data
        self.assertEqual(stats['total_notifications'], 3)
        self.assertEqual(stats['success_rate'], 33.33)


class NotificationThrottlingTest(TestCase):
    """Test cases for notification API throttling."""
    
    def setUp(self):
        self.client = APIClient()
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        self.client.force_authenticate(user=self.user)
        
        self.channel = NotificationChannel.objects.create(
            name='Test Channel',
            channel_type='telegram',
            configuration={'bot_token': 'token', 'chat_id': '123'},
            created_by=self.user
        )
        
        self.template = NotificationTemplate.objects.create(
            name='Test Template',
            template_type='test',
            subject_template='Test Subject',
            body_template='Test Body',
            created_by=self.user
        )
    
    def test_notification_api_throttling(self):
        """Test that notification API has throttling applied."""
        url = reverse('notification-list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # In a real test environment, you would configure test throttle rates
        # and test that rapid requests get throttled


class NotificationPermissionsTest(TestCase):
    """Test cases for notification API permissions."""
    
    def setUp(self):
        self.client = APIClient()
        self.user1 = User.objects.create_user(
            username='user1',
            email='user1@example.com',
            password='testpass123'
        )
        self.user2 = User.objects.create_user(
            username='user2',
            email='user2@example.com',
            password='testpass123'
        )
        
        self.channel = NotificationChannel.objects.create(
            name='User1 Channel',
            channel_type='telegram',
            configuration={'bot_token': 'token', 'chat_id': '123'},
            created_by=self.user1
        )
    
    def test_unauthorized_access(self):
        """Test API access without authentication."""
        url = reverse('notificationchannel-list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
    
    def test_user_can_only_see_own_channels(self):
        """Test that users can only see their own channels."""
        # User1 creates a channel
        self.client.force_authenticate(user=self.user1)
        
        url = reverse('notificationchannel-list')
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)
        
        # User2 should not see User1's channel
        self.client.force_authenticate(user=self.user2)
        
        response = self.client.get(url)
        
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 0)
    
    def test_user_cannot_modify_others_channels(self):
        """Test that users cannot modify other users' channels."""
        self.client.force_authenticate(user=self.user2)
        
        url = reverse('notificationchannel-detail', kwargs={'pk': self.channel.pk})
        data = {'name': 'Hacked Channel'}
        
        response = self.client.patch(url, data)
        
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
