"""
Comprehensive tests for the ereader layout system.

Tests the complete ereader functionality including position tracking,
font scaling, chapter navigation, and page buffering.
"""

import unittest
import tempfile
import shutil
from pathlib import Path

from pyWebLayout.abstract.block import Paragraph, Heading, HeadingLevel
from pyWebLayout.abstract.inline import Word
from pyWebLayout.style import Font
from pyWebLayout.style.page_style import PageStyle
from pyWebLayout.layout.ereader_layout import RenderingPosition, ChapterNavigator, FontScaler, BidirectionalLayouter
from pyWebLayout.layout.ereader_manager import EreaderLayoutManager, BookmarkManager, create_ereader_manager


class TestRenderingPosition(unittest.TestCase):
    """Test the RenderingPosition class"""
    
    def test_position_creation(self):
        """Test creating a rendering position"""
        pos = RenderingPosition(
            chapter_index=1,
            block_index=5,
            word_index=10,
            table_row=2,
            table_col=3
        )
        
        self.assertEqual(pos.chapter_index, 1)
        self.assertEqual(pos.block_index, 5)
        self.assertEqual(pos.word_index, 10)
        self.assertEqual(pos.table_row, 2)
        self.assertEqual(pos.table_col, 3)
    
    def test_position_serialization(self):
        """Test position serialization and deserialization"""
        pos = RenderingPosition(
            chapter_index=1,
            block_index=5,
            word_index=10,
            remaining_pretext="test"
        )
        
        # Serialize to dict
        pos_dict = pos.to_dict()
        self.assertIsInstance(pos_dict, dict)
        self.assertEqual(pos_dict['chapter_index'], 1)
        self.assertEqual(pos_dict['remaining_pretext'], "test")
        
        # Deserialize from dict
        pos2 = RenderingPosition.from_dict(pos_dict)
        self.assertEqual(pos, pos2)
    
    def test_position_copy(self):
        """Test position copying"""
        pos = RenderingPosition(chapter_index=1, block_index=5)
        pos_copy = pos.copy()
        
        self.assertEqual(pos, pos_copy)
        self.assertIsNot(pos, pos_copy)  # Different objects
        
        # Modify copy
        pos_copy.word_index = 10
        self.assertNotEqual(pos, pos_copy)
    
    def test_position_equality_and_hashing(self):
        """Test position equality and hashing"""
        pos1 = RenderingPosition(chapter_index=1, block_index=5)
        pos2 = RenderingPosition(chapter_index=1, block_index=5)
        pos3 = RenderingPosition(chapter_index=1, block_index=6)
        
        self.assertEqual(pos1, pos2)
        self.assertNotEqual(pos1, pos3)
        
        # Test hashing (for use as dict keys)
        pos_dict = {pos1: "test"}
        self.assertEqual(pos_dict[pos2], "test")  # Should work due to equality


class TestChapterNavigator(unittest.TestCase):
    """Test the ChapterNavigator class"""
    
    def setUp(self):
        """Set up test data"""
        self.font = Font()
        
        # Create test blocks with headings
        self.blocks = [
            Paragraph(self.font),  # Block 0
            Heading(HeadingLevel.H1, self.font),  # Block 1 - Chapter 1
            Paragraph(self.font),  # Block 2
            Heading(HeadingLevel.H2, self.font),  # Block 3 - Subsection
            Paragraph(self.font),  # Block 4
            Heading(HeadingLevel.H1, self.font),  # Block 5 - Chapter 2
            Paragraph(self.font),  # Block 6
        ]
        
        # Add text to headings
        self.blocks[1].add_word(Word("Chapter", self.font))
        self.blocks[1].add_word(Word("One", self.font))
        
        self.blocks[3].add_word(Word("Subsection", self.font))
        self.blocks[3].add_word(Word("A", self.font))
        
        self.blocks[5].add_word(Word("Chapter", self.font))
        self.blocks[5].add_word(Word("Two", self.font))
    
    def test_chapter_detection(self):
        """Test that chapters are detected correctly"""
        navigator = ChapterNavigator(self.blocks)
        
        self.assertEqual(len(navigator.chapters), 3)  # 2 H1s + 1 H2
        
        # Check chapter titles
        titles = [chapter.title for chapter in navigator.chapters]
        self.assertIn("Chapter One", titles)
        self.assertIn("Subsection A", titles)
        self.assertIn("Chapter Two", titles)
    
    def test_table_of_contents(self):
        """Test table of contents generation"""
        navigator = ChapterNavigator(self.blocks)
        toc = navigator.get_table_of_contents()
        
        self.assertEqual(len(toc), 3)
        
        # Check first entry
        title, level, position = toc[0]
        self.assertEqual(title, "Chapter One")
        self.assertEqual(level, HeadingLevel.H1)
        self.assertIsInstance(position, RenderingPosition)
    
    def test_chapter_position_lookup(self):
        """Test looking up chapter positions"""
        navigator = ChapterNavigator(self.blocks)
        
        pos = navigator.get_chapter_position("Chapter One")
        self.assertIsNotNone(pos)
        self.assertEqual(pos.chapter_index, 0)
        
        pos = navigator.get_chapter_position("Nonexistent Chapter")
        self.assertIsNone(pos)
    
    def test_current_chapter_detection(self):
        """Test detecting current chapter from position"""
        navigator = ChapterNavigator(self.blocks)
        
        # Position in first chapter
        pos = RenderingPosition(chapter_index=0, block_index=2)
        chapter = navigator.get_current_chapter(pos)
        self.assertIsNotNone(chapter)
        self.assertEqual(chapter.title, "Chapter One")


class TestFontScaler(unittest.TestCase):
    """Test the FontScaler class"""
    
    def test_font_scaling(self):
        """Test font scaling functionality"""
        original_font = Font(font_size=12)
        
        # Test no scaling
        scaled_font = FontScaler.scale_font(original_font, 1.0)
        self.assertEqual(scaled_font.font_size, 12)
        
        # Test 2x scaling
        scaled_font = FontScaler.scale_font(original_font, 2.0)
        self.assertEqual(scaled_font.font_size, 24)
        
        # Test 0.5x scaling
        scaled_font = FontScaler.scale_font(original_font, 0.5)
        self.assertEqual(scaled_font.font_size, 6)
        
        # Test minimum size constraint
        scaled_font = FontScaler.scale_font(original_font, 0.01)
        self.assertGreaterEqual(scaled_font.font_size, 1)
    
    def test_word_spacing_scaling(self):
        """Test word spacing scaling"""
        original_spacing = (5, 15)
        
        # Test no scaling
        scaled_spacing = FontScaler.scale_word_spacing(original_spacing, 1.0)
        self.assertEqual(scaled_spacing, (5, 15))
        
        # Test 2x scaling
        scaled_spacing = FontScaler.scale_word_spacing(original_spacing, 2.0)
        self.assertEqual(scaled_spacing, (10, 30))
        
        # Test minimum constraints
        scaled_spacing = FontScaler.scale_word_spacing(original_spacing, 0.1)
        self.assertGreaterEqual(scaled_spacing[0], 1)
        self.assertGreaterEqual(scaled_spacing[1], 2)


class TestBookmarkManager(unittest.TestCase):
    """Test the BookmarkManager class"""
    
    def setUp(self):
        """Set up test environment"""
        self.temp_dir = tempfile.mkdtemp()
        self.document_id = "test_document"
        self.bookmark_manager = BookmarkManager(self.document_id, self.temp_dir)
    
    def tearDown(self):
        """Clean up test environment"""
        shutil.rmtree(self.temp_dir)
    
    def test_bookmark_operations(self):
        """Test bookmark add/remove/get operations"""
        pos = RenderingPosition(chapter_index=1, block_index=5)
        
        # Add bookmark
        self.bookmark_manager.add_bookmark("test_bookmark", pos)
        
        # Get bookmark
        retrieved_pos = self.bookmark_manager.get_bookmark("test_bookmark")
        self.assertEqual(retrieved_pos, pos)
        
        # List bookmarks
        bookmarks = self.bookmark_manager.list_bookmarks()
        self.assertEqual(len(bookmarks), 1)
        self.assertEqual(bookmarks[0][0], "test_bookmark")
        self.assertEqual(bookmarks[0][1], pos)
        
        # Remove bookmark
        success = self.bookmark_manager.remove_bookmark("test_bookmark")
        self.assertTrue(success)
        
        # Verify removal
        retrieved_pos = self.bookmark_manager.get_bookmark("test_bookmark")
        self.assertIsNone(retrieved_pos)
    
    def test_reading_position_persistence(self):
        """Test saving and loading reading position"""
        pos = RenderingPosition(chapter_index=2, block_index=10, word_index=5)
        
        # Save position
        self.bookmark_manager.save_reading_position(pos)
        
        # Create new manager instance (simulates app restart)
        new_manager = BookmarkManager(self.document_id, self.temp_dir)
        
        # Load position
        loaded_pos = new_manager.load_reading_position()
        self.assertEqual(loaded_pos, pos)
    
    def test_bookmark_persistence(self):
        """Test that bookmarks persist across manager instances"""
        pos = RenderingPosition(chapter_index=1, block_index=5)
        
        # Add bookmark
        self.bookmark_manager.add_bookmark("persistent_bookmark", pos)
        
        # Create new manager instance
        new_manager = BookmarkManager(self.document_id, self.temp_dir)
        
        # Verify bookmark exists
        retrieved_pos = new_manager.get_bookmark("persistent_bookmark")
        self.assertEqual(retrieved_pos, pos)


class TestEreaderLayoutManager(unittest.TestCase):
    """Test the complete EreaderLayoutManager"""
    
    def setUp(self):
        """Set up test data"""
        self.temp_dir = tempfile.mkdtemp()
        self.font = Font()
        
        # Create test document with multiple paragraphs and headings
        self.blocks = []
        
        # Add a heading
        heading = Heading(HeadingLevel.H1, self.font)
        heading.add_word(Word("Test", self.font))
        heading.add_word(Word("Chapter", self.font))
        self.blocks.append(heading)
        
        # Add several paragraphs with multiple words
        for i in range(3):
            paragraph = Paragraph(self.font)
            for j in range(20):  # 20 words per paragraph
                paragraph.add_word(Word(f"Word{i}_{j}", self.font))
            self.blocks.append(paragraph)
        
        self.page_size = (400, 600)
        self.document_id = "test_document"
    
    def tearDown(self):
        """Clean up test environment"""
        shutil.rmtree(self.temp_dir)
    
    def test_manager_initialization(self):
        """Test ereader manager initialization"""
        # Change to temp directory for bookmarks
        original_cwd = Path.cwd()
        try:
            import os
            os.chdir(self.temp_dir)
            
            manager = EreaderLayoutManager(
                self.blocks, 
                self.page_size, 
                self.document_id,
                bookmarks_dir=self.temp_dir
            )
            
            self.assertEqual(manager.page_size, self.page_size)
            self.assertEqual(manager.document_id, self.document_id)
            self.assertEqual(manager.font_scale, 1.0)
            self.assertIsInstance(manager.current_position, RenderingPosition)
            
            manager.shutdown()
        finally:
            os.chdir(original_cwd)
    
    def test_font_scaling(self):
        """Test font scaling functionality"""
        original_cwd = Path.cwd()
        try:
            import os
            os.chdir(self.temp_dir)
            
            manager = EreaderLayoutManager(
                self.blocks, 
                self.page_size, 
                self.document_id,
                bookmarks_dir=self.temp_dir
            )
            
            # Test initial scale
            self.assertEqual(manager.get_font_scale(), 1.0)
            
            # Test scaling
            page = manager.set_font_scale(1.5)
            self.assertEqual(manager.get_font_scale(), 1.5)
            self.assertIsNotNone(page)
            
            manager.shutdown()
        finally:
            os.chdir(original_cwd)
    
    def test_table_of_contents(self):
        """Test table of contents functionality"""
        original_cwd = Path.cwd()
        try:
            import os
            os.chdir(self.temp_dir)
            
            manager = EreaderLayoutManager(
                self.blocks, 
                self.page_size, 
                self.document_id,
                bookmarks_dir=self.temp_dir
            )
            
            toc = manager.get_table_of_contents()
            self.assertGreater(len(toc), 0)
            
            # Check first entry
            title, level, position = toc[0]
            self.assertEqual(title, "Test Chapter")
            self.assertEqual(level, HeadingLevel.H1)
            
            manager.shutdown()
        finally:
            os.chdir(original_cwd)
    
    def test_bookmark_functionality(self):
        """Test bookmark functionality"""
        manager = EreaderLayoutManager(
            self.blocks, 
            self.page_size, 
            self.document_id,
            bookmarks_dir=self.temp_dir
        )
        
        # Add bookmark
        success = manager.add_bookmark("test_bookmark")
        self.assertTrue(success)
        
        # List bookmarks
        bookmarks = manager.list_bookmarks()
        self.assertEqual(len(bookmarks), 1)
        self.assertEqual(bookmarks[0][0], "test_bookmark")
        
        # Jump to bookmark (should work even though it's the same position)
        page = manager.jump_to_bookmark("test_bookmark")
        self.assertIsNotNone(page)
        
        # Remove bookmark
        success = manager.remove_bookmark("test_bookmark")
        self.assertTrue(success)
        
        manager.shutdown()
    
    def test_progress_tracking(self):
        """Test reading progress tracking"""
        original_cwd = Path.cwd()
        try:
            import os
            os.chdir(self.temp_dir)
            
            manager = EreaderLayoutManager(
                self.blocks, 
                self.page_size, 
                self.document_id,
                bookmarks_dir=self.temp_dir
            )
            
            # Initial progress should be 0
            progress = manager.get_reading_progress()
            self.assertGreaterEqual(progress, 0.0)
            self.assertLessEqual(progress, 1.0)
            
            # Get position info
            info = manager.get_position_info()
            self.assertIn('position', info)
            self.assertIn('progress', info)
            self.assertIn('font_scale', info)
            
            manager.shutdown()
        finally:
            os.chdir(original_cwd)
    
    def test_convenience_function(self):
        """Test the convenience function"""
        original_cwd = Path.cwd()
        try:
            import os
            os.chdir(self.temp_dir)
            
            manager = create_ereader_manager(
                self.blocks, 
                self.page_size, 
                self.document_id,
                bookmarks_dir=self.temp_dir
            )
            
            self.assertIsInstance(manager, EreaderLayoutManager)
            self.assertEqual(manager.page_size, self.page_size)
            
            manager.shutdown()
        finally:
            os.chdir(original_cwd)


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