"""
Comprehensive test suite for multilingual AI analysis functionality.

This test suite covers:

1. **i18n Utility Tests** (TestI18nUtilities):
   - Normal cases: Translation retrieval, language validation, element translation
   - Edge cases: Missing translations, invalid language codes, empty values
   - Outlier cases: Unicode handling, special characters, extreme values

2. **Prompt Template Tests** (TestPromptTemplateLanguage):
   - Normal cases: Language-specific template retrieval, fallback logic
   - Edge cases: Missing templates, inactive templates, multiple languages
   - Outlier cases: Invalid language codes, template conflicts

3. **BaZi Analysis Language Tests** (TestBaziAnalysisLanguage):
   - Normal cases: Chinese and English analysis generation
   - Edge cases: Language fallback, missing translations
   - Outlier cases: Invalid language codes, mixed language content

4. **LiuYao Analysis Language Tests** (TestLiuYaoAnalysisLanguage):
   - Normal cases: Chinese and English analysis generation
   - Edge cases: Language fallback, missing translations
   - Outlier cases: Invalid language codes, mixed language content

5. **API Endpoint Tests** (TestAPILanguageSupport):
   - Normal cases: Language parameter in API requests
   - Edge cases: Missing language parameter, invalid language codes
   - Outlier cases: Language in query params vs body, case sensitivity

Total: Comprehensive coverage of multilingual AI analysis feature.
"""

import json
from unittest.mock import Mock, patch, MagicMock
from django.test import TestCase, Client
from django.contrib.auth import get_user_model
from django.utils import timezone
from datetime import datetime, date, time

from ai.utils.i18n import (
    SUPPORTED_LANGUAGES,
    DEFAULT_LANGUAGE,
    TRANSLATIONS,
    ELEMENT_TRANSLATION,
    get_translation,
    translate_gender,
    translate_element,
    get_text,
    none_text,
    error_text,
    unknown_text,
    join_list,
    is_missing,
    format_luck_description,
    validate_language,
    normalize_language,
)
from ai.utils import get_active_template
from ai.models import PromptTemplate
from bazi.models import Person as BaziPerson


User = get_user_model()


# ============================================================================
# Test i18n Utility Functions
# ============================================================================

class TestI18nUtilities(TestCase):
    """Test i18n utility functions - Normal, Edge, and Outlier cases."""
    
    # Normal Cases
    def test_get_translation_normal(self):
        """Test normal translation retrieval."""
        # Chinese translations
        self.assertEqual(get_translation('gender_male', 'zh-hans'), '男')
        self.assertEqual(get_translation('gender_female', 'zh-hans'), '女')
        self.assertEqual(get_translation('element_wood', 'zh-hans'), '木')
        
        # English translations
        self.assertEqual(get_translation('gender_male', 'en'), 'Male')
        self.assertEqual(get_translation('gender_female', 'en'), 'Female')
        self.assertEqual(get_translation('element_wood', 'en'), 'Wood')
    
    def test_translate_gender_normal(self):
        """Test gender translation - normal cases."""
        # Chinese
        self.assertEqual(translate_gender('M', 'zh-hans'), '男')
        self.assertEqual(translate_gender('F', 'zh-hans'), '女')
        self.assertEqual(translate_gender('N', 'zh-hans'), '不指定')
        
        # English
        self.assertEqual(translate_gender('M', 'en'), 'Male')
        self.assertEqual(translate_gender('F', 'en'), 'Female')
        self.assertEqual(translate_gender('N', 'en'), 'Not Specified')
    
    def test_translate_element_normal(self):
        """Test element translation - normal cases."""
        # Chinese
        self.assertEqual(translate_element('wood', 'zh-hans'), '木')
        self.assertEqual(translate_element('fire', 'zh-hans'), '火')
        self.assertEqual(translate_element('earth', 'zh-hans'), '土')
        self.assertEqual(translate_element('metal', 'zh-hans'), '金')
        self.assertEqual(translate_element('water', 'zh-hans'), '水')
        
        # English
        self.assertEqual(translate_element('wood', 'en'), 'Wood')
        self.assertEqual(translate_element('fire', 'en'), 'Fire')
        self.assertEqual(translate_element('earth', 'en'), 'Earth')
        self.assertEqual(translate_element('metal', 'en'), 'Metal')
        self.assertEqual(translate_element('water', 'en'), 'Water')
    
    def test_validate_language_normal(self):
        """Test language validation - normal cases."""
        self.assertEqual(validate_language('zh-hans'), 'zh-hans')
        self.assertEqual(validate_language('en'), 'en')
        # Note: validate_language is case-sensitive, only exact matches work
        self.assertEqual(validate_language('ZH-HANS'), DEFAULT_LANGUAGE)  # Falls back to default
        self.assertEqual(validate_language('EN'), DEFAULT_LANGUAGE)  # Falls back to default
    
    def test_normalize_language_normal(self):
        """Test language normalization - normal cases."""
        self.assertEqual(normalize_language('zh-hans'), 'zh-hans')
        self.assertEqual(normalize_language('en'), 'en')
        self.assertEqual(normalize_language(None), DEFAULT_LANGUAGE)
        self.assertEqual(normalize_language(''), DEFAULT_LANGUAGE)
    
    def test_join_list_normal(self):
        """Test list joining - normal cases."""
        # Chinese
        self.assertEqual(join_list(['a', 'b', 'c'], 'zh-hans'), 'a, b, c')
        self.assertEqual(join_list(['a'], 'zh-hans'), 'a')
        
        # English
        self.assertEqual(join_list(['a', 'b', 'c'], 'en'), 'a, b, c')
        self.assertEqual(join_list(['a'], 'en'), 'a')
    
    def test_format_luck_description_normal(self):
        """Test luck description formatting - normal cases."""
        # Chinese format
        result = format_luck_description('甲', '子', '10-20', '比肩', '正财', 'zh-hans')
        self.assertIn('甲子', result)
        self.assertIn('Ages 10-20', result)
        self.assertIn('十神', result)
        
        # English format
        result = format_luck_description('甲', '子', '10-20', '比肩', '正财', 'en')
        self.assertIn('甲子', result)
        self.assertIn('Ages 10-20', result)
        self.assertIn('Ten Gods', result)
    
    # Edge Cases
    def test_get_translation_missing_key(self):
        """Test translation with missing key - edge case."""
        # Should return the key itself if not found
        result = get_translation('nonexistent_key', 'zh-hans')
        self.assertEqual(result, 'nonexistent_key')
    
    def test_get_translation_invalid_language(self):
        """Test translation with invalid language - edge case."""
        # Should fallback to default language
        result = get_translation('gender_male', 'invalid-lang')
        self.assertEqual(result, '男')  # Falls back to zh-hans
    
    def test_translate_gender_invalid(self):
        """Test gender translation with invalid gender code - edge case."""
        # Should return "not specified" for invalid codes
        self.assertEqual(translate_gender('X', 'zh-hans'), '不指定')
        self.assertEqual(translate_gender('X', 'en'), 'Not Specified')
        self.assertEqual(translate_gender(None, 'zh-hans'), '不指定')
        self.assertEqual(translate_gender('', 'zh-hans'), '不指定')
    
    def test_translate_element_invalid(self):
        """Test element translation with invalid element - edge case."""
        # Should return the element name itself for invalid elements
        self.assertEqual(translate_element('invalid', 'zh-hans'), 'invalid')
        # Note: translate_element calls .lower() on None, which will raise AttributeError
        # This is expected behavior - None values should be handled before calling this function
        with self.assertRaises(AttributeError):
            translate_element(None, 'zh-hans')
        # Empty string should work
        self.assertEqual(translate_element('', 'zh-hans'), '')
    
    def test_validate_language_edge(self):
        """Test language validation - edge cases."""
        # Invalid languages should fallback to default
        self.assertEqual(validate_language('invalid'), DEFAULT_LANGUAGE)
        self.assertEqual(validate_language('zh'), DEFAULT_LANGUAGE)
        self.assertEqual(validate_language('english'), DEFAULT_LANGUAGE)
        self.assertEqual(validate_language(''), DEFAULT_LANGUAGE)
        self.assertEqual(validate_language(None), DEFAULT_LANGUAGE)
    
    def test_join_list_empty(self):
        """Test list joining with empty list - edge case."""
        # Should return "None" text
        self.assertEqual(join_list([], 'zh-hans'), '无')
        self.assertEqual(join_list([], 'en'), 'None')
        self.assertEqual(join_list(None, 'zh-hans'), '无')
    
    def test_is_missing_edge(self):
        """Test missing value detection - edge cases."""
        # None values
        self.assertTrue(is_missing(None, 'zh-hans'))
        self.assertTrue(is_missing(None, 'en'))
        
        # Empty strings
        self.assertTrue(is_missing('', 'zh-hans'))
        self.assertTrue(is_missing('', 'en'))
        
        # None text
        self.assertTrue(is_missing('无', 'zh-hans'))
        self.assertTrue(is_missing('None', 'en'))
        
        # Valid values
        self.assertFalse(is_missing('test', 'zh-hans'))
        self.assertFalse(is_missing('test', 'en'))
    
    def test_format_luck_description_edge(self):
        """Test luck description formatting - edge cases."""
        # Missing relationships
        result = format_luck_description('甲', '子', '10-20', None, None, 'zh-hans')
        self.assertIn('甲子', result)
        self.assertIn('Ages 10-20', result)
        self.assertNotIn('十神', result)
        
        # Empty relationships
        result = format_luck_description('甲', '子', '10-20', '', '', 'zh-hans')
        self.assertIn('甲子', result)
        self.assertNotIn('十神', result)
    
    # Outlier Cases
    def test_get_translation_unicode(self):
        """Test translation with Unicode content - outlier case."""
        # Should handle Unicode properly
        result = get_translation('gender_male', 'zh-hans')
        self.assertEqual(result, '男')  # Chinese character
        self.assertIsInstance(result, str)
    
    def test_translate_gender_special_codes(self):
        """Test gender translation with special codes - outlier case."""
        # Test various special codes
        special_codes = ['m', 'f', 'MALE', 'FEMALE', '1', '2', '0']
        for code in special_codes:
            result = translate_gender(code, 'zh-hans')
            # Should not crash, may return unknown
            self.assertIsInstance(result, str)
    
    def test_translate_element_case_sensitivity(self):
        """Test element translation case sensitivity - outlier case."""
        # translate_element is case-insensitive (uses .lower())
        # Should handle case variations by converting to lowercase
        self.assertEqual(translate_element('WOOD', 'zh-hans'), '木')  # Case insensitive
        self.assertEqual(translate_element('Wood', 'zh-hans'), '木')
        self.assertEqual(translate_element('wood', 'zh-hans'), '木')  # Lowercase works
    
    def test_validate_language_case_variations(self):
        """Test language validation with case variations - outlier case."""
        # Note: validate_language is case-sensitive, only exact matches work
        # Various case combinations fall back to default
        self.assertEqual(validate_language('ZH-HANS'), DEFAULT_LANGUAGE)
        self.assertEqual(validate_language('Zh-Hans'), DEFAULT_LANGUAGE)
        self.assertEqual(validate_language('zh-HANS'), DEFAULT_LANGUAGE)
        self.assertEqual(validate_language('En'), DEFAULT_LANGUAGE)
        self.assertEqual(validate_language('EN'), DEFAULT_LANGUAGE)
    
    def test_join_list_unicode_items(self):
        """Test list joining with Unicode items - outlier case."""
        unicode_items = ['测试', '中文', '内容']
        result = join_list(unicode_items, 'zh-hans')
        self.assertEqual(result, '测试, 中文, 内容')
        self.assertIsInstance(result, str)
    
    def test_format_luck_description_unicode(self):
        """Test luck description with Unicode content - outlier case."""
        result = format_luck_description('甲', '子', '10-20', '比肩', '正财', 'zh-hans')
        # Should contain Chinese characters
        self.assertIn('甲', result)
        self.assertIn('子', result)
        self.assertIn('十神', result)
    
    def test_get_text_aliases(self):
        """Test text getter aliases - normal cases."""
        # Test that aliases work correctly
        self.assertEqual(get_text('gender_male', 'zh-hans'), get_translation('gender_male', 'zh-hans'))
        self.assertEqual(none_text('zh-hans'), get_translation('none', 'zh-hans'))
        self.assertEqual(error_text('zh-hans'), get_translation('error', 'zh-hans'))
        self.assertEqual(unknown_text('zh-hans'), get_translation('unknown', 'zh-hans'))


# ============================================================================
# Test Prompt Template Language Support
# ============================================================================

class TestPromptTemplateLanguage(TestCase):
    """Test prompt template language support - Normal, Edge, and Outlier cases."""
    
    def setUp(self):
        """Set up test data."""
        # Create test templates
        self.template_zh = PromptTemplate.objects.create(
            divination_type='bazi',
            name='Test Template ZH',
            content='中文模板内容',
            language='zh-hans',
            status='active'
        )
        self.template_en = PromptTemplate.objects.create(
            divination_type='bazi',
            name='Test Template EN',
            content='English template content',
            language='en',
            status='active'
        )
        self.template_draft = PromptTemplate.objects.create(
            divination_type='bazi',
            name='Test Template Draft',
            content='Draft template',
            language='en',
            status='draft'
        )
    
    def tearDown(self):
        """Clean up test data."""
        PromptTemplate.objects.all().delete()
    
    # Normal Cases
    def test_get_active_template_language_normal(self):
        """Test getting active template by language - normal case."""
        # Get Chinese template
        result = get_active_template('bazi', language='zh-hans')
        self.assertEqual(result, '中文模板内容')
        
        # Get English template
        result = get_active_template('bazi', language='en')
        self.assertEqual(result, 'English template content')
    
    def test_get_active_template_default_language(self):
        """Test getting template with default language - normal case."""
        # Should default to Chinese
        result = get_active_template('bazi')
        self.assertEqual(result, '中文模板内容')
    
    # Edge Cases
    def test_get_active_template_missing_language(self):
        """Test getting template when language template doesn't exist - edge case."""
        # Delete English template
        self.template_en.delete()
        
        # Should fallback to Chinese
        result = get_active_template('bazi', language='en')
        self.assertEqual(result, '中文模板内容')
    
    def test_get_active_template_draft_status(self):
        """Test that draft templates are not returned - edge case."""
        # Should not return draft template
        result = get_active_template('bazi', language='en')
        self.assertEqual(result, 'English template content')
        self.assertNotEqual(result, 'Draft template')
    
    def test_get_active_template_no_active_templates(self):
        """Test when no active templates exist - edge case."""
        # Archive all templates
        PromptTemplate.objects.all().update(status='archived')
        
        # Should fallback to file-based template or return None
        result = get_active_template('bazi', language='zh-hans')
        # May return None or file-based fallback
        self.assertIsInstance(result, (str, type(None)))
    
    def test_get_active_template_multiple_languages(self):
        """Test template retrieval with multiple languages - edge case."""
        # Create templates for different divination types
        liuyao_template = PromptTemplate.objects.create(
            divination_type='liuyao',
            name='LiuYao Template',
            content='LiuYao content',
            language='zh-hans',
            status='active'
        )
        
        # Should get correct template for each type
        bazi_result = get_active_template('bazi', language='zh-hans')
        liuyao_result = get_active_template('liuyao', language='zh-hans')
        
        self.assertEqual(bazi_result, '中文模板内容')
        self.assertEqual(liuyao_result, 'LiuYao content')
    
    # Outlier Cases
    def test_get_active_template_invalid_language(self):
        """Test template retrieval with invalid language - outlier case."""
        # Should fallback to default language
        result = get_active_template('bazi', language='invalid-lang')
        self.assertEqual(result, '中文模板内容')
    
    def test_get_active_template_case_insensitive(self):
        """Test template retrieval with case variations - outlier case."""
        # Language codes should be normalized
        result1 = get_active_template('bazi', language='ZH-HANS')
        result2 = get_active_template('bazi', language='zh-hans')
        self.assertEqual(result1, result2)
    
    def test_get_active_template_unique_constraint(self):
        """Test unique constraint on active templates - normal case."""
        # Should only have one active template per language
        active_count = PromptTemplate.objects.filter(
            divination_type='bazi',
            language='zh-hans',
            status='active'
        ).count()
        self.assertEqual(active_count, 1)


# ============================================================================
# Test BaZi Analysis Language Support
# ============================================================================

class TestBaziAnalysisLanguage(TestCase):
    """Test BaZi analysis language support - Normal, Edge, and Outlier cases."""
    
    def setUp(self):
        """Set up test data."""
        self.user = User.objects.create_user(
            phone='1234567890',
            email='test@example.com',
            password='testpass123'
        )
        self.person = BaziPerson.objects.create(
            name='Test Person',
            gender='M',
            birth_date=date(1990, 1, 1),
            birth_time=time(12, 0),
            created_by=self.user
        )
    
    def tearDown(self):
        """Clean up test data."""
        BaziPerson.objects.all().delete()
        User.objects.all().delete()
    
    # Normal Cases
    @patch('ai.utils.bazi.get_active_template')
    @patch('ai.utils.bazi.LLMServiceFactory')
    @patch('ai.utils.config.get_ai_config')
    def test_analyze_bazi_chinese(self, mock_get_ai_config, mock_factory, mock_get_template):
        """Test BaZi analysis in Chinese - normal case."""
        from ai.utils.bazi import analyze_bazi
        
        # Setup mocks
        mock_get_template.return_value = "中文提示模板"
        mock_service = MagicMock()
        mock_service.get_completion.return_value = '{"analysis": "中文分析结果"}'
        mock_service.model = "test-model"
        mock_factory.get_service.return_value = mock_service
        mock_get_ai_config.return_value = {'provider': 'test-provider', 'model': 'test-model'}
        
        # Run analysis in Chinese
        result = analyze_bazi(self.person, language='zh-hans')
        
        # Verify results
        self.assertIsNotNone(result)
        self.assertEqual(result['language'], 'zh-hans')
        self.assertIn('bazi_analysis', result)
    
    @patch('ai.utils.bazi.get_active_template')
    @patch('ai.utils.bazi.LLMServiceFactory')
    @patch('ai.utils.config.get_ai_config')
    def test_analyze_bazi_english(self, mock_get_ai_config, mock_factory, mock_get_template):
        """Test BaZi analysis in English - normal case."""
        from ai.utils.bazi import analyze_bazi
        
        # Setup mocks
        mock_get_template.return_value = "English prompt template"
        mock_service = MagicMock()
        mock_service.get_completion.return_value = '{"analysis": "English analysis result"}'
        mock_service.model = "test-model"
        mock_factory.get_service.return_value = mock_service
        mock_get_ai_config.return_value = {'provider': 'test-provider', 'model': 'test-model'}
        
        # Run analysis in English
        result = analyze_bazi(self.person, language='en')
        
        # Verify results
        self.assertIsNotNone(result)
        self.assertEqual(result['language'], 'en')
        self.assertIn('bazi_analysis', result)
    
    @patch('ai.utils.bazi.get_active_template')
    def test_prepare_bazi_prompt_language(self, mock_template):
        """Test BaZi prompt preparation with language parameter."""
        from ai.utils.bazi import prepare_bazi_prompt
        
        # Mock template
        mock_template.return_value = "Template with {{name}}"
        
        # Test Chinese
        result_zh = prepare_bazi_prompt(self.person, language='zh-hans')
        self.assertIn('Test Person', result_zh)
        
        # Test English
        result_en = prepare_bazi_prompt(self.person, language='en')
        self.assertIn('Test Person', result_en)
    
    # Edge Cases
    @patch('ai.utils.bazi.get_active_template')
    @patch('ai.utils.bazi.LLMServiceFactory')
    @patch('ai.utils.config.get_ai_config')
    def test_analyze_bazi_language_fallback(self, mock_get_ai_config, mock_factory, mock_get_template):
        """Test BaZi analysis with language fallback - edge case."""
        from ai.utils.bazi import analyze_bazi
        
        # Setup mocks - no English template, should fallback to Chinese
        mock_get_template.return_value = "中文模板"  # Only Chinese available
        mock_service = MagicMock()
        mock_service.get_completion.return_value = '{"analysis": "分析结果"}'
        mock_service.model = "test-model"
        mock_factory.get_service.return_value = mock_service
        mock_get_ai_config.return_value = {'provider': 'test-provider', 'model': 'test-model'}
        
        # Request English but should fallback to Chinese
        result = analyze_bazi(self.person, language='en')
        
        # Should still work with fallback
        self.assertIsNotNone(result)
        self.assertEqual(result['language'], 'en')  # Language requested is still recorded
    
    @patch('ai.utils.bazi.get_active_template')
    def test_prepare_bazi_prompt_invalid_language(self, mock_get_template):
        """Test BaZi prompt preparation with invalid language - edge case."""
        from ai.utils.bazi import prepare_bazi_prompt
        
        mock_get_template.return_value = "Template"
        
        # Invalid language should fallback to default
        result = prepare_bazi_prompt(self.person, language='invalid')
        self.assertIsNotNone(result)
    
    # Outlier Cases
    @patch('ai.utils.bazi.get_active_template')
    @patch('ai.utils.bazi.LLMServiceFactory')
    @patch('ai.utils.config.get_ai_config')
    def test_analyze_bazi_case_insensitive_language(self, mock_get_ai_config, mock_factory, mock_get_template):
        """Test BaZi analysis with case-insensitive language - outlier case."""
        from ai.utils.bazi import analyze_bazi
        
        mock_get_template.return_value = "Template"
        mock_service = MagicMock()
        mock_service.get_completion.return_value = '{"analysis": "Result"}'
        mock_service.model = "test-model"
        mock_factory.get_service.return_value = mock_service
        mock_get_ai_config.return_value = {'provider': 'test-provider', 'model': 'test-model'}
        
        # Test various case combinations
        for lang in ['EN', 'En', 'en', 'ZH-HANS', 'Zh-Hans', 'zh-hans']:
            result = analyze_bazi(self.person, language=lang)
            self.assertIsNotNone(result)
            # Language should be normalized
            self.assertIn(result['language'], ['zh-hans', 'en'])


# ============================================================================
# Test LiuYao Analysis Language Support
# ============================================================================

class TestLiuYaoAnalysisLanguage(TestCase):
    """Test LiuYao analysis language support - Normal, Edge, and Outlier cases."""
    
    def setUp(self):
        """Set up test data."""
        from liuyao.models import liuyao
        
        self.user = User.objects.create_user(
            phone='1234567890',
            email='test@example.com',
            password='testpass123'
        )
        self.liuyao = liuyao.objects.create(
            question='测试问题',
            qdate=timezone.now(),
            y1='111',
            y2='0',
            y3='111',
            y4='1',
            y5='111',
            y6='111',
            user=self.user
        )
    
    def tearDown(self):
        """Clean up test data."""
        from liuyao.models import liuyao
        liuyao.objects.all().delete()
        User.objects.all().delete()
    
    # Normal Cases
    @patch('ai.utils.liuyao_analysis.prepare_liuyao_prompt')
    @patch('ai.utils.liuyao_analysis.LLMServiceFactory')
    @patch('ai.utils.config.get_ai_config')
    def test_analyze_liuyao_chinese(self, mock_get_ai_config, mock_factory, mock_prepare_prompt):
        """Test LiuYao analysis in Chinese - normal case."""
        from ai.utils.liuyao_analysis import analyze_liuyao
        
        # Setup mocks
        mock_prepare_prompt.return_value = "中文提示模板"
        mock_service = MagicMock()
        mock_service.get_completion.return_value = '{"think": "思考", "analysis": "中文分析结果"}'
        mock_service.model = "test-model"
        mock_factory.get_service.return_value = mock_service
        mock_get_ai_config.return_value = {'provider': 'test-provider', 'model': 'test-model'}
        
        # Run analysis in Chinese
        result = analyze_liuyao(self.liuyao, language='zh-hans')
        
        # Verify results
        self.assertIsNotNone(result)
        self.assertEqual(result['language'], 'zh-hans')
        self.assertIn('liuyao_analysis', result)
    
    @patch('ai.utils.liuyao_analysis.prepare_liuyao_prompt')
    @patch('ai.utils.liuyao_analysis.LLMServiceFactory')
    @patch('ai.utils.config.get_ai_config')
    def test_analyze_liuyao_english(self, mock_get_ai_config, mock_factory, mock_prepare_prompt):
        """Test LiuYao analysis in English - normal case."""
        from ai.utils.liuyao_analysis import analyze_liuyao
        
        # Setup mocks
        mock_prepare_prompt.return_value = "English prompt template"
        mock_service = MagicMock()
        mock_service.get_completion.return_value = '{"think": "Thinking", "analysis": "English analysis result"}'
        mock_service.model = "test-model"
        mock_factory.get_service.return_value = mock_service
        mock_get_ai_config.return_value = {'provider': 'test-provider', 'model': 'test-model'}
        
        # Run analysis in English
        result = analyze_liuyao(self.liuyao, language='en')
        
        # Verify results
        self.assertIsNotNone(result)
        self.assertEqual(result['language'], 'en')
        self.assertIn('liuyao_analysis', result)
    
    # Edge Cases
    @patch('ai.utils.liuyao_analysis.prepare_liuyao_prompt')
    @patch('ai.utils.liuyao_analysis.LLMServiceFactory')
    @patch('ai.utils.config.get_ai_config')
    def test_analyze_liuyao_language_fallback(self, mock_get_ai_config, mock_factory, mock_prepare_prompt):
        """Test LiuYao analysis with language fallback - edge case."""
        from ai.utils.liuyao_analysis import analyze_liuyao
        
        # Setup mocks - no English template, should fallback to Chinese
        mock_prepare_prompt.return_value = "中文模板"
        mock_service = MagicMock()
        mock_service.get_completion.return_value = '{"think": "思考", "analysis": "分析结果"}'
        mock_service.model = "test-model"
        mock_factory.get_service.return_value = mock_service
        mock_get_ai_config.return_value = {'provider': 'test-provider', 'model': 'test-model'}
        
        # Request English but should fallback to Chinese
        result = analyze_liuyao(self.liuyao, language='en')
        
        # Should still work with fallback
        self.assertIsNotNone(result)
        self.assertEqual(result['language'], 'en')  # Language requested is still recorded
    
    # Outlier Cases
    @patch('ai.utils.liuyao_analysis.prepare_liuyao_prompt')
    @patch('ai.utils.liuyao_analysis.LLMServiceFactory')
    @patch('ai.utils.config.get_ai_config')
    def test_analyze_liuyao_case_insensitive_language(self, mock_get_ai_config, mock_factory, mock_prepare_prompt):
        """Test LiuYao analysis with case-insensitive language - outlier case."""
        from ai.utils.liuyao_analysis import analyze_liuyao
        
        mock_prepare_prompt.return_value = "Template"
        mock_service = MagicMock()
        mock_service.get_completion.return_value = '{"think": "Thinking", "analysis": "Result"}'
        mock_service.model = "test-model"
        mock_factory.get_service.return_value = mock_service
        mock_get_ai_config.return_value = {'provider': 'test-provider', 'model': 'test-model'}
        
        # Test various case combinations
        for lang in ['EN', 'En', 'en', 'ZH-HANS', 'Zh-Hans', 'zh-hans']:
            result = analyze_liuyao(self.liuyao, language=lang)
            self.assertIsNotNone(result)
            # Language should be normalized
            self.assertIn(result['language'], ['zh-hans', 'en'])


# ============================================================================
# Test API Endpoint Language Support
# ============================================================================

class TestAPILanguageSupport(TestCase):
    """Test API endpoint language support - Normal, Edge, and Outlier cases."""
    
    def setUp(self):
        """Set up test data."""
        self.client = Client()
        self.user = User.objects.create_user(
            phone='1234567890',
            email='test@example.com',
            password='testpass123'
        )
        self.client.force_login(self.user)
        
        # Create test person
        self.person = BaziPerson.objects.create(
            name='Test Person',
            gender='M',
            birth_date=date(1990, 1, 1),
            birth_time=time(12, 0),
            created_by=self.user
        )
    
    def tearDown(self):
        """Clean up test data."""
        BaziPerson.objects.all().delete()
        User.objects.all().delete()
    
    # Normal Cases
    @patch('ai.utils.bazi.analyze_bazi')
    def test_bazi_analysis_api_with_language(self, mock_analyze):
        """Test BaZi analysis API with language parameter - normal case."""
        # Mock the analysis function
        mock_analyze.return_value = {
            'bazi_analysis': 'Test analysis',
            'language': 'en',
            'provider': 'test-provider',
            'model': 'test-model'
        }
        
        # Make API request with language parameter
        response = self.client.post(
            f'/api/bazi/bazi/{self.person.id}/analysis/',
            data={'language': 'en'},
            content_type='application/json'
        )
        
        # Verify response - API returns 201 for successful creation
        self.assertIn(response.status_code, [200, 201])
        # Check that language was passed to analyze function
        mock_analyze.assert_called_once()
        call_kwargs = mock_analyze.call_args[1]
        self.assertEqual(call_kwargs.get('language'), 'en')
    
    @patch('ai.utils.liuyao_analysis.analyze_liuyao')
    def test_liuyao_analysis_api_with_language(self, mock_analyze):
        """Test LiuYao analysis API with language parameter - normal case."""
        from liuyao.models import liuyao
        
        liuyao_obj = liuyao.objects.create(
            question='Test question',
            qdate=timezone.now(),
            y1='111',
            y2='0',
            y3='111',
            y4='1',
            y5='111',
            y6='111',
            user=self.user
        )
        
        # Mock the analysis function
        mock_analyze.return_value = {
            'liuyao_analysis': 'Test analysis',
            'language': 'en',
            'provider': 'test-provider',
            'model': 'test-model'
        }
        
        # Make API request with language parameter
        response = self.client.post(
            f'/api/liuyao/{liuyao_obj.id}/analysis/',
            data={'language': 'en'},
            content_type='application/json'
        )
        
        # Verify response - API returns 201 for successful creation
        self.assertIn(response.status_code, [200, 201])
        # Check that language was passed to analyze function
        mock_analyze.assert_called_once()
        call_kwargs = mock_analyze.call_args[1]
        self.assertEqual(call_kwargs.get('language'), 'en')
    
    # Edge Cases
    @patch('ai.utils.bazi.analyze_bazi')
    def test_bazi_analysis_api_default_language(self, mock_analyze):
        """Test BaZi analysis API without language parameter - edge case."""
        # Mock the analysis function
        mock_analyze.return_value = {
            'bazi_analysis': 'Test analysis',
            'language': 'zh-hans',
            'provider': 'test-provider',
            'model': 'test-model'
        }
        
        # Make API request without language parameter
        response = self.client.post(
            f'/api/bazi/bazi/{self.person.id}/analysis/',
            data={},
            content_type='application/json'
        )
        
        # Verify response - should default to Chinese (API returns 201 for successful creation)
        self.assertIn(response.status_code, [200, 201])
        call_kwargs = mock_analyze.call_args[1]
        self.assertEqual(call_kwargs.get('language'), 'zh-hans')
    
    @patch('ai.utils.bazi.analyze_bazi')
    def test_bazi_analysis_api_invalid_language(self, mock_analyze):
        """Test BaZi analysis API with invalid language - edge case."""
        # Mock the analysis function
        mock_analyze.return_value = {
            'bazi_analysis': 'Test analysis',
            'language': 'zh-hans',
            'provider': 'test-provider',
            'model': 'test-model'
        }
        
        # Make API request with invalid language
        response = self.client.post(
            f'/api/bazi/bazi/{self.person.id}/analysis/',
            data={'language': 'invalid-lang'},
            content_type='application/json'
        )
        
        # Should still work, fallback to default (API returns 201 for successful creation)
        self.assertIn(response.status_code, [200, 201])
        call_kwargs = mock_analyze.call_args[1]
        # Should be normalized to default
        self.assertIn(call_kwargs.get('language'), ['zh-hans', 'en'])
    
    # Outlier Cases
    @patch('ai.utils.bazi.analyze_bazi')
    def test_bazi_analysis_api_case_insensitive_language(self, mock_analyze):
        """Test BaZi analysis API with case variations - outlier case."""
        # Mock the analysis function
        mock_analyze.return_value = {
            'bazi_analysis': 'Test analysis',
            'language': 'en',
            'provider': 'test-provider',
            'model': 'test-model'
        }
        
        # Test various case combinations
        for lang in ['EN', 'En', 'en']:
            response = self.client.post(
                f'/api/bazi/bazi/{self.person.id}/analysis/',
                data={'language': lang},
                content_type='application/json'
            )
            self.assertIn(response.status_code, [200, 201])  # API returns 201 for successful creation
            call_kwargs = mock_analyze.call_args[1]
            # Should be normalized
            self.assertIn(call_kwargs.get('language'), ['zh-hans', 'en'])


# ============================================================================
# Test Integration Scenarios
# ============================================================================

class TestMultilingualIntegration(TestCase):
    """Test integration scenarios for multilingual analysis."""
    
    def setUp(self):
        """Set up test data."""
        self.user = User.objects.create_user(
            phone='1234567890',
            email='test@example.com',
            password='testpass123'
        )
        self.person = BaziPerson.objects.create(
            name='Test Person',
            gender='M',
            birth_date=date(1990, 1, 1),
            birth_time=time(12, 0),
            created_by=self.user
        )
        
        # Create templates for both languages
        self.template_zh = PromptTemplate.objects.create(
            divination_type='bazi',
            name='Test Template ZH',
            content='中文模板: {{name}}',
            language='zh-hans',
            status='active'
        )
        self.template_en = PromptTemplate.objects.create(
            divination_type='bazi',
            name='Test Template EN',
            content='English template: {{name}}',
            language='en',
            status='active'
        )
    
    def tearDown(self):
        """Clean up test data."""
        BaziPerson.objects.all().delete()
        PromptTemplate.objects.all().delete()
        User.objects.all().delete()
    
    def test_end_to_end_chinese_analysis(self):
        """Test end-to-end Chinese analysis flow."""
        from ai.utils.bazi import prepare_bazi_prompt
        
        # Prepare prompt in Chinese
        prompt = prepare_bazi_prompt(self.person, language='zh-hans')
        
        # Verify Chinese content
        self.assertIn('Test Person', prompt)
        # Should use Chinese template
        self.assertIn('中文模板', prompt)
    
    def test_end_to_end_english_analysis(self):
        """Test end-to-end English analysis flow."""
        from ai.utils.bazi import prepare_bazi_prompt
        
        # Prepare prompt in English
        prompt = prepare_bazi_prompt(self.person, language='en')
        
        # Verify English content
        self.assertIn('Test Person', prompt)
        # Should use English template
        self.assertIn('English template', prompt)
    
    def test_language_consistency(self):
        """Test that language is consistent throughout analysis."""
        from ai.utils.bazi import prepare_bazi_prompt
        
        # Test that language parameter affects all translations
        prompt_zh = prepare_bazi_prompt(self.person, language='zh-hans')
        prompt_en = prepare_bazi_prompt(self.person, language='en')
        
        # Prompts should be different
        self.assertNotEqual(prompt_zh, prompt_en)
        
        # Chinese prompt should contain Chinese characters
        self.assertIn('中文', prompt_zh)
        
        # English prompt should contain English text
        self.assertIn('English', prompt_en)

