"""
Test cases for LiuYao prompt preparation.
Tests the prompt generation without sending to LLM to verify content accuracy.
"""

import unittest
from datetime import datetime
from django.test import TestCase
from django.utils import timezone
from liuyao.models import liuyao
from ai.utils.liuyao_analysis import prepare_liuyao_prompt
from ai.models import PromptTemplate
from iching.utils import liuyao as liuyao_util, bz as bazi
import json


class LiuYaoPromptTestCase(TestCase):
    """Test cases for LiuYao prompt preparation."""

    def setUp(self):
        """Set up test fixtures."""
        self.maxDiff = None
        
        # Create a simple test template for LiuYao
        self.test_template = PromptTemplate.objects.create(
            name='Test LiuYao Template',
            divination_type='liuyao',
            content="""所问问题：{{question}}
占卜时间：{{qdate}}

六爻计算结果：
{{hexagram_calculation}}

动爻：{{changing_lines}}
动爻数量：{{changing_lines_count}}

变卦：{{changed_hexagram_info}}
变卦序：{{changed_hexagram_number}}""",
            status='active',
            created_by=None,
            language='zh-hans'
        )

    def create_liuyao_object(self, date_str, y1, y2, y3, y4, y5, y6, question="测试问题"):
        """Helper method to create a LiuYao object with specified parameters."""
        # Parse the date string
        if '/' in date_str:
            date_obj = datetime.strptime(date_str, '%Y/%m/%d %H:%M:%S')
        else:
            date_obj = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S')
        
        # Make datetime timezone-aware
        date_obj = timezone.make_aware(date_obj)
        
        # Calculate LiuYao data
        bz = bazi.getDateTimeGodEarthStem(
            date_obj.year, date_obj.month, date_obj.day, 
            date_obj.hour, date_obj.minute
        )
        bz['date'] = date_obj.strftime('%Y-%m-%d %H:%M:%S')
        bz['empty'] = bazi.calcEarthEmpty(bz['day']['g'], bz['day']['e'])
        
        god6 = liuyao_util.calc6God(bz['day']['g'])
        ly = liuyao_util.calc6Yao(str(y1), str(y2), str(y3), str(y4), str(y5), str(y6))
        rel = liuyao_util.calcRelationship(ly, bz)
        
        # Create the data structure that matches what the model expects
        # Note: Do not wrap in an additional 'gua' key as the model stores the full data directly
        gua_data = {
            'bz': bz,
            'god6': god6,
            'ly': ly,
            'rel': rel,
        }
        
        # Create LiuYao object
        liuyao_obj = liuyao.objects.create(
            qdate=date_obj,
            question=question,
            y1=str(y1),
            y2=str(y2),
            y3=str(y3),
            y4=str(y4),
            y5=str(y5),
            y6=str(y6),
            data=gua_data  # JSONField will handle the serialization automatically
        )
        
        return liuyao_obj

    def test_no_moving_lines_no_dongya(self):
        """Test 1: LiuYao without moving lines should not have '动爻' in prompt."""
        # Create a LiuYao object with no moving lines (all 0s or all 1s)
        liuyao_obj = self.create_liuyao_object(
            '2025-02-03 12:00:00', 
            y1=0, y2=0, y3=0, y4=0, y5=0, y6=0
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should show no changing lines 
        self.assertIn('无动爻', prompt)
        self.assertIn('动爻数量：0', prompt)

    def test_no_moving_lines_no_changing_gua(self):
        """Test 2: LiuYao without moving lines should not have changing gua."""
        liuyao_obj = self.create_liuyao_object(
            '2025-02-03 12:00:00',
            y1=1, y2=1, y3=1, y4=1, y5=1, y6=1
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should not have changing gua content - check that the placeholder values are empty
        self.assertIn('变卦：\n', prompt)  # Empty changing gua
        self.assertIn('变卦序：', prompt)  # Empty changing gua number (no newline needed)

    def test_with_moving_lines_has_dongya(self):
        """Test 3: LiuYao with moving lines should have '动爻' in prompt."""
        liuyao_obj = self.create_liuyao_object(
            '2025-02-03 12:00:00',
            y1=0, y2=0, y3=0, y4=111, y5=0, y6=0  # 4th yao has moving line
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should contain changing lines information and count > 0
        self.assertIn('动爻数量：1', prompt)
        self.assertNotIn('无动爻', prompt)

    def test_with_moving_lines_has_changing_gua(self):
        """Test 4: LiuYao with moving lines should have changing gua."""
        liuyao_obj = self.create_liuyao_object(
            '2025-02-03 12:00:00',
            y1=0, y2=0, y3=0, y4=111, y5=0, y6=0
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should contain changing gua information (not empty)
        # The exact format depends on the data, but it should not be just empty lines
        self.assertNotIn('变卦：\n变卦序：\n', prompt)  # Should not have consecutive empty lines

    def test_bazi_date_2025_02_03_22_11(self):
        """Test 5: Bazi date for 3rd Feb 2025 22:11:00 should be correct."""
        liuyao_obj = self.create_liuyao_object(
            '2025-02-03 22:11:00',
            y1=0, y2=0, y3=0, y4=0, y5=0, y6=0
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should contain the correct bazi: "乙巳年 戊寅月 癸卯日 癸亥时 （空亡：辰巳）"
        self.assertIn('乙巳年', prompt)
        self.assertIn('戊寅月', prompt)
        self.assertIn('癸卯日', prompt)
        self.assertIn('癸亥时', prompt)
        self.assertIn('空亡：辰巳', prompt)

    def test_bazi_date_2025_02_03_22_10(self):
        """Test 6: Bazi date for 3rd Feb 2025 22:10:00 should be correct."""
        liuyao_obj = self.create_liuyao_object(
            '2025-02-03 22:10:00',
            y1=0, y2=0, y3=0, y4=0, y5=0, y6=0
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should contain the correct bazi: "甲辰年 丁丑月 癸卯日 癸亥时 （空亡：辰巳）"
        self.assertIn('甲辰年', prompt)
        self.assertIn('丁丑月', prompt)
        self.assertIn('癸卯日', prompt)
        self.assertIn('癸亥时', prompt)
        self.assertIn('空亡：辰巳', prompt)

    def test_hidden_moving_vs_moving_line_priority(self):
        """Test 7: When hidden moving line is also moving line, no '暗动' should appear."""
        liuyao_obj = self.create_liuyao_object(
            '2025/02/12 12:00:00',
            y1=0, y2=0, y3=0, y4=111, y5=1, y6=1
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should have changing lines (4th yao has 111) but not show 暗动 for the same line
        self.assertIn('动爻数量：1', prompt)  # Only one changing line (111)
        self.assertNotIn('无动爻', prompt)

    def test_hidden_moving_line_andong(self):
        """Test 8: Test presence of hidden moving line (暗动) at 4th yao."""
        liuyao_obj = self.create_liuyao_object(
            '2025/02/12 12:00:00',
            y1=0, y2=0, y3=0, y4=1, y5=1, y6=1
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should contain "暗动" and reference to 4th yao
        self.assertIn('暗动', prompt)

    def test_no_hidden_6qin_fushen(self):
        """Test 9: No hidden 6qin (伏神) in main gua."""
        liuyao_obj = self.create_liuyao_object(
            '2025/02/12 12:00:00',
            y1=1, y2=1, y3=1, y4=1, y5=0, y6=1
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should not contain "伏神" 
        self.assertNotIn('伏神', prompt)

    def test_hidden_6qin_fushen_case1(self):
        """Test 10: Hidden 6qin (伏神) case 1 - 3rd and 5th yao."""
        liuyao_obj = self.create_liuyao_object(
            '2025/02/12 12:00:00',
            y1=1, y2=1, y3=1, y4=0, y5=1, y6=1
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should contain hidden 6qin information in the format (伏神：stem+branch，relation)
        # Based on actual calculation results
        self.assertIn('伏神：辛酉，官鬼', prompt)  # 3rd yao (actual calculated value)

    def test_hidden_6qin_fushen_case2(self):
        """Test 11: Hidden 6qin (伏神) case 2 - 2nd yao."""
        liuyao_obj = self.create_liuyao_object(
            '2025/02/12 12:00:00',
            y1=1, y2=1, y3=1, y4=0, y5=0, y6=0
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should contain hidden 6qin information
        # Based on actual calculation results
        self.assertIn('伏神：乙巳，父母', prompt)  # 2nd yao (actual calculated value)

    def test_liuchong_gua(self):
        """Test 12: Test 六冲 gua identification."""
        liuyao_obj = self.create_liuyao_object(
            '2023/08/16 12:00:00',
            y1=1, y2=0, y3=1, y4=1, y5=0, y6=1
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should identify as 六冲卦
        self.assertIn('六冲', prompt)

    def test_liuhe_gua(self):
        """Test 13: Test 六合 gua identification."""
        liuyao_obj = self.create_liuyao_object(
            '2023/08/16 12:00:00',
            y1=0, y2=0, y3=0, y4=1, y5=1, y6=1
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should identify as 六合卦
        self.assertIn('六合', prompt)

    def test_youhun_gua(self):
        """Test 14: Test 游魂 gua identification."""
        liuyao_obj = self.create_liuyao_object(
            '2023/08/16 12:00:00',
            y1=0, y2=0, y3=0, y4=1, y5=0, y6=1
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should identify as 游魂卦
        self.assertIn('游魂', prompt)

    def test_guihun_gua(self):
        """Test 15: Test 归魂 gua identification."""
        liuyao_obj = self.create_liuyao_object(
            '2023/08/16 12:00:00',
            y1=1, y2=1, y3=1, y4=1, y5=0, y6=1
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should identify as 归魂卦
        self.assertIn('归魂', prompt)

    def test_prompt_structure_completeness(self):
        """Test that the prompt contains all necessary structural elements."""
        liuyao_obj = self.create_liuyao_object(
            '2025-02-03 12:00:00',
            y1=0, y2=0, y3=0, y4=111, y5=0, y6=0
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should contain basic structural elements
        self.assertIn('问题', prompt)
        self.assertIn('本卦', prompt)
        self.assertIn('测试问题', prompt)  # The question we set
        
        # Should contain temporal information
        self.assertTrue(any(x in prompt for x in ['2025', '二月', '三日']))

    def test_prompt_yao_positions(self):
        """Test that yao positions are correctly represented in prompt."""
        liuyao_obj = self.create_liuyao_object(
            '2025-02-03 12:00:00',
            y1=1, y2=0, y3=1, y4=0, y5=1, y6=0
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should contain yao position references
        self.assertTrue(any(x in prompt for x in ['初爻', '二爻', '三爻', '四爻', '五爻', '上爻']))

    def test_bazi_date_2025_05_23_15_05(self):
        """Test for 23rd May 2025 15:05:42 should show correct empty branches."""
        liuyao_obj = self.create_liuyao_object(
            '2025-05-23 15:05:42',
            y1=0, y2=0, y3=0, y4=0, y5=0, y6=0
        )
        
        prompt = prepare_liuyao_prompt(liuyao_obj)
        
        # Should contain the correct bazi with empty branches
        self.assertIn('乙巳年', prompt)
        self.assertIn('辛巳月', prompt)
        self.assertIn('壬辰日', prompt)  # Corrected to actual calculation
        self.assertIn('戊申时', prompt)  # Corrected to actual calculation
        self.assertIn('空亡：午未', prompt)

    def tearDown(self):
        """Clean up after tests."""
        # Clean up any created objects
        liuyao.objects.all().delete()
        PromptTemplate.objects.filter(divination_type='liuyao', name='Test LiuYao Template').delete()


if __name__ == '__main__':
    unittest.main() 