"""
Unit tests for Image block rendering in the ereader layout system.

Tests cover:
- Image block layout on pages
- Navigation with images (next/previous page)
- Images at different positions (start, middle, end)
- Cover page detection and handling
- Multi-page scenarios with images
"""

import unittest
import tempfile
import shutil
from pathlib import Path

from pyWebLayout.layout.ereader_manager import EreaderLayoutManager
from pyWebLayout.layout.ereader_layout import BidirectionalLayouter, RenderingPosition
from pyWebLayout.abstract.block import Paragraph, Heading, HeadingLevel, Image
from pyWebLayout.abstract.inline import Word
from pyWebLayout.concrete.page import Page
from pyWebLayout.style import Font
from pyWebLayout.style.page_style import PageStyle


class TestImageBlockLayout(unittest.TestCase):
    """Test basic Image block layout functionality."""

    def setUp(self):
        """Set up test fixtures."""
        self.base_font = Font(font_size=14)
        self.page_size = (400, 600)
        self.page_style = PageStyle(padding=(20, 20, 20, 20))

    def test_layout_image_block_on_page(self):
        """Test that Image blocks can be laid out on pages."""
        # Create a simple document with an image
        blocks = [
            Image(source="test.jpg", alt_text="Test Image", width=200, height=300)
        ]

        layouter = BidirectionalLayouter(blocks, self.page_style)
        position = RenderingPosition()

        # Render page with image
        page, next_pos = layouter.render_page_forward(position, font_scale=1.0)

        # Should successfully render the page
        self.assertIsNotNone(page)
        self.assertIsInstance(page, Page)

        # Position should advance past the image block
        self.assertEqual(next_pos.block_index, 1)

    def test_image_block_advances_position(self):
        """Test that rendering an image block correctly advances the position."""
        blocks = [
            Image(source="img1.jpg", alt_text="Image 1"),
            Paragraph(self.base_font)
        ]
        # Add some words to the paragraph
        blocks[1].add_word(Word("Text after image", self.base_font))

        layouter = BidirectionalLayouter(blocks, self.page_style)
        position = RenderingPosition(block_index=0)

        # Render page starting at image
        page, next_pos = layouter.render_page_forward(position, font_scale=1.0)

        # Position should either:
        # 1. Move to next block if image was successfully laid out, OR
        # 2. Stay at same position if image couldn't fit/render
        # In either case, the layouter should handle it gracefully
        self.assertIsNotNone(page)
        self.assertGreaterEqual(next_pos.block_index, 0)

        # If the image is at start and can't render, it may skip to next block anyway
        # The important thing is the system doesn't crash


class TestImageNavigationScenarios(unittest.TestCase):
    """Test navigation scenarios with images in different positions."""

    def setUp(self):
        """Set up test fixtures."""
        self.base_font = Font(font_size=14)
        self.page_size = (400, 600)
        self.page_style = PageStyle(padding=(20, 20, 20, 20))
        self.temp_dir = tempfile.mkdtemp()

    def tearDown(self):
        """Clean up temporary files."""
        shutil.rmtree(self.temp_dir, ignore_errors=True)

    def _create_paragraph(self, text: str) -> Paragraph:
        """Helper to create a paragraph with text."""
        para = Paragraph(self.base_font)
        para.add_word(Word(text, self.base_font))
        return para

    def test_next_page_with_image_on_second_page(self):
        """Test navigating to next page when an image is on the second page."""
        # Document structure: paragraph → image → paragraph
        blocks = [
            self._create_paragraph("First paragraph on page 1."),
            Image(source="middle.jpg", alt_text="Middle Image"),
            self._create_paragraph("Third paragraph after image.")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_image_nav",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # Start at beginning
        initial_pos = manager.current_position.block_index
        self.assertEqual(initial_pos, 0)

        # Navigate to next page
        next_page = manager.next_page()
        self.assertIsNotNone(next_page)

        # Position should have advanced
        self.assertGreater(manager.current_position.block_index, initial_pos)

    def test_previous_page_with_image_on_previous_page(self):
        """Test navigating back when previous page contains an image."""
        blocks = [
            self._create_paragraph("First paragraph."),
            Image(source="image1.jpg", alt_text="Image 1"),
            self._create_paragraph("Third paragraph."),
            self._create_paragraph("Fourth paragraph.")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_prev_image",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # Navigate forward to get past the image
        manager.next_page()
        manager.next_page()

        current_block = manager.current_position.block_index
        self.assertGreater(current_block, 0)

        # Navigate backward
        prev_page = manager.previous_page()
        self.assertIsNotNone(prev_page)

        # Should have moved to an earlier position
        self.assertLess(manager.current_position.block_index, current_block)

    def test_multiple_images_in_sequence(self):
        """Test document with multiple consecutive images."""
        blocks = [
            self._create_paragraph("Introduction text."),
            Image(source="img1.jpg", alt_text="Image 1"),
            Image(source="img2.jpg", alt_text="Image 2"),
            Image(source="img3.jpg", alt_text="Image 3"),
            self._create_paragraph("Text after images.")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_multi_images",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # Navigate through pages
        pages_rendered = 0
        max_pages = 10  # Safety limit

        while pages_rendered < max_pages:
            current_block = manager.current_position.block_index

            # Try to go to next page
            next_page = manager.next_page()

            if next_page is None:
                # Reached end
                break

            pages_rendered += 1

            # Position should advance
            self.assertGreaterEqual(
                manager.current_position.block_index,
                current_block,
                f"Position should advance or stay same, page {pages_rendered}"
            )

        # Should have rendered at least 2 pages
        self.assertGreaterEqual(pages_rendered, 1)

    def test_image_at_document_start(self):
        """Test document starting with an image (not as cover)."""
        blocks = [
            Image(source="start.jpg", alt_text="Start Image"),
            self._create_paragraph("Text after image.")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_image_start",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # First image should be detected as cover
        self.assertTrue(manager.has_cover())
        self.assertTrue(manager.is_on_cover())

        # Navigate past cover
        manager.next_page()

        # Should now be at the text
        self.assertFalse(manager.is_on_cover())
        # Should have skipped the image block (cover)
        self.assertEqual(manager.current_position.block_index, 1)

    def test_image_at_document_end(self):
        """Test document ending with an image."""
        blocks = [
            self._create_paragraph("First paragraph."),
            self._create_paragraph("Second paragraph."),
            Image(source="end.jpg", alt_text="End Image")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_image_end",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # Navigate to end
        page_count = 0
        max_pages = 10

        while page_count < max_pages:
            next_page = manager.next_page()
            if next_page is None:
                break
            page_count += 1

        # Should have successfully navigated through document including final image
        self.assertGreater(page_count, 0)

    def test_alternating_text_and_images(self):
        """Test document with alternating text and images."""
        blocks = [
            self._create_paragraph("Paragraph 1"),
            Image(source="img1.jpg", alt_text="Image 1"),
            self._create_paragraph("Paragraph 2"),
            Image(source="img2.jpg", alt_text="Image 2"),
            self._create_paragraph("Paragraph 3"),
            Image(source="img3.jpg", alt_text="Image 3"),
            self._create_paragraph("Paragraph 4")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_alternating",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # Track blocks visited
        blocks_visited = set()
        max_pages = 15

        for _ in range(max_pages):
            blocks_visited.add(manager.current_position.block_index)
            next_page = manager.next_page()
            if next_page is None:
                break

        # Should have visited multiple different blocks
        self.assertGreater(len(blocks_visited), 1)


class TestCoverPageWithImages(unittest.TestCase):
    """Test cover page detection and handling with images."""

    def setUp(self):
        """Set up test fixtures."""
        self.base_font = Font(font_size=14)
        self.page_size = (400, 600)
        self.page_style = PageStyle(padding=(20, 20, 20, 20))
        self.temp_dir = tempfile.mkdtemp()

    def tearDown(self):
        """Clean up temporary files."""
        shutil.rmtree(self.temp_dir, ignore_errors=True)

    def _create_paragraph(self, text: str) -> Paragraph:
        """Helper to create a paragraph with text."""
        para = Paragraph(self.base_font)
        para.add_word(Word(text, self.base_font))
        return para

    def test_cover_page_detected_from_first_image(self):
        """Test that first image is detected as cover."""
        blocks = [
            Image(source="cover.jpg", alt_text="Cover"),
            self._create_paragraph("Chapter text.")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_cover_detection",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # Should detect cover
        self.assertTrue(manager.has_cover())
        self.assertTrue(manager.is_on_cover())

    def test_no_cover_when_first_block_is_text(self):
        """Test that cover is not detected when first block is text."""
        blocks = [
            self._create_paragraph("First paragraph."),
            Image(source="image.jpg", alt_text="Not a cover"),
            self._create_paragraph("Second paragraph.")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_no_cover",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # Should NOT detect cover
        self.assertFalse(manager.has_cover())
        self.assertFalse(manager.is_on_cover())

    def test_navigation_from_cover_skips_image_block(self):
        """Test that next_page from cover skips the cover image block."""
        blocks = [
            Image(source="cover.jpg", alt_text="Cover"),
            self._create_paragraph("First content paragraph."),
            self._create_paragraph("Second content paragraph.")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_cover_skip",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # Start on cover
        self.assertTrue(manager.is_on_cover())
        self.assertEqual(manager.current_position.block_index, 0)

        # Navigate past cover
        manager.next_page()

        # Should skip cover image block (index 0) and go to first content (index 1)
        self.assertFalse(manager.is_on_cover())
        self.assertEqual(manager.current_position.block_index, 1)

    def test_previous_page_returns_to_cover(self):
        """Test that previous_page from first content returns to cover."""
        blocks = [
            Image(source="cover.jpg", alt_text="Cover"),
            self._create_paragraph("Content text.")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_back_to_cover",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # Navigate past cover
        manager.next_page()
        self.assertFalse(manager.is_on_cover())

        # Go back
        manager.previous_page()

        # Should be back on cover
        self.assertTrue(manager.is_on_cover())

    def test_jump_to_cover_from_middle(self):
        """Test jumping to cover from middle of document."""
        blocks = [
            Image(source="cover.jpg", alt_text="Cover"),
            self._create_paragraph("Paragraph 1"),
            self._create_paragraph("Paragraph 2"),
            self._create_paragraph("Paragraph 3")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_jump_cover",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # Navigate to middle
        manager.next_page()
        manager.next_page()
        self.assertFalse(manager.is_on_cover())

        # Jump to cover
        cover_page = manager.jump_to_cover()

        self.assertIsNotNone(cover_page)
        self.assertTrue(manager.is_on_cover())


class TestImageBlockPositionTracking(unittest.TestCase):
    """Test position tracking with Image blocks."""

    def setUp(self):
        """Set up test fixtures."""
        self.base_font = Font(font_size=14)
        self.page_size = (400, 600)
        self.page_style = PageStyle(padding=(20, 20, 20, 20))
        self.temp_dir = tempfile.mkdtemp()

    def tearDown(self):
        """Clean up temporary files."""
        shutil.rmtree(self.temp_dir, ignore_errors=True)

    def _create_paragraph(self, text: str) -> Paragraph:
        """Helper to create a paragraph with text."""
        para = Paragraph(self.base_font)
        para.add_word(Word(text, self.base_font))
        return para

    def test_position_info_includes_image_blocks(self):
        """Test that position info correctly handles image blocks."""
        blocks = [
            self._create_paragraph("Text 1"),
            Image(source="img.jpg", alt_text="Image"),
            self._create_paragraph("Text 2")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_pos_info",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # Get initial position info
        pos_info = manager.get_position_info()

        self.assertIn('position', pos_info)
        self.assertIn('block_index', pos_info['position'])
        self.assertEqual(pos_info['position']['block_index'], 0)

    def test_bookmark_image_position(self):
        """Test bookmarking at an image position."""
        blocks = [
            self._create_paragraph("Before image"),
            Image(source="bookmarked.jpg", alt_text="Bookmarked Image"),
            self._create_paragraph("After image")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_bookmark_image",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # Navigate to image position
        manager.next_page()

        # Add bookmark
        bookmark_name = "image_location"
        success = manager.add_bookmark(bookmark_name)
        self.assertTrue(success)

        # Navigate away
        manager.next_page()

        # Jump back to bookmark
        page = manager.jump_to_bookmark(bookmark_name)
        self.assertIsNotNone(page)

        # Should be at or near the image position
        # (exact position depends on how much fits on page)
        self.assertGreater(manager.current_position.block_index, 0)

    def test_reading_progress_with_images(self):
        """Test reading progress calculation with images in document."""
        blocks = [
            self._create_paragraph("Text 1"),
            Image(source="img1.jpg", alt_text="Image 1"),
            self._create_paragraph("Text 2"),
            Image(source="img2.jpg", alt_text="Image 2"),
            self._create_paragraph("Text 3")
        ]

        manager = EreaderLayoutManager(
            blocks=blocks,
            page_size=self.page_size,
            document_id="test_progress",
            page_style=self.page_style,
            bookmarks_dir=self.temp_dir
        )

        # At start
        progress_start = manager.get_reading_progress()
        self.assertEqual(progress_start, 0.0)

        # Navigate through document
        for _ in range(5):
            if manager.next_page() is None:
                break

        # Progress should have increased
        progress_end = manager.get_reading_progress()
        self.assertGreater(progress_end, progress_start)


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