"""
Comprehensive unit tests for AI analysis reporting email system.

Tests cover:
- Admin notification emails
- User resolution notification emails
- Email template rendering
- Permission-based email recipients
- Email failure handling
"""

from django.test import TestCase, override_settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.core import mail
from django.utils import timezone
from datetime import date, time
from unittest.mock import patch, MagicMock, call
from bazi.models import Person
from liuyao.models import liuyao
from api.utils import (
    get_report_recipients,
    send_admin_report_notification,
    send_user_resolution_notification,
    get_category_display,
    get_admin_action_display
)

User = get_user_model()


class EmailRecipientsTests(TestCase):
    """Test email recipient management and permissions"""
    
    def setUp(self):
        """Set up test data"""
        self.admin1 = User.objects.create_user(
            phone='13800138001',
            email='admin1@example.com',
            first_name='Admin',
            last_name='One',
            is_staff=True,
            is_active=True
        )
        
        self.admin2 = User.objects.create_user(
            phone='13800138002',
            email='admin2@example.com',
            first_name='Admin',
            last_name='Two',
            is_staff=True,
            is_active=True
        )
        
        self.regular_user = User.objects.create_user(
            phone='13800138003',
            email='user@example.com',
            first_name='Regular',
            last_name='User',
            is_staff=False,
            is_active=True
        )
        
        # Clean up any existing permissions first
        Permission.objects.filter(codename='can_receive_ai_report_emails').delete()
        
        # Create the permission
        self.permission = Permission.objects.create(
            codename='can_receive_ai_report_emails',
            name='Can receive AI analysis report emails',
            content_type_id=1  # Generic content type
        )
    
    def tearDown(self):
        """Clean up test data"""
        from tests.test_utils import cleanup_test_permissions
        cleanup_test_permissions()
    
    def test_get_report_recipients_no_permission(self):
        """Test getting recipients when no users have permission"""
        recipients = get_report_recipients()
        self.assertEqual(recipients, [])
    
    def test_get_report_recipients_with_permission(self):
        """Test getting recipients when users have permission"""
        # Give permission to admin users
        self.admin1.user_permissions.add(self.permission)
        self.admin2.user_permissions.add(self.permission)
        
        recipients = get_report_recipients()
        self.assertIn('admin1@example.com', recipients)
        self.assertIn('admin2@example.com', recipients)
        self.assertEqual(len(recipients), 2)
    
    def test_get_report_recipients_excludes_non_staff(self):
        """Test that non-staff users are excluded even with permission"""
        self.regular_user.user_permissions.add(self.permission)
        
        recipients = get_report_recipients()
        self.assertNotIn('user@example.com', recipients)
        self.assertEqual(len(recipients), 0)
    
    def test_get_report_recipients_excludes_inactive(self):
        """Test that inactive users are excluded"""
        self.admin1.user_permissions.add(self.permission)
        self.admin1.is_active = False
        self.admin1.save()
        
        recipients = get_report_recipients()
        self.assertNotIn('admin1@example.com', recipients)
    
    def test_get_report_recipients_excludes_no_email(self):
        """Test that users without email are excluded"""
        self.admin1.user_permissions.add(self.permission)
        self.admin1.email = ''
        self.admin1.save()
        
        recipients = get_report_recipients()
        self.assertNotIn('admin1@example.com', recipients)
        self.assertEqual(len(recipients), 0)
    
    def test_get_report_recipients_handles_missing_permission(self):
        """Test handling when permission doesn't exist"""
        # Delete the permission
        Permission.objects.filter(codename='can_receive_ai_report_emails').delete()
        
        recipients = get_report_recipients()
        self.assertEqual(recipients, [])


@override_settings(EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend')
class AdminNotificationEmailTests(TestCase):
    """Test admin notification email sending"""
    
    def setUp(self):
        """Set up test data"""
        # Use the fixed admin creation utility
        from tests.test_utils import create_admin_with_report_permissions
        
        self.admin = create_admin_with_report_permissions(
            phone='13800138001',
            email='admin@example.com',
            first_name='Admin',
            last_name='User'
        )
        
        self.user = User.objects.create_user(
            phone='13800138000',
            email='user@example.com',
            first_name='Test',
            last_name='User'
        )
        
        self.person = Person.objects.create(
            name='测试用户',
            birth_date=date(1990, 1, 1),
            birth_time=time(12, 0),
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'bazi_analysis': 'Test BaZi analysis content'},
            bazi_report_timestamp=timezone.now()
        )
        
        self.liuyao_entry = liuyao.objects.create(
            question='测试问题',
            user=self.user,
            qdate=timezone.now(),
            y1='1', y2='0', y3='1', y4='0', y5='1', y6='0',
            analysis_status='completed',
            ai_analysis={'response': 'Test LiuYao analysis'},
            report_timestamp=timezone.now(),
            report_resolved_at=timezone.now()
        )
    
    def tearDown(self):
        """Clean up test data"""
        from tests.test_utils import cleanup_test_permissions
        cleanup_test_permissions()
    
    def test_send_admin_notification_bazi_success(self):
        """Test successful admin notification for BaZi report"""
        result = send_admin_report_notification(
            self.person,
            'bazi',
            'inappropriate_content',
            'This analysis is inappropriate'
        )
        
        self.assertTrue(result)
        self.assertEqual(len(mail.outbox), 1)
        
        email = mail.outbox[0]
        self.assertEqual(email.to, ['admin@example.com'])
        self.assertIn('新的AI分析举报', email.subject)
        self.assertIn('BAZI', email.subject)
        self.assertIn('内容不当', email.body)
        self.assertIn('This analysis is inappropriate', email.body)
        self.assertIn('测试用户', email.body)
    
    def test_send_admin_notification_liuyao_success(self):
        """Test successful admin notification for LiuYao report"""
        result = send_admin_report_notification(
            self.liuyao_entry,
            'liuyao',
            'inaccurate_analysis',
            'Hexagram interpretation is wrong'
        )
        
        self.assertTrue(result)
        self.assertEqual(len(mail.outbox), 1)
        
        email = mail.outbox[0]
        self.assertIn('LIUYAO', email.subject)
        self.assertIn('测试问题', email.body)
        self.assertIn('分析不准确', email.body)
    
    def test_send_admin_notification_no_recipients(self):
        """Test admin notification when no recipients available"""
        # Remove admin permission
        self.admin.user_permissions.clear()
        
        result = send_admin_report_notification(
            self.person,
            'bazi',
            'technical_error',
            'Test message',
            silent=True  # Explicitly silent for clean test output
        )
        
        self.assertFalse(result)
        self.assertEqual(len(mail.outbox), 0)
    
    def test_send_admin_notification_empty_message(self):
        """Test admin notification with empty user message"""
        result = send_admin_report_notification(
            self.person,
            'bazi',
            'other',
            ''
        )
        
        self.assertTrue(result)
        email = mail.outbox[0]
        self.assertIn('用户未留言', email.body)
    
    def test_send_admin_notification_unicode_content(self):
        """Test admin notification with Unicode content"""
        unicode_message = '这是一个包含中文和emoji的消息 🚨'
        
        result = send_admin_report_notification(
            self.person,
            'bazi',
            'inappropriate_content',
            unicode_message
        )
        
        self.assertTrue(result)
        email = mail.outbox[0]
        self.assertIn(unicode_message, email.body)
    
    @patch('api.utils.send_mail')
    def test_send_admin_notification_email_failure(self, mock_send_mail):
        """Test handling of email sending failure"""
        mock_send_mail.side_effect = Exception("SMTP server down")
        
        result = send_admin_report_notification(
            self.person,
            'bazi',
            'technical_error',
            'Test message'
        )
        
        self.assertFalse(result)
    
    def test_send_admin_notification_html_and_text(self):
        """Test that both HTML and plain text versions are sent"""
        result = send_admin_report_notification(
            self.person,
            'bazi',
            'inappropriate_content',
            'Test HTML email'
        )
        
        self.assertTrue(result)
        
        # Check that send_mail was called with html_message parameter
        email = mail.outbox[0]
        # Django's locmem backend should preserve both versions
        self.assertIsNotNone(email.body)  # Plain text
        self.assertTrue(hasattr(email, 'alternatives'))  # HTML alternative


@override_settings(EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend')
class UserResolutionEmailTests(TestCase):
    """Test user resolution notification emails"""
    
    def setUp(self):
        """Set up test data"""
        self.user = User.objects.create_user(
            phone='13800138000',
            email='user@example.com',
            first_name='测试',
            last_name='用户'
        )
        
        self.admin = User.objects.create_user(
            phone='13800138001',
            email='admin@example.com',
            first_name='Admin',
            last_name='User',
            is_staff=True
        )
        
        self.person = Person.objects.create(
            name='测试人员',
            birth_date=date(1990, 1, 1),
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'bazi_analysis': 'Test content'},
            bazi_analysis_reported=True,
            bazi_report_category='inappropriate_content',
            bazi_report_message='Test report',
            bazi_report_status='resolved',
            bazi_report_resolved_by=self.admin,
            bazi_report_resolved_at=timezone.now(),
            bazi_report_timestamp=timezone.now()
        )
    
    def tearDown(self):
        """Clean up test data"""
        from tests.test_utils import cleanup_test_permissions
        cleanup_test_permissions()
    
    def test_send_user_resolution_notification_success(self):
        """Test successful user resolution notification"""
        result = send_user_resolution_notification(
            self.user,
            self.person,
            'bazi',
            'resolved',
            'We have reviewed your report and taken appropriate action.'
        )
        
        self.assertTrue(result)
        self.assertEqual(len(mail.outbox), 1)
        
        email = mail.outbox[0]
        self.assertEqual(email.to, ['user@example.com'])
        self.assertIn('举报处理结果', email.subject)
        self.assertIn('举报处理结果', email.subject)
        self.assertIn('测试 用户', email.body)
        self.assertIn('We have reviewed', email.body)
    
    def test_send_user_resolution_notification_no_email(self):
        """Test user resolution notification when user has no email"""
        self.user.email = ''
        self.user.save()
        
        result = send_user_resolution_notification(
            self.user,
            self.person,
            'bazi',
            'resolved',
            'Admin notes'
        )
        
        self.assertFalse(result)
        self.assertEqual(len(mail.outbox), 0)
    
    def test_send_user_resolution_notification_no_user(self):
        """Test user resolution notification when user is None"""
        result = send_user_resolution_notification(
            None,
            self.person,
            'bazi',
            'dismiss',
            'Report dismissed'
        )
        
        self.assertFalse(result)
        self.assertEqual(len(mail.outbox), 0)
    
    def test_send_user_resolution_notification_all_actions(self):
        """Test user resolution notification for all admin actions"""
        actions = ['dismiss', 'regenerate', 'content_warning', 'escalate']
        
        for action in actions:
            # Clear previous emails
            mail.outbox.clear()
            
            result = send_user_resolution_notification(
                self.user,
                self.person,
                'bazi',
                action,
                f'Admin notes for {action}'
            )
            self.assertFalse(result)
            self.assertEqual(len(mail.outbox), 0)

        # Resolved should send
        mail.outbox.clear()
        result = send_user_resolution_notification(
            self.user,
            self.person,
            'bazi',
            'resolved',
            'Admin notes for resolved'
        )
        self.assertTrue(result)
        self.assertEqual(len(mail.outbox), 1)
    
    def test_send_user_resolution_notification_liuyao(self):
        """Test user resolution notification for LiuYao analysis"""
        liuyao_entry = liuyao.objects.create(
            question='Test question',
            user=self.user,
            qdate=timezone.now(),
            y1='1', y2='0', y3='1', y4='0', y5='1', y6='0',
            analysis_status='completed',
            ai_analysis={'response': 'Test content'},
            analysis_reported=True,
            report_category='inaccurate_analysis',
            report_status='resolved',
            report_resolved_by=self.admin,
            report_resolved_at=timezone.now()
        )
        
        result = send_user_resolution_notification(
            self.user,
            liuyao_entry,
            'liuyao',
            'resolved',
            'We have regenerated the analysis'
        )
        
        self.assertTrue(result)
        email = mail.outbox[0]
        self.assertIn('举报处理结果', email.subject)
        self.assertIn('Test question', email.body)
    
    @patch('api.utils.send_mail')
    def test_send_user_resolution_notification_email_failure(self, mock_send_mail):
        """Test handling of user notification email failure"""
        mock_send_mail.side_effect = Exception("Email service unavailable")
        
        result = send_user_resolution_notification(
            self.user,
            self.person,
            'bazi',
            'resolve',
            'Test admin notes'
        )
        
        self.assertFalse(result)


class EmailUtilityFunctionTests(TestCase):
    """Test email utility functions"""
    
    def test_get_category_display(self):
        """Test category display name function"""
        test_cases = {
            'inappropriate_content': '内容不当',
            'inaccurate_analysis': '分析不准确',
            'offensive_language': '语言冒犯',
            'technical_error': '技术错误',
            'other': '其他',
            'invalid_category': 'invalid_category'  # Should return as-is
        }
        
        for category, expected in test_cases.items():
            result = get_category_display(category)
            self.assertEqual(result, expected)
    
    def test_get_admin_action_display(self):
        """Test admin action display name function"""
        test_cases = {
            'dismiss': '已忽略',
            'resolve': '已解决',
            'regenerate': 'regenerate',
            'content_warning': 'content_warning',
            'escalate': 'escalate',
            'invalid_action': 'invalid_action'  # Should return as-is
        }
        
        for action, expected in test_cases.items():
            result = get_admin_action_display(action)
            self.assertEqual(result, expected)


class EmailTemplateTests(TestCase):
    """Test email template rendering and content"""
    
    def setUp(self):
        """Set up test data"""
        self.user = User.objects.create_user(
            phone='13800138000',
            email='user@example.com',
            first_name='测试',
            last_name='用户'
        )
        
        self.admin = User.objects.create_user(
            phone='13800138001',
            email='admin@example.com',
            first_name='管理员',
            last_name='用户',
            is_staff=True
        )
        
        # Give admin permission
        # Clean up any existing permissions first
        Permission.objects.filter(codename='can_receive_ai_report_emails').delete()
        
        permission = Permission.objects.create(
            codename='can_receive_ai_report_emails',
            name='Can receive AI analysis report emails',
            content_type_id=1
        )
        self.admin.user_permissions.add(permission)
    
    def tearDown(self):
        """Clean up test data"""
        from tests.test_utils import cleanup_test_permissions
        cleanup_test_permissions()
    
    @override_settings(EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend')
    def test_admin_email_template_variables(self):
        """Test that admin email template receives all necessary variables"""
        person = Person.objects.create(
            name='模板测试',
            birth_date=date(1990, 5, 15),
            gender='F',
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'bazi_analysis': 'Template test content'},
            bazi_report_timestamp=timezone.now(),
            bazi_report_resolved_at=timezone.now()
        )
        
        send_admin_report_notification(
            person,
            'bazi',
            'inappropriate_content',
            '测试模板变量渲染'
        )
        
        email = mail.outbox[0]
        
        # Check that all expected variables are in the email
        expected_content = [
            'BAZI',  # analysis_type
            '内容不当',  # category_display
            '测试模板变量渲染',  # user_message
            '模板测试',  # analysis object name
            'May 15, 1990',  # birth date
            '测试 用户',  # reporting user name
            'user@example.com'  # reporting user email
        ]
        
        for content in expected_content:
            self.assertIn(content, email.body)
    
    @override_settings(EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend')
    def test_user_resolution_email_template_variables(self):
        """Test that user resolution email template receives all necessary variables"""
        person = Person.objects.create(
            name='解决测试',
            birth_date=date(1990, 1, 1),
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'bazi_analysis': 'Resolution test'},
            bazi_analysis_reported=True,
            bazi_report_category='technical_error',
            bazi_report_timestamp=timezone.now(),
            bazi_report_resolved_at=timezone.now(),
            bazi_report_resolved_by=self.admin
        )
        
        send_user_resolution_notification(
            self.user,
            person,
            'bazi',
            'resolved',
            '我们已经解决了您报告的问题'
        )
        
        email = mail.outbox[0]
        
        expected_content = [
            '测试 用户',  # user name
            '八字分析',  # analysis_type (Chinese)
            '已解决',  # status display
            '我们已经解决了您报告的问题',  # admin_notes
            '解决测试',  # analysis object name
        ]
        
        for content in expected_content:
            self.assertIn(content, email.body)
    
    @override_settings(EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend')
    def test_email_encoding_chinese_characters(self):
        """Test proper encoding of Chinese characters in emails"""
        person = Person.objects.create(
            name='中文测试用户名 🎯',
            birth_date=date(1990, 1, 1),
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'bazi_analysis': '中文分析内容'},
            bazi_report_timestamp=timezone.now(),
            bazi_report_resolved_at=timezone.now()
        )
        
        chinese_message = '这是一个包含中文字符、标点符号！和emoji 🚨 的测试消息。'
        
        send_admin_report_notification(
            person,
            'bazi',
            'inappropriate_content',
            chinese_message
        )
        
        email = mail.outbox[0]
        self.assertIn('中文测试用户名 🎯', email.body)
        self.assertIn(chinese_message, email.body)
        self.assertIn('内容不当', email.body)
    
    @override_settings(EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend')
    def test_email_html_content_safety(self):
        """Test that HTML content is properly handled in emails"""
        person = Person.objects.create(
            name='HTML Test',
            birth_date=date(1990, 1, 1),
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'bazi_analysis': 'HTML test content'},
            bazi_report_timestamp=timezone.now(),
            bazi_report_resolved_at=timezone.now()
        )
        
        html_message = '<script>alert("XSS")</script><b>Bold text</b>'
        
        send_admin_report_notification(
            person,
            'bazi',
            'other',
            html_message
        )
        
        email = mail.outbox[0]
        # HTML should be escaped in the email body
        self.assertIn('&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;&lt;b&gt;Bold text&lt;/b&gt;', email.body)


class EmailEdgeCaseTests(TestCase):
    """Test edge cases and error scenarios for email system"""
    
    def setUp(self):
        """Set up test data"""
        self.user = User.objects.create_user(
            phone='13800138000',
            email='user@example.com'
        )
    
    def tearDown(self):
        """Clean up test data"""
        from tests.test_utils import cleanup_test_permissions
        cleanup_test_permissions()
    
    @patch('api.utils.logger')
    def test_email_logging_on_failure(self, mock_logger):
        """Test that email failures are properly logged"""
        # Create admin user with permission to ensure email is sent
        admin = User.objects.create_user(
            phone='13800138001',
            email='admin@example.com',
            first_name='Admin',
            last_name='User',
            is_staff=True
        )
        
        # Clean up any existing permissions first
        Permission.objects.filter(codename='can_receive_ai_report_emails').delete()
        
        permission = Permission.objects.create(
            codename='can_receive_ai_report_emails',
            name='Can receive AI analysis report emails',
            content_type_id=1
        )
        admin.user_permissions.add(permission)
        
        person = Person.objects.create(
            name='Logging Test',
            birth_date=date(1990, 1, 1),
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'test': 'data'},
            bazi_report_timestamp=timezone.now()
        )
        
        with patch('api.utils.send_mail', side_effect=Exception("SMTP Error")):
            result = send_admin_report_notification(
                person,
                'bazi',
                'technical_error',
                'Logging test'
            )
            
            self.assertFalse(result)
            mock_logger.error.assert_called()
    
    def test_email_with_none_analysis_object(self):
        """Test email sending with None analysis object"""
        result = send_admin_report_notification(
            None,
            'bazi',
            'technical_error',
            'None object test'
        )
        
        # Should handle gracefully and not crash
        self.assertFalse(result)
    
    def test_email_with_missing_attributes(self):
        """Test email sending when analysis object has missing attributes"""
        # Create a mock object with limited attributes
        class MockAnalysis:
            def __init__(self):
                self.id = 1
                self.name = 'Mock Analysis'
                # Missing many expected attributes
        
        mock_obj = MockAnalysis()
        
        # Should handle gracefully without crashing
        try:
            result = send_admin_report_notification(
                mock_obj,
                'bazi',
                'technical_error',
                'Missing attributes test'
            )
            # Result may be True or False, but shouldn't crash
        except Exception as e:
            self.fail(f"Email sending crashed with missing attributes: {e}") 