"""
Tests for the ereader manager components.

This module tests:
- BookmarkManager: Bookmark and position persistence
- EreaderLayoutManager: High-level ereader interface
- create_ereader_manager: Convenience function
"""

import pytest
import json
from pathlib import Path

from pyWebLayout.layout.ereader_manager import (
    BookmarkManager,
    EreaderLayoutManager,
    create_ereader_manager
)
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.style.page_style import PageStyle


# ============================================================================
# Fixtures
# ============================================================================

@pytest.fixture
def temp_bookmarks_dir(tmp_path):
    """Create a temporary directory for bookmarks."""
    bookmarks_dir = tmp_path / "bookmarks"
    bookmarks_dir.mkdir()
    return str(bookmarks_dir)


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


@pytest.fixture
def sample_blocks(sample_font):
    """Create sample document blocks."""
    blocks = []

    # Heading
    h1 = Heading(HeadingLevel.H1, sample_font)
    h1.add_word(Word("Chapter", sample_font))
    h1.add_word(Word("One", sample_font))
    blocks.append(h1)

    # Paragraphs
    for i in range(5):
        p = Paragraph(sample_font)
        p.add_word(Word("Paragraph", sample_font))
        p.add_word(Word(f"{i}", sample_font))
        blocks.append(p)

    return blocks


@pytest.fixture
def sample_position():
    """Create a sample rendering position."""
    return RenderingPosition(
        chapter_index=1,
        block_index=5,
        word_index=10
    )


# ============================================================================
# BookmarkManager Tests
# ============================================================================

class TestBookmarkManager:
    """Tests for the BookmarkManager class."""

    def test_initialization(self, temp_bookmarks_dir):
        """Test BookmarkManager initialization."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        assert manager.document_id == "test_doc"
        assert manager.bookmarks_dir == Path(temp_bookmarks_dir)
        assert manager.bookmarks_file.exists() or True  # May not exist yet
        assert isinstance(manager._bookmarks, dict)

    def test_initialization_creates_directory(self, tmp_path):
        """Test that initialization creates bookmarks directory if needed."""
        bookmarks_dir = str(tmp_path / "new_bookmarks")

        BookmarkManager("test_doc", bookmarks_dir)

        assert Path(bookmarks_dir).exists()
        assert Path(bookmarks_dir).is_dir()

    def test_add_bookmark(self, temp_bookmarks_dir, sample_position):
        """Test adding a bookmark."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        manager.add_bookmark("Chapter 1", sample_position)

        # Verify bookmark was added
        bookmark = manager.get_bookmark("Chapter 1")
        assert bookmark is not None
        assert bookmark == sample_position

    def test_add_multiple_bookmarks(self, temp_bookmarks_dir):
        """Test adding multiple bookmarks."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        pos1 = RenderingPosition(chapter_index=1, block_index=5)
        pos2 = RenderingPosition(chapter_index=2, block_index=10)
        pos3 = RenderingPosition(chapter_index=3, block_index=15)

        manager.add_bookmark("Bookmark 1", pos1)
        manager.add_bookmark("Bookmark 2", pos2)
        manager.add_bookmark("Bookmark 3", pos3)

        assert len(manager._bookmarks) == 3
        assert manager.get_bookmark("Bookmark 1") == pos1
        assert manager.get_bookmark("Bookmark 2") == pos2
        assert manager.get_bookmark("Bookmark 3") == pos3

    def test_remove_bookmark(self, temp_bookmarks_dir, sample_position):
        """Test removing a bookmark."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        manager.add_bookmark("Test", sample_position)
        assert manager.get_bookmark("Test") is not None

        result = manager.remove_bookmark("Test")

        assert result is True
        assert manager.get_bookmark("Test") is None

    def test_remove_nonexistent_bookmark(self, temp_bookmarks_dir):
        """Test removing a bookmark that doesn't exist."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        result = manager.remove_bookmark("Nonexistent")

        assert result is False

    def test_get_bookmark(self, temp_bookmarks_dir, sample_position):
        """Test getting a bookmark."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        manager.add_bookmark("Test", sample_position)

        retrieved = manager.get_bookmark("Test")

        assert retrieved == sample_position

    def test_get_nonexistent_bookmark(self, temp_bookmarks_dir):
        """Test getting a bookmark that doesn't exist."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        result = manager.get_bookmark("Nonexistent")

        assert result is None

    def test_list_bookmarks(self, temp_bookmarks_dir):
        """Test listing all bookmarks."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        pos1 = RenderingPosition(chapter_index=1, block_index=5)
        pos2 = RenderingPosition(chapter_index=2, block_index=10)

        manager.add_bookmark("First", pos1)
        manager.add_bookmark("Second", pos2)

        bookmarks = manager.list_bookmarks()

        assert len(bookmarks) == 2
        assert ("First", pos1) in bookmarks
        assert ("Second", pos2) in bookmarks

    def test_list_bookmarks_empty(self, temp_bookmarks_dir):
        """Test listing bookmarks when none exist."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        bookmarks = manager.list_bookmarks()

        assert bookmarks == []

    def test_save_reading_position(self, temp_bookmarks_dir, sample_position):
        """Test saving current reading position."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        manager.save_reading_position(sample_position)

        # Verify file was created
        assert manager.position_file.exists()

        # Verify content
        with open(manager.position_file, 'r') as f:
            data = json.load(f)

        assert data['chapter_index'] == sample_position.chapter_index
        assert data['block_index'] == sample_position.block_index

    def test_load_reading_position(self, temp_bookmarks_dir, sample_position):
        """Test loading saved reading position."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        manager.save_reading_position(sample_position)

        loaded = manager.load_reading_position()

        assert loaded is not None
        assert loaded == sample_position

    def test_load_reading_position_nonexistent(self, temp_bookmarks_dir):
        """Test loading reading position when file doesn't exist."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        loaded = manager.load_reading_position()

        assert loaded is None

    def test_bookmark_persistence(self, temp_bookmarks_dir, sample_position):
        """Test that bookmarks persist across manager instances."""
        # Create first manager and add bookmark
        manager1 = BookmarkManager("test_doc", temp_bookmarks_dir)
        manager1.add_bookmark("Persistent", sample_position)

        # Create second manager and verify bookmark exists
        manager2 = BookmarkManager("test_doc", temp_bookmarks_dir)
        loaded = manager2.get_bookmark("Persistent")

        assert loaded is not None
        assert loaded == sample_position

    def test_position_persistence(self, temp_bookmarks_dir, sample_position):
        """Test that reading position persists across manager instances."""
        # Save with first manager
        manager1 = BookmarkManager("test_doc", temp_bookmarks_dir)
        manager1.save_reading_position(sample_position)

        # Load with second manager
        manager2 = BookmarkManager("test_doc", temp_bookmarks_dir)
        loaded = manager2.load_reading_position()

        assert loaded == sample_position

    def test_corrupt_bookmarks_file(self, temp_bookmarks_dir):
        """Test handling of corrupt bookmarks file."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        # Write corrupt JSON
        with open(manager.bookmarks_file, 'w') as f:
            f.write("{ invalid json }}")

        # Should handle gracefully and load empty bookmarks
        manager2 = BookmarkManager("test_doc", temp_bookmarks_dir)
        assert manager2._bookmarks == {}

    def test_corrupt_position_file(self, temp_bookmarks_dir):
        """Test handling of corrupt position file."""
        manager = BookmarkManager("test_doc", temp_bookmarks_dir)

        # Write corrupt JSON
        with open(manager.position_file, 'w') as f:
            f.write("{ invalid json }}")

        # Should handle gracefully
        loaded = manager.load_reading_position()
        assert loaded is None


# ============================================================================
# EreaderLayoutManager Tests
# ============================================================================

class TestEreaderLayoutManager:
    """Tests for the EreaderLayoutManager class."""

    def test_initialization(self, sample_blocks, temp_bookmarks_dir):
        """Test EreaderLayoutManager initialization."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            document_id="test_doc",
            bookmarks_dir=temp_bookmarks_dir
        )

        assert manager.blocks == sample_blocks
        assert manager.page_size == (800, 600)
        assert manager.document_id == "test_doc"
        assert manager.font_scale == 1.0
        assert isinstance(manager.current_position, RenderingPosition)

    def test_initialization_with_custom_page_style(
            self, sample_blocks, temp_bookmarks_dir):
        """Test initialization with custom page style."""
        custom_style = PageStyle()

        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            page_style=custom_style,
            bookmarks_dir=temp_bookmarks_dir
        )

        assert manager.page_style == custom_style

    def test_initialization_loads_saved_position(
            self, sample_blocks, temp_bookmarks_dir):
        """Test that initialization loads saved reading position."""
        # Save a position first
        bookmark_mgr = BookmarkManager("test_doc", temp_bookmarks_dir)
        saved_pos = RenderingPosition(chapter_index=2, block_index=10)
        bookmark_mgr.save_reading_position(saved_pos)

        # Create manager - should load saved position
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            document_id="test_doc",
            bookmarks_dir=temp_bookmarks_dir
        )

        assert manager.current_position == saved_pos

    def test_get_current_page(self, sample_blocks, temp_bookmarks_dir):
        """Test getting the current page."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        page = manager.get_current_page()

        from pyWebLayout.concrete.page import Page
        assert isinstance(page, Page)

    def test_next_page(self, sample_blocks, temp_bookmarks_dir):
        """Test advancing to next page."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        initial_pos = manager.current_position.copy()

        next_page = manager.next_page()

        # Position should have advanced
        assert manager.current_position != initial_pos or next_page is None

    def test_next_page_at_end(self, sample_blocks, temp_bookmarks_dir):
        """Test next_page when at end of document."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        # Move to end
        manager.current_position = RenderingPosition(
            block_index=len(sample_blocks) + 100
        )

        result = manager.next_page()

        # Should return None at end
        assert result is None

    def test_previous_page(self, sample_blocks, temp_bookmarks_dir):
        """Test going to previous page."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        # Move forward first
        manager.current_position = RenderingPosition(block_index=3)

        prev_page = manager.previous_page()

        # Should return a page or None if at beginning
        from pyWebLayout.concrete.page import Page
        assert prev_page is None or isinstance(prev_page, Page)

    def test_previous_page_at_beginning(self, sample_blocks, temp_bookmarks_dir):
        """Test previous_page when at beginning of document."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        # At beginning
        manager.current_position = RenderingPosition()

        result = manager.previous_page()

        assert result is None

    def test_jump_to_position(self, sample_blocks, temp_bookmarks_dir):
        """Test jumping to a specific position."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        target_pos = RenderingPosition(chapter_index=1, block_index=3)

        page = manager.jump_to_position(target_pos)

        assert manager.current_position == target_pos
        from pyWebLayout.concrete.page import Page
        assert isinstance(page, Page)

    def test_jump_to_chapter(self, sample_blocks, temp_bookmarks_dir):
        """Test jumping to a chapter by title."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        page = manager.jump_to_chapter("Chapter One")

        # May return page or None if chapter not found
        from pyWebLayout.concrete.page import Page
        assert page is None or isinstance(page, Page)

    def test_jump_to_chapter_not_found(self, sample_blocks, temp_bookmarks_dir):
        """Test jumping to non-existent chapter."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        result = manager.jump_to_chapter("Nonexistent Chapter")

        assert result is None

    def test_jump_to_chapter_index(self, sample_blocks, temp_bookmarks_dir):
        """Test jumping to chapter by index."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        page = manager.jump_to_chapter_index(0)

        from pyWebLayout.concrete.page import Page
        assert page is None or isinstance(page, Page)

    def test_jump_to_chapter_index_invalid(self, sample_blocks, temp_bookmarks_dir):
        """Test jumping to invalid chapter index."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        result = manager.jump_to_chapter_index(999)

        assert result is None

    def test_set_font_scale(self, sample_blocks, temp_bookmarks_dir):
        """Test changing font scale."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        page = manager.set_font_scale(1.5)

        assert manager.font_scale == 1.5
        from pyWebLayout.concrete.page import Page
        assert isinstance(page, Page)

    def test_set_font_scale_same(self, sample_blocks, temp_bookmarks_dir):
        """Test setting font scale to same value."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        manager.set_font_scale(1.0)

        assert manager.font_scale == 1.0

    def test_get_font_scale(self, sample_blocks, temp_bookmarks_dir):
        """Test getting current font scale."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        scale = manager.get_font_scale()

        assert scale == 1.0

    def test_get_table_of_contents(self, sample_blocks, temp_bookmarks_dir):
        """Test getting table of contents."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        toc = manager.get_table_of_contents()

        assert isinstance(toc, list)

    def test_get_current_chapter(self, sample_blocks, temp_bookmarks_dir):
        """Test getting current chapter info."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        chapter = manager.get_current_chapter()

        # May be None or ChapterInfo
        assert chapter is None or hasattr(chapter, 'title')

    def test_add_bookmark(self, sample_blocks, temp_bookmarks_dir):
        """Test adding a bookmark at current position."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        result = manager.add_bookmark("Test Bookmark")

        assert result is True
        # Verify bookmark was added
        bookmark = manager.bookmark_manager.get_bookmark("Test Bookmark")
        assert bookmark is not None

    def test_remove_bookmark(self, sample_blocks, temp_bookmarks_dir):
        """Test removing a bookmark."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        manager.add_bookmark("Test")
        result = manager.remove_bookmark("Test")

        assert result is True

    def test_jump_to_bookmark(self, sample_blocks, temp_bookmarks_dir):
        """Test jumping to a bookmark."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        # Add bookmark at specific position
        manager.current_position = RenderingPosition(block_index=3)
        manager.add_bookmark("Test Position")

        # Move away
        manager.current_position = RenderingPosition(block_index=0)

        # Jump to bookmark
        page = manager.jump_to_bookmark("Test Position")

        from pyWebLayout.concrete.page import Page
        assert isinstance(page, Page)
        assert manager.current_position.block_index == 3

    def test_jump_to_nonexistent_bookmark(self, sample_blocks, temp_bookmarks_dir):
        """Test jumping to non-existent bookmark."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        result = manager.jump_to_bookmark("Nonexistent")

        assert result is None

    def test_list_bookmarks(self, sample_blocks, temp_bookmarks_dir):
        """Test listing all bookmarks."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        manager.add_bookmark("Bookmark 1")
        manager.add_bookmark("Bookmark 2")

        bookmarks = manager.list_bookmarks()

        assert len(bookmarks) == 2

    def test_get_reading_progress(self, sample_blocks, temp_bookmarks_dir):
        """Test getting reading progress percentage."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        progress = manager.get_reading_progress()

        assert 0.0 <= progress <= 1.0

    def test_get_reading_progress_at_end(self, sample_blocks, temp_bookmarks_dir):
        """Test reading progress at end of document."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        manager.current_position = RenderingPosition(
            block_index=len(sample_blocks) - 1
        )

        progress = manager.get_reading_progress()

        assert progress == 1.0

    def test_get_reading_progress_empty_document(self, temp_bookmarks_dir):
        """Test reading progress with empty document."""
        manager = EreaderLayoutManager(
            [],
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        progress = manager.get_reading_progress()

        assert progress == 0.0

    def test_get_position_info(self, sample_blocks, temp_bookmarks_dir):
        """Test getting detailed position information."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        info = manager.get_position_info()

        assert isinstance(info, dict)
        assert 'position' in info
        assert 'chapter' in info
        assert 'progress' in info
        assert 'font_scale' in info
        assert 'page_size' in info

    def test_get_cache_stats(self, sample_blocks, temp_bookmarks_dir):
        """Test getting cache statistics."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        stats = manager.get_cache_stats()

        assert isinstance(stats, dict)

    def test_position_changed_callback(self, sample_blocks, temp_bookmarks_dir):
        """Test position changed callback."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        callback_called = []

        def callback(position):
            callback_called.append(position)

        manager.set_position_changed_callback(callback)
        manager.jump_to_position(RenderingPosition(block_index=3))

        assert len(callback_called) > 0

    def test_chapter_changed_callback(self, sample_blocks, temp_bookmarks_dir):
        """Test chapter changed callback."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            bookmarks_dir=temp_bookmarks_dir
        )

        callback_called = []

        def callback(chapter):
            callback_called.append(chapter)

        manager.set_chapter_changed_callback(callback)
        manager.jump_to_position(RenderingPosition(block_index=3))

        assert len(callback_called) > 0

    def test_shutdown(self, sample_blocks, temp_bookmarks_dir):
        """Test shutdown saves position."""
        manager = EreaderLayoutManager(
            sample_blocks,
            page_size=(800, 600),
            document_id="test_doc",
            bookmarks_dir=temp_bookmarks_dir
        )

        test_pos = RenderingPosition(block_index=5)
        manager.current_position = test_pos

        manager.shutdown()

        # Verify position was saved
        bookmark_mgr = BookmarkManager("test_doc", temp_bookmarks_dir)
        loaded = bookmark_mgr.load_reading_position()
        assert loaded == test_pos


# ============================================================================
# Convenience Function Tests
# ============================================================================

class TestCreateEreaderManager:
    """Tests for the create_ereader_manager convenience function."""

    def test_create_with_defaults(self, sample_blocks):
        """Test creating manager with default parameters."""
        manager = create_ereader_manager(
            sample_blocks,
            page_size=(800, 600)
        )

        assert isinstance(manager, EreaderLayoutManager)
        assert manager.blocks == sample_blocks
        assert manager.page_size == (800, 600)
        assert manager.document_id == "default"

    def test_create_with_custom_document_id(self, sample_blocks):
        """Test creating manager with custom document ID."""
        manager = create_ereader_manager(
            sample_blocks,
            page_size=(800, 600),
            document_id="custom_doc"
        )

        assert manager.document_id == "custom_doc"

    def test_create_with_kwargs(self, sample_blocks, tmp_path):
        """Test creating manager with additional kwargs."""
        bookmarks_dir = str(tmp_path / "custom_bookmarks")

        manager = create_ereader_manager(
            sample_blocks,
            page_size=(800, 600),
            document_id="test",
            buffer_size=10,
            bookmarks_dir=bookmarks_dir
        )

        assert isinstance(manager, EreaderLayoutManager)


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