"""
Comprehensive unit tests for AI analysis reporting model functionality.

Tests cover:
- Person model BaZi and Number analysis reporting
- LiuYao model analysis reporting
- Model validation and edge cases
- Report state transitions
"""

from django.test import TestCase
from django.contrib.auth import get_user_model
from django.utils import timezone
from datetime import date, time
from bazi.models import Person
from liuyao.models import liuyao

User = get_user_model()


class PersonReportingModelTests(TestCase):
    """Test Person model reporting functionality"""
    
    def setUp(self):
        """Set up test data"""
        self.user = User.objects.create_user(
            phone='13800138000',
            email='test@example.com',
            first_name='Test',
            last_name='User'
        )
        
        self.admin_user = 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),
            birth_time=time(12, 0),
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'bazi_analysis': 'Test analysis content'},
            number_analysis_status='completed',
            number_ai_analysis={'number_analysis': 'Test number analysis'}
        )
    
    # BaZi Analysis Reporting Tests
    
    def test_can_report_bazi_analysis_normal_case(self):
        """Test that completed BaZi analysis can be reported"""
        self.assertTrue(self.person.can_report_bazi_analysis())
    
    def test_can_report_bazi_analysis_no_analysis(self):
        """Test that analysis without content cannot be reported"""
        self.person.ai_analysis = None
        self.assertFalse(self.person.can_report_bazi_analysis())
    
    def test_can_report_bazi_analysis_not_completed(self):
        """Test that non-completed analysis cannot be reported"""
        self.person.analysis_status = 'pending'
        self.assertFalse(self.person.can_report_bazi_analysis())
    
    def test_can_report_bazi_analysis_already_reported(self):
        """Test that already reported analysis cannot be reported again"""
        self.person.bazi_analysis_reported = True
        self.assertFalse(self.person.can_report_bazi_analysis())
    
    def test_report_bazi_analysis_success(self):
        """Test successful BaZi analysis reporting"""
        result = self.person.report_bazi_analysis(
            category='inappropriate_content',
            user_message='Test report message',
            reported_by=self.user
        )
        
        self.assertTrue(result)
        self.assertTrue(self.person.bazi_analysis_reported)
        self.assertEqual(self.person.bazi_report_category, 'inappropriate_content')
        self.assertEqual(self.person.bazi_report_message, 'Test report message')
        self.assertEqual(self.person.bazi_report_status, 'pending')
        self.assertIsNotNone(self.person.bazi_report_timestamp)
    
    def test_report_bazi_analysis_invalid_category(self):
        """Test reporting with invalid category (model accepts it, validation happens at API level)"""
        result = self.person.report_bazi_analysis(
            category='invalid_category',
            user_message='Test message'
        )
        self.assertTrue(result)
        self.assertEqual(self.person.bazi_report_category, 'invalid_category')
    
    def test_report_bazi_analysis_cannot_report(self):
        """Test reporting when analysis cannot be reported"""
        self.person.bazi_analysis_reported = True
        result = self.person.report_bazi_analysis(
            category='inappropriate_content',
            user_message='Test message'
        )
        self.assertFalse(result)
    
    def test_report_bazi_analysis_empty_message(self):
        """Test reporting with empty message (should be allowed)"""
        result = self.person.report_bazi_analysis(
            category='technical_error',
            user_message='',
            reported_by=self.user
        )
        
        self.assertTrue(result)
        self.assertEqual(self.person.bazi_report_message, '')
    
    def test_report_bazi_analysis_long_message(self):
        """Test reporting with very long message"""
        long_message = 'A' * 1500  # Longer than max_length=1000
        result = self.person.report_bazi_analysis(
            category='other',
            user_message=long_message,
            reported_by=self.user
        )
        
        self.assertTrue(result)
        # Message should be truncated or handled by model validation
    
    # Number Analysis Reporting Tests
    
    def test_can_report_number_analysis_normal_case(self):
        """Test that completed Number analysis can be reported"""
        self.assertTrue(self.person.can_report_number_analysis())
    
    def test_can_report_number_analysis_no_analysis(self):
        """Test that analysis without content cannot be reported"""
        self.person.number_ai_analysis = None
        self.assertFalse(self.person.can_report_number_analysis())
    
    def test_can_report_number_analysis_not_completed(self):
        """Test that non-completed analysis cannot be reported"""
        self.person.number_analysis_status = 'error'
        self.assertFalse(self.person.can_report_number_analysis())
    
    def test_report_number_analysis_success(self):
        """Test successful Number analysis reporting"""
        result = self.person.report_number_analysis(
            category='inaccurate_analysis',
            user_message='Number analysis is wrong',
            reported_by=self.user
        )
        
        self.assertTrue(result)
        self.assertTrue(self.person.number_analysis_reported)
        self.assertEqual(self.person.number_report_category, 'inaccurate_analysis')
        self.assertEqual(self.person.number_report_message, 'Number analysis is wrong')
        self.assertEqual(self.person.number_report_status, 'pending')
    
    # Reset Analysis Tests
    
    def test_reset_bazi_analysis_clears_all_data(self):
        """Test that reset clears all BaZi analysis and reporting data"""
        # First report the analysis
        self.person.report_bazi_analysis(
            category='inappropriate_content',
            user_message='Test report'
        )
        
        # Then reset
        self.person.reset_bazi_analysis()
        
        # Check all fields are cleared
        self.assertIsNone(self.person.ai_analysis)
        self.assertIsNone(self.person.analysis_timestamp)
        self.assertIsNone(self.person.analysis_status)
        self.assertFalse(self.person.bazi_analysis_reported)
        self.assertIsNone(self.person.bazi_report_category)
        self.assertIsNone(self.person.bazi_report_message)
        self.assertIsNone(self.person.bazi_report_timestamp)
        self.assertIsNone(self.person.bazi_report_status)
        self.assertIsNone(self.person.bazi_report_admin_notes)
        self.assertIsNone(self.person.bazi_report_resolved_by)
        self.assertIsNone(self.person.bazi_report_resolved_at)
    
    def test_reset_number_analysis_clears_all_data(self):
        """Test that reset clears all Number analysis and reporting data"""
        # First report the analysis
        self.person.report_number_analysis(
            category='technical_error',
            user_message='System error'
        )
        
        # Then reset
        self.person.reset_number_analysis()
        
        # Check all fields are cleared
        self.assertIsNone(self.person.number_ai_analysis)
        self.assertIsNone(self.person.number_analysis_timestamp)
        self.assertIsNone(self.person.number_analysis_status)
        self.assertFalse(self.person.number_analysis_reported)
        self.assertIsNone(self.person.number_report_category)
        self.assertIsNone(self.person.number_report_message)
    
    # Edge Cases
    
    def test_multiple_report_attempts(self):
        """Test that multiple report attempts on same analysis fail after first"""
        # First report should succeed
        result1 = self.person.report_bazi_analysis(
            category='inappropriate_content',
            user_message='First report'
        )
        self.assertTrue(result1)
        
        # Second report should fail
        result2 = self.person.report_bazi_analysis(
            category='offensive_language',
            user_message='Second report'
        )
        self.assertFalse(result2)
        
        # Original report data should remain unchanged
        self.assertEqual(self.person.bazi_report_category, 'inappropriate_content')
        self.assertEqual(self.person.bazi_report_message, 'First report')
    
    def test_report_with_none_values(self):
        """Test reporting with None values"""
        result = self.person.report_bazi_analysis(
            category='other',
            user_message=None,
            reported_by=None
        )
        
        self.assertTrue(result)
        self.assertIsNone(self.person.bazi_report_message)
    
    def test_choice_field_validation(self):
        """Test that report category choices are validated"""
        # Valid categories should work
        valid_categories = ['inappropriate_content', 'inaccurate_analysis', 
                          'offensive_language', 'technical_error', 'other']
        
        for category in valid_categories:
            person = Person.objects.create(
                name=f'Test {category}',
                birth_date=date(1990, 1, 1),
                created_by=self.user,
                analysis_status='completed',
                ai_analysis={'test': 'data'}
            )
            
            result = person.report_bazi_analysis(category=category)
            self.assertTrue(result)
            self.assertEqual(person.bazi_report_category, category)


class LiuYaoReportingModelTests(TestCase):
    """Test LiuYao model reporting functionality"""
    
    def setUp(self):
        """Set up test data"""
        self.user = User.objects.create_user(
            phone='13800138000',
            email='test@example.com',
            first_name='Test',
            last_name='User'
        )
        
        self.liuyao_entry = liuyao.objects.create(
            question='Test question for divination',
            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 content'}
        )
    
    def test_can_report_analysis_normal_case(self):
        """Test that completed LiuYao analysis can be reported"""
        self.assertTrue(self.liuyao_entry.can_report_analysis())
    
    def test_can_report_analysis_no_analysis(self):
        """Test that analysis without content cannot be reported"""
        self.liuyao_entry.ai_analysis = None
        self.assertFalse(self.liuyao_entry.can_report_analysis())
    
    def test_can_report_analysis_not_completed(self):
        """Test that non-completed analysis cannot be reported"""
        self.liuyao_entry.analysis_status = 'pending'
        self.assertFalse(self.liuyao_entry.can_report_analysis())
    
    def test_can_report_analysis_already_reported(self):
        """Test that already reported analysis cannot be reported again"""
        self.liuyao_entry.analysis_reported = True
        self.assertFalse(self.liuyao_entry.can_report_analysis())
    
    def test_report_analysis_success(self):
        """Test successful LiuYao analysis reporting"""
        result = self.liuyao_entry.report_analysis(
            category='inaccurate_analysis',
            user_message='The hexagram interpretation is incorrect',
            reported_by=self.user
        )
        
        self.assertTrue(result)
        self.assertTrue(self.liuyao_entry.analysis_reported)
        self.assertEqual(self.liuyao_entry.report_category, 'inaccurate_analysis')
        self.assertEqual(self.liuyao_entry.report_message, 'The hexagram interpretation is incorrect')
        self.assertEqual(self.liuyao_entry.report_status, 'pending')
        self.assertIsNotNone(self.liuyao_entry.report_timestamp)
    
    def test_report_analysis_cannot_report(self):
        """Test reporting when analysis cannot be reported"""
        self.liuyao_entry.analysis_reported = True
        result = self.liuyao_entry.report_analysis(
            category='inappropriate_content',
            user_message='Test message'
        )
        self.assertFalse(result)
    
    def test_reset_analysis_clears_all_data(self):
        """Test that reset clears all LiuYao analysis and reporting data"""
        # First report the analysis
        self.liuyao_entry.report_analysis(
            category='offensive_language',
            user_message='Offensive content'
        )
        
        # Then reset
        self.liuyao_entry.reset_analysis()
        
        # Check all fields are cleared
        self.assertIsNone(self.liuyao_entry.ai_analysis)
        self.assertIsNone(self.liuyao_entry.analysis_timestamp)
        self.assertIsNone(self.liuyao_entry.analysis_status)
        self.assertFalse(self.liuyao_entry.analysis_reported)
        self.assertIsNone(self.liuyao_entry.report_category)
        self.assertIsNone(self.liuyao_entry.report_message)
        self.assertIsNone(self.liuyao_entry.report_timestamp)
        self.assertIsNone(self.liuyao_entry.report_status)
        self.assertIsNone(self.liuyao_entry.report_admin_notes)
        self.assertIsNone(self.liuyao_entry.report_resolved_by)
        self.assertIsNone(self.liuyao_entry.report_resolved_at)
    
    # Edge Cases for LiuYao
    
    def test_report_with_special_characters(self):
        """Test reporting with special characters in message"""
        special_message = "This contains 特殊字符 and émojis 🤔 and symbols !@#$%^&*()"
        result = self.liuyao_entry.report_analysis(
            category='other',
            user_message=special_message
        )
        
        self.assertTrue(result)
        self.assertEqual(self.liuyao_entry.report_message, special_message)
    
    def test_report_with_empty_question(self):
        """Test reporting analysis for entry with empty question"""
        self.liuyao_entry.question = ''
        result = self.liuyao_entry.report_analysis(
            category='technical_error',
            user_message='Empty question issue'
        )
        
        self.assertTrue(result)
    
    def test_multiple_liuyao_reports_same_user(self):
        """Test that same user can report multiple different LiuYao analyses"""
        # Create second LiuYao entry
        liuyao2 = liuyao.objects.create(
            question='Second question',
            user=self.user,
            qdate=timezone.now(),
            y1='0', y2='1', y3='0', y4='1', y5='0', y6='1',
            analysis_status='completed',
            ai_analysis={'response': 'Second analysis'}
        )
        
        # Report both
        result1 = self.liuyao_entry.report_analysis(category='inappropriate_content')
        result2 = liuyao2.report_analysis(category='inaccurate_analysis')
        
        self.assertTrue(result1)
        self.assertTrue(result2)
        self.assertTrue(self.liuyao_entry.analysis_reported)
        self.assertTrue(liuyao2.analysis_reported)


class ModelReportingEdgeCaseTests(TestCase):
    """Test edge cases and outlier scenarios for reporting models"""
    
    def setUp(self):
        """Set up test data for edge cases"""
        self.user = User.objects.create_user(
            phone='13800138000',
            email='test@example.com'
        )
    
    def test_person_without_user(self):
        """Test Person reporting when created_by is None"""
        person = Person.objects.create(
            name='Orphan Person',
            birth_date=date(1990, 1, 1),
            created_by=None,  # No user
            analysis_status='completed',
            ai_analysis={'test': 'data'}
        )
        
        result = person.report_bazi_analysis(category='other')
        self.assertTrue(result)  # Should still work
    
    def test_liuyao_without_user(self):
        """Test LiuYao reporting when user is None"""
        entry = liuyao.objects.create(
            question='Guest question',
            user=None,  # No user
            qdate=timezone.now(),
            y1='1', y2='0', y3='1', y4='0', y5='1', y6='0',
            analysis_status='completed',
            ai_analysis={'response': 'Guest analysis'}
        )
        
        result = entry.report_analysis(category='technical_error')
        self.assertTrue(result)  # Should still work
    
    def test_concurrent_report_attempts(self):
        """Test concurrent reporting attempts (race condition simulation)"""
        person = Person.objects.create(
            name='Concurrent Test',
            birth_date=date(1990, 1, 1),
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'test': 'data'}
        )
        
        # Simulate concurrent access by manually setting reported flag
        person.bazi_analysis_reported = True
        person.save()
        
        # Attempt to report should fail
        result = person.report_bazi_analysis(category='inappropriate_content')
        self.assertFalse(result)
    
    def test_malformed_analysis_data(self):
        """Test reporting with malformed AI analysis data"""
        person = Person.objects.create(
            name='Malformed Data Test',
            birth_date=date(1990, 1, 1),
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'invalid': 'structure', 'missing': 'required_fields'}
        )
        
        # Should still be able to report even with malformed data
        result = person.report_bazi_analysis(category='technical_error')
        self.assertTrue(result)
    
    def test_unicode_and_emoji_handling(self):
        """Test handling of Unicode characters and emojis in reports"""
        person = Person.objects.create(
            name='测试用户 👤',
            birth_date=date(1990, 1, 1),
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'test': '数据 📊'}
        )
        
        unicode_message = "报告内容包含中文、English、🔥emojis🔥、和symbols!@#$%^&*()"
        result = person.report_bazi_analysis(
            category='other',
            user_message=unicode_message
        )
        
        self.assertTrue(result)
        self.assertEqual(person.bazi_report_message, unicode_message)
    
    def test_extreme_date_values(self):
        """Test reporting with extreme date values"""
        # Test with very old date
        old_person = Person.objects.create(
            name='Ancient Person',
            birth_date=date(1900, 1, 1),
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'test': 'old data'}
        )
        
        result = old_person.report_bazi_analysis(category='inaccurate_analysis')
        self.assertTrue(result)
        
        # Test with future date (edge case)
        future_person = Person.objects.create(
            name='Future Person',
            birth_date=date(2030, 12, 31),
            created_by=self.user,
            analysis_status='completed',
            ai_analysis={'test': 'future data'}
        )
        
        result = future_person.report_bazi_analysis(category='other')
        self.assertTrue(result) 