"""
Unit tests for TOC overlay functionality.

Tests the complete workflow of:
1. Opening TOC overlay with swipe up gesture
2. Selecting a chapter from the TOC
3. Closing overlay by tapping outside or swiping down
"""

import unittest
from pathlib import Path
from dreader import (
    EbookReader,
    TouchEvent,
    GestureType,
    ActionType,
    OverlayState
)


class TestTOCOverlay(unittest.TestCase):
    """Test TOC overlay opening, interaction, and closing"""

    def setUp(self):
        """Set up test reader with a book"""
        self.reader = EbookReader(page_size=(800, 1200))

        # Load a test EPUB
        test_epub = Path(__file__).parent / 'data' / 'library-epub' / 'alice.epub'
        if not test_epub.exists():
            # Try to find any EPUB in test data
            epub_dir = Path(__file__).parent / 'data' / 'library-epub'
            epubs = list(epub_dir.glob('*.epub'))
            if epubs:
                test_epub = epubs[0]
            else:
                self.skipTest("No test EPUB files available")

        success = self.reader.load_epub(str(test_epub))
        self.assertTrue(success, "Failed to load test EPUB")

    def tearDown(self):
        """Clean up"""
        self.reader.close()

    def test_overlay_manager_initialization(self):
        """Test that overlay sub-applications are properly initialized"""
        # Check that overlay sub-applications exist
        self.assertIsNotNone(self.reader._overlay_subapps)
        self.assertIn(OverlayState.TOC, self.reader._overlay_subapps)
        self.assertIn(OverlayState.SETTINGS, self.reader._overlay_subapps)
        self.assertIn(OverlayState.NAVIGATION, self.reader._overlay_subapps)

        # Initially no overlay should be active
        self.assertFalse(self.reader.is_overlay_open())
        self.assertEqual(self.reader.get_overlay_state(), OverlayState.NONE)

    def test_open_toc_overlay_directly(self):
        """Test opening TOC overlay using direct API call"""
        # Initially no overlay
        self.assertFalse(self.reader.is_overlay_open())

        # Open TOC overlay
        overlay_image = self.reader.open_toc_overlay()

        # Should return an image
        self.assertIsNotNone(overlay_image)
        self.assertEqual(overlay_image.size, (800, 1200))

        # Overlay should be open
        self.assertTrue(self.reader.is_overlay_open())
        self.assertEqual(self.reader.get_overlay_state(), OverlayState.TOC)

    def test_close_toc_overlay_directly(self):
        """Test closing TOC overlay using direct API call"""
        # Open overlay first
        self.reader.open_toc_overlay()
        self.assertTrue(self.reader.is_overlay_open())

        # Close overlay
        page_image = self.reader.close_overlay()

        # Should return base page
        self.assertIsNotNone(page_image)

        # Overlay should be closed
        self.assertFalse(self.reader.is_overlay_open())
        self.assertEqual(self.reader.get_overlay_state(), OverlayState.NONE)

    def test_swipe_up_from_bottom_opens_toc(self):
        """Test that swipe up from bottom of screen opens TOC overlay"""
        # Create swipe up event from bottom of screen (y=1100, which is > 80% of 1200)
        event = TouchEvent(
            gesture=GestureType.SWIPE_UP,
            x=400,
            y=1100
        )

        # Handle gesture
        response = self.reader.handle_touch(event)

        # Should open overlay (navigation or toc, depending on implementation)
        self.assertEqual(response.action, ActionType.OVERLAY_OPENED)
        self.assertIn(response.data['overlay_type'], ['toc', 'navigation'])
        self.assertTrue(self.reader.is_overlay_open())

    def test_swipe_up_from_middle_opens_navigation(self):
        """Test that swipe up from anywhere opens navigation overlay"""
        # Create swipe up event from middle of screen
        event = TouchEvent(
            gesture=GestureType.SWIPE_UP,
            x=400,
            y=600
        )

        # Handle gesture
        response = self.reader.handle_touch(event)

        # Should open navigation overlay from anywhere
        self.assertEqual(response.action, ActionType.OVERLAY_OPENED)
        self.assertIn(response.data['overlay_type'], ['toc', 'navigation'])
        self.assertTrue(self.reader.is_overlay_open())

    def test_swipe_down_closes_overlay(self):
        """Test that swipe down closes the overlay"""
        # Open overlay first
        self.reader.open_toc_overlay()
        self.assertTrue(self.reader.is_overlay_open())

        # Create swipe down event
        event = TouchEvent(
            gesture=GestureType.SWIPE_DOWN,
            x=400,
            y=300
        )

        # Handle gesture
        response = self.reader.handle_touch(event)

        # Should close overlay
        self.assertEqual(response.action, ActionType.OVERLAY_CLOSED)
        self.assertFalse(self.reader.is_overlay_open())

    def test_tap_outside_overlay_closes_it(self):
        """Test that tapping outside the overlay panel closes it"""
        # Open overlay first
        self.reader.open_toc_overlay()
        self.assertTrue(self.reader.is_overlay_open())

        # Tap in the far left (outside the centered panel)
        # Panel is 60% wide centered, so left edge is at 20%
        event = TouchEvent(
            gesture=GestureType.TAP,
            x=50,  # Well outside panel
            y=600
        )

        # Handle gesture
        response = self.reader.handle_touch(event)

        # Should close overlay
        self.assertEqual(response.action, ActionType.OVERLAY_CLOSED)
        self.assertFalse(self.reader.is_overlay_open())

    def test_tap_on_chapter_selects_and_closes(self):
        """Test that tapping on a chapter navigates to it and closes overlay"""
        # Open overlay first
        self.reader.open_toc_overlay()
        chapters = self.reader.get_chapters()

        if len(chapters) < 2:
            self.skipTest("Need at least 2 chapters for this test")

        # Calculate tap position for second chapter (index 1 - "Metamorphosis")
        # Based on actual measurements from pyWebLayout query_point:
        # Overlay bounds: (38, 138, 122, 16) -> X=[38,160], Y=[138,154]
        # With panel offset (160, 180): Screen X=[198,320], Y=[318,334]
        tap_x = 250  # Within the link text bounds
        tap_y = 335  # Chapter 1 "Metamorphosis" at overlay Y=155 (138+16=154, screen 180+155=335)

        event = TouchEvent(
            gesture=GestureType.TAP,
            x=tap_x,
            y=tap_y
        )

        # Handle gesture
        response = self.reader.handle_touch(event)

        # Should select chapter
        self.assertEqual(response.action, ActionType.CHAPTER_SELECTED)
        self.assertIn('chapter_index', response.data)

        # Overlay should be closed
        self.assertFalse(self.reader.is_overlay_open())

    def test_multiple_overlay_operations(self):
        """Test opening and closing overlay multiple times"""
        # Open and close 3 times
        for i in range(3):
            # Open
            self.reader.open_toc_overlay()
            self.assertTrue(self.reader.is_overlay_open())

            # Close
            self.reader.close_overlay()
            self.assertFalse(self.reader.is_overlay_open())

    def test_overlay_with_page_navigation(self):
        """Test that overlay works correctly after navigating pages"""
        # Navigate to page 2
        self.reader.next_page()

        # Open overlay
        overlay_image = self.reader.open_toc_overlay()
        self.assertIsNotNone(overlay_image)
        self.assertTrue(self.reader.is_overlay_open())

        # Close overlay
        self.reader.close_overlay()
        self.assertFalse(self.reader.is_overlay_open())

    def test_toc_overlay_contains_all_chapters(self):
        """Test that TOC overlay includes all book chapters"""
        chapters = self.reader.get_chapters()

        # Open overlay (this generates the HTML with chapters)
        overlay_image = self.reader.open_toc_overlay()
        self.assertIsNotNone(overlay_image)

        # Verify overlay manager has correct chapter count
        # This is implicit in the rendering - if it renders without error,
        # all chapters were included
        self.assertTrue(self.reader.is_overlay_open())

    def test_overlay_state_persistence_ready(self):
        """Test that overlay state can be tracked (for future state persistence)"""
        # This test verifies the state tracking is ready for StateManager integration

        # Start with no overlay
        self.assertEqual(self.reader.current_overlay_state, OverlayState.NONE)

        # Open TOC
        self.reader.open_toc_overlay()
        self.assertEqual(self.reader.current_overlay_state, OverlayState.TOC)

        # Close overlay
        self.reader.close_overlay()
        self.assertEqual(self.reader.current_overlay_state, OverlayState.NONE)


class TestOverlayRendering(unittest.TestCase):
    """Test overlay rendering and compositing"""

    def setUp(self):
        """Set up test reader"""
        self.reader = EbookReader(page_size=(800, 1200))
        test_epub = Path(__file__).parent / 'data' / 'library-epub' / 'alice.epub'

        if not test_epub.exists():
            epub_dir = Path(__file__).parent / 'data' / 'library-epub'
            epubs = list(epub_dir.glob('*.epub'))
            if epubs:
                test_epub = epubs[0]
            else:
                self.skipTest("No test EPUB files available")

        self.reader.load_epub(str(test_epub))

    def tearDown(self):
        """Clean up"""
        self.reader.close()

    def test_overlay_image_size(self):
        """Test that overlay image matches page size"""
        overlay_image = self.reader.open_toc_overlay()
        self.assertEqual(overlay_image.size, (800, 1200))

    def test_overlay_compositing(self):
        """Test that overlay is properly composited on base page"""
        # Get base page
        base_page = self.reader.get_current_page()

        # Open overlay (creates composited image)
        overlay_image = self.reader.open_toc_overlay()

        # Composited image should be different from base page
        self.assertIsNotNone(overlay_image)

        # Images should have same size but different content
        self.assertEqual(base_page.size, overlay_image.size)

    def test_overlay_html_to_image_conversion(self):
        """Test that HTML overlay is correctly converted to image"""
        from dreader.html_generator import generate_toc_overlay

        # Get chapters
        chapters = self.reader.get_chapters()
        chapter_data = [{"index": idx, "title": title} for title, idx in chapters]

        # Generate HTML
        html = generate_toc_overlay(chapter_data)
        self.assertIsNotNone(html)
        self.assertIn("Table of Contents", html)

        # Open the TOC overlay which internally renders HTML to image
        overlay_image = self.reader.open_toc_overlay()

        # Should produce valid image
        self.assertIsNotNone(overlay_image)
        self.assertEqual(overlay_image.size, (800, 1200))


class TestTOCPagination(unittest.TestCase):
    """Test TOC overlay pagination functionality"""

    def setUp(self):
        """Set up test reader with a book"""
        self.reader = EbookReader(page_size=(800, 1200))

        # Load a test EPUB
        test_epub = Path(__file__).parent / 'data' / 'library-epub' / 'alice.epub'
        if not test_epub.exists():
            epub_dir = Path(__file__).parent / 'data' / 'library-epub'
            epubs = list(epub_dir.glob('*.epub'))
            if epubs:
                test_epub = epubs[0]
            else:
                self.skipTest("No test EPUB files available")

        success = self.reader.load_epub(str(test_epub))
        self.assertTrue(success, "Failed to load test EPUB")

    def tearDown(self):
        """Clean up"""
        self.reader.close()

    def test_pagination_with_many_chapters(self):
        """Test pagination when there are more chapters than fit on one page"""
        from dreader.html_generator import generate_toc_overlay

        # Create test data with many chapters
        chapters = [{"index": i, "title": f"Chapter {i+1}"} for i in range(25)]

        # Generate HTML for page 1 (chapters 0-9)
        html_page1 = generate_toc_overlay(chapters, page_size=(800, 1200), toc_page=0, toc_items_per_page=10)
        self.assertIn("1. Chapter 1", html_page1)
        self.assertIn("10. Chapter 10", html_page1)
        self.assertNotIn("11. Chapter 11", html_page1)
        self.assertIn("Page 1 of 3", html_page1)

        # Generate HTML for page 2 (chapters 10-19)
        html_page2 = generate_toc_overlay(chapters, page_size=(800, 1200), toc_page=1, toc_items_per_page=10)
        self.assertNotIn("10. Chapter 10", html_page2)
        self.assertIn("11. Chapter 11", html_page2)
        self.assertIn("20. Chapter 20", html_page2)
        self.assertIn("Page 2 of 3", html_page2)

        # Generate HTML for page 3 (chapters 20-24)
        html_page3 = generate_toc_overlay(chapters, page_size=(800, 1200), toc_page=2, toc_items_per_page=10)
        self.assertNotIn("20. Chapter 20", html_page3)
        self.assertIn("21. Chapter 21", html_page3)
        self.assertIn("25. Chapter 25", html_page3)
        self.assertIn("Page 3 of 3", html_page3)

    def test_pagination_buttons_disabled_at_boundaries(self):
        """Test that pagination buttons are disabled at first and last pages"""
        from dreader.html_generator import generate_toc_overlay

        chapters = [{"index": i, "title": f"Chapter {i+1}"} for i in range(25)]

        # Page 1: prev button should be disabled
        html_page1 = generate_toc_overlay(chapters, page_size=(800, 1200), toc_page=0, toc_items_per_page=10)
        self.assertIn("page:prev", html_page1)
        self.assertIn("page:next", html_page1)
        # Check that prev button has disabled styling
        self.assertIn("opacity: 0.3; pointer-events: none;", html_page1)

        # Last page: next button should be disabled
        html_page3 = generate_toc_overlay(chapters, page_size=(800, 1200), toc_page=2, toc_items_per_page=10)
        self.assertIn("page:prev", html_page3)
        self.assertIn("page:next", html_page3)

    def test_no_pagination_for_small_list(self):
        """Test that pagination is not shown when all chapters fit on one page"""
        from dreader.html_generator import generate_toc_overlay

        chapters = [{"index": i, "title": f"Chapter {i+1}"} for i in range(5)]

        html = generate_toc_overlay(chapters, page_size=(800, 1200), toc_page=0, toc_items_per_page=10)
        self.assertNotIn("page:prev", html)
        self.assertNotIn("page:next", html)
        self.assertNotIn("Page", html.split("chapters")[1])  # No "Page X of Y" after "N chapters"

    def test_navigation_overlay_pagination(self):
        """Test pagination in the modern navigation overlay"""
        from dreader.html_generator import generate_navigation_overlay

        chapters = [{"index": i, "title": f"Chapter {i+1}"} for i in range(25)]
        bookmarks = [{"name": f"Bookmark {i+1}", "position": f"Page {i}"} for i in range(15)]

        # Generate navigation overlay with pagination
        html = generate_navigation_overlay(
            chapters=chapters,
            bookmarks=bookmarks,
            active_tab="contents",
            page_size=(800, 1200),
            toc_page=1,
            toc_items_per_page=10,
            bookmarks_page=0
        )

        # Should show chapters 11-20 on page 2
        self.assertIn("11. Chapter 11", html)
        self.assertIn("20. Chapter 20", html)
        self.assertNotIn("10. Chapter 10", html)
        self.assertNotIn("21. Chapter 21", html)

    def test_bookmarks_pagination(self):
        """Test pagination works for bookmarks tab too"""
        from dreader.html_generator import generate_navigation_overlay

        chapters = [{"index": i, "title": f"Chapter {i+1}"} for i in range(5)]
        bookmarks = [{"name": f"Bookmark {i+1}", "position": f"Page {i}"} for i in range(25)]

        # Generate navigation overlay with bookmarks on page 2
        html = generate_navigation_overlay(
            chapters=chapters,
            bookmarks=bookmarks,
            active_tab="bookmarks",
            page_size=(800, 1200),
            toc_page=0,
            toc_items_per_page=10,
            bookmarks_page=1
        )

        # Should show bookmarks 11-20 on page 2
        self.assertIn("Bookmark 11", html)
        self.assertIn("Bookmark 20", html)
        self.assertNotIn("Bookmark 10", html)
        self.assertNotIn("Bookmark 21", html)

    def test_pagination_handles_empty_list(self):
        """Test pagination handles empty chapter list gracefully"""
        from dreader.html_generator import generate_toc_overlay

        chapters = []
        html = generate_toc_overlay(chapters, page_size=(800, 1200), toc_page=0, toc_items_per_page=10)

        self.assertIn("0 chapters", html)
        self.assertNotIn("page:prev", html)
        self.assertNotIn("page:next", html)


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