"""
High-performance ereader layout manager with sub-second page rendering.

This module provides the main interface for ereader applications, combining
position tracking, font scaling, chapter navigation, and intelligent page buffering
into a unified, easy-to-use API.
"""

from __future__ import annotations
from typing import List, Dict, Optional, Tuple, Any, Callable
import json
import os
from pathlib import Path

from .ereader_layout import RenderingPosition, ChapterNavigator, ChapterInfo
from .page_buffer import BufferedPageRenderer
from pyWebLayout.abstract.block import Block, HeadingLevel
from pyWebLayout.concrete.page import Page
from pyWebLayout.style.page_style import PageStyle


class BookmarkManager:
    """
    Manages bookmarks and reading position persistence for ereader applications.
    """
    
    def __init__(self, document_id: str, bookmarks_dir: str = "bookmarks"):
        """
        Initialize bookmark manager.
        
        Args:
            document_id: Unique identifier for the document
            bookmarks_dir: Directory to store bookmark files
        """
        self.document_id = document_id
        self.bookmarks_dir = Path(bookmarks_dir)
        self.bookmarks_dir.mkdir(exist_ok=True)
        
        self.bookmarks_file = self.bookmarks_dir / f"{document_id}_bookmarks.json"
        self.position_file = self.bookmarks_dir / f"{document_id}_position.json"
        
        self._bookmarks: Dict[str, RenderingPosition] = {}
        self._load_bookmarks()
    
    def _load_bookmarks(self):
        """Load bookmarks from file"""
        if self.bookmarks_file.exists():
            try:
                with open(self.bookmarks_file, 'r') as f:
                    data = json.load(f)
                    self._bookmarks = {
                        name: RenderingPosition.from_dict(pos_data)
                        for name, pos_data in data.items()
                    }
            except Exception as e:
                print(f"Failed to load bookmarks: {e}")
                self._bookmarks = {}
    
    def _save_bookmarks(self):
        """Save bookmarks to file"""
        try:
            data = {
                name: position.to_dict()
                for name, position in self._bookmarks.items()
            }
            with open(self.bookmarks_file, 'w') as f:
                json.dump(data, f, indent=2)
        except Exception as e:
            print(f"Failed to save bookmarks: {e}")
    
    def add_bookmark(self, name: str, position: RenderingPosition):
        """
        Add a bookmark at the given position.
        
        Args:
            name: Bookmark name
            position: Position to bookmark
        """
        self._bookmarks[name] = position
        self._save_bookmarks()
    
    def remove_bookmark(self, name: str) -> bool:
        """
        Remove a bookmark.
        
        Args:
            name: Bookmark name to remove
            
        Returns:
            True if bookmark was removed, False if not found
        """
        if name in self._bookmarks:
            del self._bookmarks[name]
            self._save_bookmarks()
            return True
        return False
    
    def get_bookmark(self, name: str) -> Optional[RenderingPosition]:
        """
        Get a bookmark position.
        
        Args:
            name: Bookmark name
            
        Returns:
            Bookmark position or None if not found
        """
        return self._bookmarks.get(name)
    
    def list_bookmarks(self) -> List[Tuple[str, RenderingPosition]]:
        """
        Get all bookmarks.
        
        Returns:
            List of (name, position) tuples
        """
        return list(self._bookmarks.items())
    
    def save_reading_position(self, position: RenderingPosition):
        """
        Save the current reading position.
        
        Args:
            position: Current reading position
        """
        try:
            with open(self.position_file, 'w') as f:
                json.dump(position.to_dict(), f, indent=2)
        except Exception as e:
            print(f"Failed to save reading position: {e}")
    
    def load_reading_position(self) -> Optional[RenderingPosition]:
        """
        Load the last reading position.
        
        Returns:
            Last reading position or None if not found
        """
        if self.position_file.exists():
            try:
                with open(self.position_file, 'r') as f:
                    data = json.load(f)
                    return RenderingPosition.from_dict(data)
            except Exception as e:
                print(f"Failed to load reading position: {e}")
        return None


class EreaderLayoutManager:
    """
    High-level ereader layout manager providing a complete interface for ereader applications.
    
    Features:
    - Sub-second page rendering with intelligent buffering
    - Font scaling support
    - Chapter navigation
    - Bookmark management
    - Position persistence
    - Progress tracking
    """
    
    def __init__(self, 
                 blocks: List[Block], 
                 page_size: Tuple[int, int], 
                 document_id: str = "default",
                 buffer_size: int = 5,
                 page_style: Optional[PageStyle] = None,
                 bookmarks_dir: str = "bookmarks"):
        """
        Initialize the ereader layout manager.
        
        Args:
            blocks: Document blocks to render
            page_size: Page size (width, height) in pixels
            document_id: Unique identifier for the document (for bookmarks/position)
            buffer_size: Number of pages to cache in each direction
            page_style: Custom page styling (uses default if None)
            bookmarks_dir: Directory to store bookmark files
        """
        self.blocks = blocks
        self.page_size = page_size
        self.document_id = document_id
        
        # Initialize page style
        if page_style is None:
            page_style = PageStyle()
        self.page_style = page_style
        
        # Initialize core components
        self.renderer = BufferedPageRenderer(blocks, page_style, buffer_size, page_size)
        self.chapter_navigator = ChapterNavigator(blocks)
        self.bookmark_manager = BookmarkManager(document_id, bookmarks_dir)
        
        # Current state
        self.current_position = RenderingPosition()
        self.font_scale = 1.0
        
        # Load last reading position if available
        saved_position = self.bookmark_manager.load_reading_position()
        if saved_position:
            self.current_position = saved_position
        
        # Callbacks for UI updates
        self.position_changed_callback: Optional[Callable[[RenderingPosition], None]] = None
        self.chapter_changed_callback: Optional[Callable[[Optional[ChapterInfo]], None]] = None
    
    def set_position_changed_callback(self, callback: Callable[[RenderingPosition], None]):
        """Set callback for position changes"""
        self.position_changed_callback = callback
    
    def set_chapter_changed_callback(self, callback: Callable[[Optional[ChapterInfo]], None]):
        """Set callback for chapter changes"""
        self.chapter_changed_callback = callback
    
    def _notify_position_changed(self):
        """Notify UI of position change"""
        if self.position_changed_callback:
            self.position_changed_callback(self.current_position)
        
        # Check if chapter changed
        current_chapter = self.chapter_navigator.get_current_chapter(self.current_position)
        if self.chapter_changed_callback:
            self.chapter_changed_callback(current_chapter)
        
        # Auto-save reading position
        self.bookmark_manager.save_reading_position(self.current_position)
    
    def get_current_page(self) -> Page:
        """
        Get the page at the current reading position.
        
        Returns:
            Rendered page
        """
        page, _ = self.renderer.render_page(self.current_position, self.font_scale)
        return page
    
    def next_page(self) -> Optional[Page]:
        """
        Advance to the next page.
        
        Returns:
            Next page or None if at end of document
        """
        page, next_position = self.renderer.render_page(self.current_position, self.font_scale)
        
        # Check if we made progress
        if next_position != self.current_position:
            self.current_position = next_position
            self._notify_position_changed()
            return self.get_current_page()
        
        return None  # At end of document
    
    def previous_page(self) -> Optional[Page]:
        """
        Go to the previous page.
        
        Returns:
            Previous page or None if at beginning of document
        """
        if self._is_at_beginning():
            return None
        
        # Use backward rendering to find the previous page
        page, start_position = self.renderer.render_page_backward(self.current_position, self.font_scale)
        
        if start_position != self.current_position:
            self.current_position = start_position
            self._notify_position_changed()
            return page
        
        return None  # At beginning of document
    
    def _is_at_beginning(self) -> bool:
        """Check if we're at the beginning of the document"""
        return (self.current_position.chapter_index == 0 and 
                self.current_position.block_index == 0 and 
                self.current_position.word_index == 0)
    
    def jump_to_position(self, position: RenderingPosition) -> Page:
        """
        Jump to a specific position in the document.
        
        Args:
            position: Position to jump to
            
        Returns:
            Page at the new position
        """
        self.current_position = position
        self._notify_position_changed()
        return self.get_current_page()
    
    def jump_to_chapter(self, chapter_title: str) -> Optional[Page]:
        """
        Jump to a specific chapter by title.
        
        Args:
            chapter_title: Title of the chapter to jump to
            
        Returns:
            Page at chapter start or None if chapter not found
        """
        position = self.chapter_navigator.get_chapter_position(chapter_title)
        if position:
            return self.jump_to_position(position)
        return None
    
    def jump_to_chapter_index(self, chapter_index: int) -> Optional[Page]:
        """
        Jump to a chapter by index.
        
        Args:
            chapter_index: Index of the chapter (0-based)
            
        Returns:
            Page at chapter start or None if index invalid
        """
        chapters = self.chapter_navigator.chapters
        if 0 <= chapter_index < len(chapters):
            return self.jump_to_position(chapters[chapter_index].position)
        return None
    
    def set_font_scale(self, scale: float) -> Page:
        """
        Change the font scale and re-render current page.
        
        Args:
            scale: Font scaling factor (1.0 = normal, 2.0 = double size, etc.)
            
        Returns:
            Re-rendered page with new font scale
        """
        if scale != self.font_scale:
            self.font_scale = scale
            # The renderer will handle cache invalidation
        
        return self.get_current_page()
    
    def get_font_scale(self) -> float:
        """Get the current font scale"""
        return self.font_scale
    
    def get_table_of_contents(self) -> List[Tuple[str, HeadingLevel, RenderingPosition]]:
        """
        Get the table of contents.
        
        Returns:
            List of (title, level, position) tuples
        """
        return self.chapter_navigator.get_table_of_contents()
    
    def get_current_chapter(self) -> Optional[ChapterInfo]:
        """
        Get information about the current chapter.
        
        Returns:
            Current chapter info or None if no chapters
        """
        return self.chapter_navigator.get_current_chapter(self.current_position)
    
    def add_bookmark(self, name: str) -> bool:
        """
        Add a bookmark at the current position.
        
        Args:
            name: Bookmark name
            
        Returns:
            True if bookmark was added successfully
        """
        try:
            self.bookmark_manager.add_bookmark(name, self.current_position)
            return True
        except Exception:
            return False
    
    def remove_bookmark(self, name: str) -> bool:
        """
        Remove a bookmark.
        
        Args:
            name: Bookmark name
            
        Returns:
            True if bookmark was removed
        """
        return self.bookmark_manager.remove_bookmark(name)
    
    def jump_to_bookmark(self, name: str) -> Optional[Page]:
        """
        Jump to a bookmark.
        
        Args:
            name: Bookmark name
            
        Returns:
            Page at bookmark position or None if bookmark not found
        """
        position = self.bookmark_manager.get_bookmark(name)
        if position:
            return self.jump_to_position(position)
        return None
    
    def list_bookmarks(self) -> List[Tuple[str, RenderingPosition]]:
        """
        Get all bookmarks.
        
        Returns:
            List of (name, position) tuples
        """
        return self.bookmark_manager.list_bookmarks()
    
    def get_reading_progress(self) -> float:
        """
        Get reading progress as a percentage.
        
        Returns:
            Progress from 0.0 to 1.0
        """
        if not self.blocks:
            return 0.0
        
        # Simple progress calculation based on block index
        # A more sophisticated version would consider word positions
        total_blocks = len(self.blocks)
        current_block = min(self.current_position.block_index, total_blocks - 1)
        
        return current_block / max(1, total_blocks - 1)
    
    def get_position_info(self) -> Dict[str, Any]:
        """
        Get detailed information about the current position.
        
        Returns:
            Dictionary with position details
        """
        current_chapter = self.get_current_chapter()
        
        return {
            'position': self.current_position.to_dict(),
            'chapter': {
                'title': current_chapter.title if current_chapter else None,
                'level': current_chapter.level if current_chapter else None,
                'index': current_chapter.block_index if current_chapter else None
            },
            'progress': self.get_reading_progress(),
            'font_scale': self.font_scale,
            'page_size': self.page_size
        }
    
    def get_cache_stats(self) -> Dict[str, Any]:
        """
        Get cache statistics for debugging/monitoring.
        
        Returns:
            Dictionary with cache statistics
        """
        return self.renderer.get_cache_stats()
    
    def shutdown(self):
        """
        Shutdown the ereader manager and clean up resources.
        Call this when the application is closing.
        """
        # Save current position
        self.bookmark_manager.save_reading_position(self.current_position)
        
        # Shutdown renderer and buffer
        self.renderer.shutdown()
    
    def __del__(self):
        """Cleanup on destruction"""
        self.shutdown()


# Convenience function for quick setup
def create_ereader_manager(blocks: List[Block], 
                          page_size: Tuple[int, int], 
                          document_id: str = "default",
                          **kwargs) -> EreaderLayoutManager:
    """
    Convenience function to create an ereader manager with sensible defaults.
    
    Args:
        blocks: Document blocks to render
        page_size: Page size (width, height) in pixels
        document_id: Unique identifier for the document
        **kwargs: Additional arguments passed to EreaderLayoutManager
        
    Returns:
        Configured EreaderLayoutManager instance
    """
    return EreaderLayoutManager(blocks, page_size, document_id, **kwargs)
