"""
Tests for navigation consistency in the ereader manager.

This module tests that forward and backward navigation are consistent
and produce the same content when returning to previously visited positions.
"""

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


@pytest.fixture
def sample_font():
    """Create a standard font for testing."""
    return Font(font_size=12, colour=(0, 0, 0))


@pytest.fixture
def large_document(sample_font):
    """Create a large document with many paragraphs to ensure multiple pages."""
    blocks = []

    # Add multiple headings and paragraphs to create a substantial document
    for section in range(5):
        # Add a heading
        heading = Heading(HeadingLevel.H2, sample_font)
        heading.add_word(Word(f"Section", sample_font))
        heading.add_word(Word(f"{section + 1}", sample_font))
        blocks.append(heading)

        # Add multiple paragraphs per section
        for para_num in range(10):
            p = Paragraph(sample_font)
            # Add enough words to make substantial paragraphs
            for word_num in range(15):
                p.add_word(Word(f"Word_{section}_{para_num}_{word_num}", sample_font))
            blocks.append(p)

    return blocks


def extract_text_content(page: Page) -> str:
    """
    Extract text content from a rendered page.

    This provides a way to compare pages semantically rather than pixel-perfect.
    Pages contain Line objects as children, and Lines contain Text objects.
    """
    text_content = []

    # Get children from the page (these are Line objects)
    if hasattr(page, 'children'):
        for child in page.children:
            # Each Line contains Text objects
            if hasattr(child, 'text_objects') and child.text_objects:
                for text_obj in child.text_objects:
                    if hasattr(text_obj, '_text'):
                        text_content.append(text_obj._text)
                    elif hasattr(text_obj, 'text'):
                        text_content.append(text_obj.text)

    # Join all text with spaces
    return ' '.join(text_content)


def get_page_summary(page: Page) -> dict:
    """
    Get a summary of page content for comparison.

    Returns a dictionary with:
    - text_content: All text on the page
    - child_count: Number of child objects (Lines) on the page
    - approximate_content_size: Rough measure of content amount
    """
    text = extract_text_content(page)

    child_count = 0
    if hasattr(page, 'children'):
        child_count = len(page.children)

    return {
        'text_content': text,
        'child_count': child_count,
        'approximate_content_size': len(text),
        'has_content': len(text) > 0
    }


class TestNavigationConsistency:
    """Tests for forward/backward navigation consistency."""

    def test_forward_backward_consistency(self, large_document, tmp_path):
        """
        Test that navigating forward then backward returns to the same content.
        """
        manager = EreaderLayoutManager(
            large_document,
            page_size=(600, 800),
            document_id="test_consistency",
            bookmarks_dir=str(tmp_path)
        )

        # Navigate forward and capture page summaries
        forward_pages = []
        positions = [manager.current_position.copy()]

        for i in range(5):
            page = manager.get_current_page()
            forward_pages.append(get_page_summary(page))

            next_page = manager.next_page()
            if next_page is None:
                break
            positions.append(manager.current_position.copy())

        # Now navigate backward
        backward_pages = []
        for i in range(len(positions) - 1):
            prev_page = manager.previous_page()
            assert prev_page is not None, f"Backward navigation {i} failed"

            page = manager.get_current_page()
            backward_pages.append(get_page_summary(page))

        # Reverse backward_pages to align with forward_pages
        backward_pages.reverse()

        # Compare content (excluding the last page since we didn't go back to it)
        for i in range(len(backward_pages)):
            forward_summary = forward_pages[i]
            backward_summary = backward_pages[i]

            # Check that content is the same
            assert forward_summary['text_content'] == backward_summary['text_content'], \
                f"Page {i} content differs between forward and backward navigation"

            # Check that we have content
            assert forward_summary['has_content'], f"Page {i} has no content"

    def test_forward_backward_forward_consistency(self, large_document, tmp_path):
        """
        Test navigating forward, backward, then forward again produces consistent content.
        """
        manager = EreaderLayoutManager(
            large_document,
            page_size=(600, 800),
            document_id="test_fbf_consistency",
            bookmarks_dir=str(tmp_path)
        )

        # Navigate forward 5 pages
        forward_first = []
        for i in range(5):
            page = manager.get_current_page()
            forward_first.append(get_page_summary(page))
            next_page = manager.next_page()
            if next_page is None:
                break

        # Navigate backward 3 pages
        for i in range(3):
            prev_page = manager.previous_page()
            assert prev_page is not None, f"Backward navigation {i} failed"

        # Navigate forward 3 pages again
        forward_second = []
        for i in range(3):
            page = manager.get_current_page()
            forward_second.append(get_page_summary(page))
            next_page = manager.next_page()
            if next_page is None:
                break

        # Compare the content - the last 3 pages of first forward pass
        # should match the 3 pages of second forward pass
        for i in range(min(3, len(forward_second))):
            original_idx = len(forward_first) - 3 + i
            if original_idx >= 0:
                assert forward_first[original_idx]['text_content'] == forward_second[i]['text_content'], \
                    f"Page content differs at position {i}"

    def test_position_consistency(self, large_document, tmp_path):
        """
        Test that positions are consistent when navigating forward and backward.
        """
        manager = EreaderLayoutManager(
            large_document,
            page_size=(600, 800),
            document_id="test_position_consistency",
            bookmarks_dir=str(tmp_path)
        )

        # Navigate forward and record positions
        forward_positions = [manager.current_position.copy()]
        for i in range(5):
            next_page = manager.next_page()
            if next_page is None:
                break
            forward_positions.append(manager.current_position.copy())

        # Navigate backward and record positions
        backward_positions = [manager.current_position.copy()]
        for i in range(len(forward_positions) - 1):
            prev_page = manager.previous_page()
            assert prev_page is not None, f"Backward navigation {i} failed"
            backward_positions.append(manager.current_position.copy())

        # Reverse backward positions to align with forward
        backward_positions.reverse()

        # Verify positions match
        for i in range(min(len(forward_positions), len(backward_positions))):
            assert forward_positions[i] == backward_positions[i], \
                f"Position {i} differs: forward={forward_positions[i]}, backward={backward_positions[i]}"

    def test_navigation_after_reload(self, large_document, tmp_path):
        """
        Test that navigation works correctly after closing and reopening the document.

        This is the critical test for the bug we fixed: backward and forward navigation
        after reloading should work without cached position mappings.
        """
        document_id = "test_reload_consistency"

        # Session 1: Navigate forward to middle of document
        manager1 = EreaderLayoutManager(
            large_document,
            page_size=(600, 800),
            document_id=document_id,
            bookmarks_dir=str(tmp_path)
        )

        # Navigate forward 3 pages (not too far, so we have room to navigate)
        for i in range(3):
            next_page = manager1.next_page()
            if next_page is None:
                break

        saved_position = manager1.current_position.copy()
        manager1.shutdown()
        del manager1

        # Session 2: Reload and test navigation
        manager2 = EreaderLayoutManager(
            large_document,
            page_size=(600, 800),
            document_id=document_id,
            bookmarks_dir=str(tmp_path)
        )

        # Verify position was restored
        assert manager2.current_position == saved_position, \
            "Position was not correctly restored after reload"

        # Test 1: Backward navigation works after reload (THE KEY BUG FIX)
        current_before = manager2.current_position.copy()
        prev_page = manager2.previous_page()
        assert prev_page is not None, \
            "Backward navigation after reload returned None (bug still exists!)"
        assert manager2.current_position != current_before, \
            "Backward navigation after reload did not change position"

        # Test 2: Forward navigation works after backward
        current_before = manager2.current_position.copy()
        next_page = manager2.next_page()
        assert next_page is not None, \
            "Forward navigation after backward returned None"
        assert manager2.current_position != current_before, \
            "Forward navigation after backward did not change position"

        # Test 3: Can go backward again
        current_before = manager2.current_position.copy()
        prev_page = manager2.previous_page()
        assert prev_page is not None, \
            "Second backward navigation returned None"
        assert manager2.current_position != current_before, \
            "Second backward navigation did not change position"

        manager2.shutdown()

    def test_multiple_navigation_cycles(self, large_document, tmp_path):
        """
        Test multiple cycles of forward and backward navigation.
        """
        manager = EreaderLayoutManager(
            large_document,
            page_size=(600, 800),
            document_id="test_cycles",
            bookmarks_dir=str(tmp_path)
        )

        # Perform multiple cycles
        for cycle in range(3):
            start_position = manager.current_position.copy()

            # Go forward 3 pages
            for i in range(3):
                next_page = manager.next_page()
                assert next_page is not None, \
                    f"Cycle {cycle}: Forward navigation {i} failed"

            # Go backward 3 pages
            for i in range(3):
                prev_page = manager.previous_page()
                assert prev_page is not None, \
                    f"Cycle {cycle}: Backward navigation {i} failed"

            # Should be back at start position
            assert manager.current_position == start_position, \
                f"Cycle {cycle}: Did not return to start position"

    def test_content_boundaries(self, large_document, tmp_path):
        """
        Test navigation at document boundaries (beginning and near end).
        """
        manager = EreaderLayoutManager(
            large_document,
            page_size=(600, 800),
            document_id="test_boundaries",
            bookmarks_dir=str(tmp_path)
        )

        # Test at beginning
        initial_position = manager.current_position.copy()
        prev_page = manager.previous_page()
        assert prev_page is None, "Should not be able to go back from beginning"
        assert manager.current_position == initial_position, \
            "Position should not change when at beginning"

        # Navigate forward to near the end
        page_count = 0
        max_pages = 100  # Safety limit
        while page_count < max_pages:
            next_page = manager.next_page()
            if next_page is None:
                break
            page_count += 1

        # We should have moved from the beginning
        assert page_count > 0, "Should have moved at least one page forward"

        # Test going back from near the end
        end_position = manager.current_position.copy()
        prev_page = manager.previous_page()

        # Should be able to go back
        if page_count > 0:  # If we moved forward, we should be able to go back
            assert prev_page is not None, "Should be able to go back from end"
            assert manager.current_position != end_position, \
                "Position should change when going back from end"


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
