"""
Unit tests for notifications app models.
"""

import json
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.utils import timezone

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

User = get_user_model()


class NotificationChannelModelTest(TestCase):
    """Test cases for NotificationChannel model."""
    
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
    
    def test_telegram_channel_creation(self):
        """Test creating a Telegram notification channel."""
        channel = NotificationChannel.objects.create(
            name='Test Telegram',
            channel_type='telegram',
            configuration={
                'bot_token': 'test_token',
                'chat_id': '123456789'
            },
            created_by=self.user
        )
        
        self.assertEqual(channel.name, 'Test Telegram')
        self.assertEqual(channel.channel_type, 'telegram')
        self.assertTrue(channel.is_active)
        self.assertEqual(channel.configuration['bot_token'], 'test_token')
    
    def test_email_channel_creation(self):
        """Test creating an email notification channel."""
        channel = NotificationChannel.objects.create(
            name='Test Email',
            channel_type='email',
            configuration={
                'smtp_host': 'smtp.gmail.com',
                'smtp_port': 587,
                'username': 'test@gmail.com',
                'password': 'app_password',
                'use_tls': True
            },
            created_by=self.user
        )
        
        self.assertEqual(channel.channel_type, 'email')
        self.assertEqual(channel.configuration['smtp_host'], 'smtp.gmail.com')
    
    def test_webhook_channel_creation(self):
        """Test creating a webhook notification channel."""
        channel = NotificationChannel.objects.create(
            name='Test Webhook',
            channel_type='webhook',
            configuration={
                'url': 'https://api.example.com/webhook',
                'method': 'POST',
                'headers': {'Authorization': 'Bearer token123'}
            },
            created_by=self.user
        )
        
        self.assertEqual(channel.channel_type, 'webhook')
        self.assertEqual(channel.configuration['url'], 'https://api.example.com/webhook')
    
    def test_channel_unique_name(self):
        """Test unique constraint on channel name."""
        NotificationChannel.objects.create(
            name='Test Channel',
            channel_type='telegram',
            configuration={'bot_token': 'token1', 'chat_id': '123'},
            created_by=self.user
        )
        
        with self.assertRaises(Exception):
            NotificationChannel.objects.create(
                name='Test Channel',  # Duplicate name
                channel_type='email',
                configuration={'smtp_host': 'smtp.test.com'},
                created_by=self.user
            )


class NotificationTemplateModelTest(TestCase):
    """Test cases for NotificationTemplate model."""
    
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
    
    def test_template_creation(self):
        """Test basic template creation."""
        template = NotificationTemplate.objects.create(
            name='Stream Started',
            template_type='stream_started',
            subject_template='Stream {{channel_name}} Started',
            body_template='Stream {{channel_name}} has started at {{start_time}}',
            created_by=self.user
        )
        
        self.assertEqual(template.name, 'Stream Started')
        self.assertEqual(template.template_type, 'stream_started')
        self.assertIn('{{channel_name}}', template.subject_template)
        self.assertTrue(template.is_active)
    
    def test_template_unique_constraint(self):
        """Test unique constraint on template type."""
        NotificationTemplate.objects.create(
            name='Stream Started 1',
            template_type='stream_started',
            subject_template='Subject 1',
            body_template='Body 1',
            created_by=self.user
        )
        
        with self.assertRaises(Exception):
            NotificationTemplate.objects.create(
                name='Stream Started 2',
                template_type='stream_started',  # Duplicate type
                subject_template='Subject 2',
                body_template='Body 2',
                created_by=self.user
            )
    
    def test_template_rendering(self):
        """Test template rendering with context."""
        template = NotificationTemplate.objects.create(
            name='Stream Started',
            template_type='stream_started',
            subject_template='Stream {{channel_name}} Started',
            body_template='Stream {{channel_name}} has started at {{start_time}}',
            created_by=self.user
        )
        
        context = {
            'channel_name': 'Test Channel',
            'start_time': '2024-01-01 12:00:00'
        }
        
        rendered = template.render(context)
        
        self.assertEqual(rendered['subject'], 'Stream Test Channel Started')
        self.assertEqual(rendered['body'], 'Stream Test Channel has started at 2024-01-01 12:00:00')
    
    def test_template_validation(self):
        """Test template validation."""
        # Test empty subject template
        with self.assertRaises(ValidationError):
            template = NotificationTemplate(
                name='Invalid Template',
                template_type='test',
                subject_template='',  # Empty
                body_template='Valid body',
                created_by=self.user
            )
            template.full_clean()


class NotificationModelTest(TestCase):
    """Test cases for Notification model."""
    
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        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_creation(self):
        """Test basic notification creation."""
        notification = Notification.objects.create(
            channel=self.channel,
            template=self.template,
            recipient='test@example.com',
            subject='Test Subject',
            message='Test Message',
            context={'key': 'value'}
        )
        
        self.assertEqual(notification.channel, self.channel)
        self.assertEqual(notification.template, self.template)
        self.assertEqual(notification.status, 'pending')  # Default
        self.assertEqual(notification.retry_count, 0)  # Default
        self.assertIsNone(notification.sent_at)
    
    def test_notification_status_transitions(self):
        """Test notification status transitions."""
        notification = Notification.objects.create(
            channel=self.channel,
            template=self.template,
            recipient='test@example.com',
            subject='Test Subject',
            message='Test Message'
        )
        
        # Mark as processing
        notification.status = 'processing'
        notification.save()
        self.assertEqual(notification.status, 'processing')
        
        # Mark as completed
        notification.status = 'completed'
        notification.sent_at = timezone.now()
        notification.save()
        self.assertEqual(notification.status, 'completed')
        self.assertIsNotNone(notification.sent_at)
        
        # Mark as failed
        notification.status = 'failed'
        notification.error_message = 'Test error'
        notification.save()
        self.assertEqual(notification.status, 'failed')
        self.assertEqual(notification.error_message, 'Test error')
    
    def test_notification_retry_logic(self):
        """Test notification retry logic."""
        notification = Notification.objects.create(
            channel=self.channel,
            template=self.template,
            recipient='test@example.com',
            subject='Test Subject',
            message='Test Message'
        )
        
        # Increment retry count
        notification.retry_count += 1
        notification.save()
        self.assertEqual(notification.retry_count, 1)
        
        # Check if can retry (default max is 3)
        self.assertTrue(notification.retry_count < 3)
    
    def test_notification_str_representation(self):
        """Test string representation of notification."""
        notification = Notification.objects.create(
            channel=self.channel,
            template=self.template,
            recipient='test@example.com',
            subject='Test Subject',
            message='Test Message'
        )
        
        expected_str = f"Notification {notification.id} - {self.channel.name} (pending)"
        self.assertEqual(str(notification), expected_str)


class NotificationRuleModelTest(TestCase):
    """Test cases for NotificationRule model."""
    
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        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
        )
    
    def test_notification_rule_creation(self):
        """Test basic notification rule creation."""
        rule = NotificationRule.objects.create(
            name='Stream Start Rule',
            event_type='stream_started',
            template=self.template,
            conditions={'channel_name': 'Test Channel'},
            created_by=self.user
        )
        
        rule.channels.add(self.channel)
        
        self.assertEqual(rule.name, 'Stream Start Rule')
        self.assertEqual(rule.event_type, 'stream_started')
        self.assertEqual(rule.template, self.template)
        self.assertTrue(rule.is_active)
        self.assertIn(self.channel, rule.channels.all())
    
    def test_rule_condition_matching(self):
        """Test rule condition matching."""
        rule = NotificationRule.objects.create(
            name='Channel Specific Rule',
            event_type='stream_started',
            template=self.template,
            conditions={'channel_name': 'Specific Channel'},
            created_by=self.user
        )
        
        # Test matching context
        matching_context = {'channel_name': 'Specific Channel', 'other_data': 'value'}
        self.assertTrue(rule.matches_conditions(matching_context))
        
        # Test non-matching context
        non_matching_context = {'channel_name': 'Different Channel'}
        self.assertFalse(rule.matches_conditions(non_matching_context))
        
        # Test partial matching
        partial_context = {'other_data': 'value'}
        self.assertFalse(rule.matches_conditions(partial_context))
    
    def test_rule_without_conditions(self):
        """Test rule without specific conditions (matches all)."""
        rule = NotificationRule.objects.create(
            name='Global Rule',
            event_type='stream_started',
            template=self.template,
            conditions={},  # No conditions
            created_by=self.user
        )
        
        # Should match any context
        context = {'channel_name': 'Any Channel', 'data': 'anything'}
        self.assertTrue(rule.matches_conditions(context))
    
    def test_rule_priority_ordering(self):
        """Test rule ordering by priority."""
        high_priority = NotificationRule.objects.create(
            name='High Priority Rule',
            event_type='stream_started',
            template=self.template,
            priority=1,
            created_by=self.user
        )
        
        low_priority = NotificationRule.objects.create(
            name='Low Priority Rule',
            event_type='stream_started',
            template=self.template,
            priority=10,
            created_by=self.user
        )
        
        # Should be ordered by priority (ascending)
        rules = list(NotificationRule.objects.filter(event_type='stream_started'))
        self.assertEqual(rules[0], high_priority)
        self.assertEqual(rules[1], low_priority)
    
    def test_rule_str_representation(self):
        """Test string representation of notification rule."""
        rule = NotificationRule.objects.create(
            name='Test Rule',
            event_type='stream_started',
            template=self.template,
            created_by=self.user
        )
        
        expected_str = f"Rule: Test Rule (stream_started)"
        self.assertEqual(str(rule), expected_str)


class NotificationModelRelationshipsTest(TestCase):
    """Test cases for notification model relationships."""
    
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        self.telegram_channel = NotificationChannel.objects.create(
            name='Telegram Channel',
            channel_type='telegram',
            configuration={'bot_token': 'token', 'chat_id': '123'},
            created_by=self.user
        )
        
        self.email_channel = NotificationChannel.objects.create(
            name='Email Channel',
            channel_type='email',
            configuration={
                'smtp_host': 'smtp.test.com',
                'smtp_port': 587,
                'username': 'test@test.com',
                'password': 'password'
            },
            created_by=self.user
        )
        
        self.template = NotificationTemplate.objects.create(
            name='Multi Channel Template',
            template_type='ad_break_detected',
            subject_template='Ad Break Detected',
            body_template='Ad break detected in {{channel_name}}',
            created_by=self.user
        )
    
    def test_rule_multiple_channels(self):
        """Test notification rule with multiple channels."""
        rule = NotificationRule.objects.create(
            name='Multi Channel Rule',
            event_type='ad_break_detected',
            template=self.template,
            created_by=self.user
        )
        
        rule.channels.add(self.telegram_channel, self.email_channel)
        
        self.assertEqual(rule.channels.count(), 2)
        self.assertIn(self.telegram_channel, rule.channels.all())
        self.assertIn(self.email_channel, rule.channels.all())
    
    def test_channel_notifications_relationship(self):
        """Test channel to notifications relationship."""
        # Create notifications for the channel
        notification1 = Notification.objects.create(
            channel=self.telegram_channel,
            template=self.template,
            recipient='user1',
            subject='Test 1',
            message='Message 1'
        )
        
        notification2 = Notification.objects.create(
            channel=self.telegram_channel,
            template=self.template,
            recipient='user2',
            subject='Test 2',
            message='Message 2'
        )
        
        # Test reverse relationship
        channel_notifications = self.telegram_channel.notifications.all()
        self.assertEqual(channel_notifications.count(), 2)
        self.assertIn(notification1, channel_notifications)
        self.assertIn(notification2, channel_notifications)
    
    def test_template_notifications_relationship(self):
        """Test template to notifications relationship."""
        notification = Notification.objects.create(
            channel=self.telegram_channel,
            template=self.template,
            recipient='test_user',
            subject='Test Subject',
            message='Test Message'
        )
        
        # Test reverse relationship
        template_notifications = self.template.notifications.all()
        self.assertEqual(template_notifications.count(), 1)
        self.assertEqual(template_notifications.first(), notification)
    
    def test_user_created_objects(self):
        """Test user relationships to created objects."""
        # User should have created channels, templates, and rules
        user_channels = self.user.notification_channels.all()
        user_templates = self.user.notification_templates.all()
        
        self.assertEqual(user_channels.count(), 2)
        self.assertEqual(user_templates.count(), 1)
        
        self.assertIn(self.telegram_channel, user_channels)
        self.assertIn(self.email_channel, user_channels)
        self.assertIn(self.template, user_templates)
