"""
Base class for overlay sub-applications.

This provides a common interface for all overlay types (TOC, Settings, Navigation, etc.)
Each overlay is a self-contained sub-application that handles its own rendering and gestures.
"""

from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Optional, Dict, Any, Tuple
from PIL import Image

from ..gesture import GestureResponse, ActionType
from ..state import OverlayState

if TYPE_CHECKING:
    from ..application import EbookReader


class OverlaySubApplication(ABC):
    """
    Base class for overlay sub-applications.

    Each overlay type extends this class and implements:
    - open(): Generate HTML, render, and return composited image
    - handle_tap(): Process tap gestures within the overlay
    - close(): Clean up and return base page
    - get_overlay_type(): Return the OverlayState enum value

    The base class provides:
    - Common rendering infrastructure (HTML to image conversion)
    - Coordinate translation (screen to overlay panel)
    - Query pixel support (detecting interactive elements)
    - Compositing (darkened background + centered panel)
    """

    def __init__(self, reader: 'EbookReader'):
        """
        Initialize overlay sub-application.

        Args:
            reader: Reference to parent EbookReader instance
        """
        self.reader = reader
        self.page_size = reader.page_size

        # Overlay rendering state
        self._overlay_reader: Optional['EbookReader'] = None
        self._cached_base_page: Optional[Image.Image] = None
        self._cached_overlay_image: Optional[Image.Image] = None
        self._overlay_panel_offset: Tuple[int, int] = (0, 0)
        self._panel_size: Tuple[int, int] = (0, 0)

    @abstractmethod
    def get_overlay_type(self) -> OverlayState:
        """
        Get the overlay type identifier.

        Returns:
            OverlayState enum value for this overlay
        """
        pass

    @abstractmethod
    def open(self, base_page: Image.Image, **kwargs) -> Image.Image:
        """
        Open the overlay and return composited image.

        Args:
            base_page: Current reading page to show underneath
            **kwargs: Overlay-specific parameters

        Returns:
            Composited image with overlay on top of base page
        """
        pass

    @abstractmethod
    def handle_tap(self, x: int, y: int) -> GestureResponse:
        """
        Handle tap gesture within the overlay.

        Args:
            x, y: Screen coordinates of tap

        Returns:
            GestureResponse indicating what action to take
        """
        pass

    def close(self) -> Optional[Image.Image]:
        """
        Close the overlay and clean up resources.

        Returns:
            Base page image (without overlay), or None if not open
        """
        base_page = self._cached_base_page

        # Clear caches
        self._cached_base_page = None
        self._cached_overlay_image = None
        self._overlay_panel_offset = (0, 0)
        self._panel_size = (0, 0)

        # Close overlay reader
        if self._overlay_reader:
            self._overlay_reader.close()
            self._overlay_reader = None

        return base_page

    # ===================================================================
    # Common Infrastructure Methods
    # ===================================================================

    def render_html_to_image(self, html: str, panel_size: Tuple[int, int]) -> Image.Image:
        """
        Render HTML to image using a temporary EbookReader.

        Args:
            html: HTML content to render
            panel_size: Size for the overlay panel (width, height)

        Returns:
            Rendered PIL Image of the HTML
        """
        # Import here to avoid circular dependency
        from ..application import EbookReader

        # Create or reuse overlay reader
        if self._overlay_reader:
            self._overlay_reader.close()

        self._overlay_reader = EbookReader(
            page_size=panel_size,
            margin=15,
            background_color=(255, 255, 255)
        )

        # Load the HTML content
        success = self._overlay_reader.load_html(
            html_string=html,
            title=f"{self.get_overlay_type().name} Overlay",
            author="",
            document_id=f"{self.get_overlay_type().name.lower()}_overlay"
        )

        if not success:
            raise ValueError(f"Failed to load {self.get_overlay_type().name} overlay HTML")

        # Get the rendered page
        return self._overlay_reader.get_current_page()

    def composite_overlay(self, base_page: Image.Image, overlay_panel: Image.Image) -> Image.Image:
        """
        Composite overlay panel on top of base page with darkened background.

        Creates popup effect by:
        1. Darkening the base image (70% brightness for e-ink visibility)
        2. Placing the overlay panel centered on top with a border

        Args:
            base_page: Base reading page
            overlay_panel: Rendered overlay panel

        Returns:
            Composited PIL Image with popup effect
        """
        from PIL import ImageDraw, ImageEnhance
        import os

        # Convert base image to RGB
        result = base_page.convert('RGB').copy()

        # Lighten the background slightly (70% brightness for e-ink visibility)
        enhancer = ImageEnhance.Brightness(result)
        result = enhancer.enhance(0.7)

        # Convert overlay panel to RGB
        if overlay_panel.mode != 'RGB':
            overlay_panel = overlay_panel.convert('RGB')

        # DEBUG: Draw bounding boxes on interactive elements if debug mode enabled
        debug_mode = os.environ.get('DREADER_DEBUG_OVERLAY', '0') == '1'
        if debug_mode:
            overlay_panel = self._draw_debug_bounding_boxes(overlay_panel.copy())

        # Calculate centered position for the panel
        panel_x = int((self.page_size[0] - overlay_panel.width) / 2)
        panel_y = int((self.page_size[1] - overlay_panel.height) / 2)

        # Store panel position and size for coordinate translation
        self._overlay_panel_offset = (panel_x, panel_y)
        self._panel_size = (overlay_panel.width, overlay_panel.height)

        # Add a thick black border around the panel for e-ink clarity
        draw = ImageDraw.Draw(result)
        border_width = 3
        draw.rectangle(
            [panel_x - border_width, panel_y - border_width,
             panel_x + overlay_panel.width + border_width,
             panel_y + overlay_panel.height + border_width],
            outline=(0, 0, 0),
            width=border_width
        )

        # Paste the panel onto the dimmed background
        result.paste(overlay_panel, (panel_x, panel_y))

        return result

    def query_overlay_pixel(self, x: int, y: int) -> Optional[Dict[str, Any]]:
        """
        Query a pixel in the overlay to detect interactive elements.

        Uses pyWebLayout's query_point() to detect tapped elements,
        including link targets and data attributes.

        Args:
            x, y: Screen coordinates to query

        Returns:
            Dictionary with query result (text, link_target, is_interactive),
            or None if query failed or coordinates outside overlay
        """
        if not self._overlay_reader:
            return None

        # Translate screen coordinates to overlay panel coordinates
        panel_x, panel_y = self._overlay_panel_offset
        overlay_x = x - panel_x
        overlay_y = y - panel_y

        # Check if coordinates are within the overlay panel
        if overlay_x < 0 or overlay_y < 0:
            return None

        panel_width, panel_height = self._panel_size
        if overlay_x >= panel_width or overlay_y >= panel_height:
            return None

        # Get the current page from the overlay reader
        if not self._overlay_reader.manager:
            return None

        current_page = self._overlay_reader.manager.get_current_page()
        if not current_page:
            return None

        # Query the point
        result = current_page.query_point((overlay_x, overlay_y))

        import logging
        logger = logging.getLogger(__name__)
        logger.info(f"[OVERLAY_BASE] query_point({overlay_x}, {overlay_y}) returned: {result}")
        if result:
            logger.info(f"[OVERLAY_BASE]   text={result.text}, link_target={result.link_target}, is_interactive={result.is_interactive}")
            logger.info(f"[OVERLAY_BASE]   bounds={result.bounds}, object_type={result.object_type}")

        if not result:
            return None

        # Extract relevant data from QueryResult
        return {
            "text": result.text,
            "link_target": result.link_target,
            "is_interactive": result.is_interactive,
            "bounds": result.bounds,
            "object_type": result.object_type
        }

    def _calculate_panel_size(self, width_ratio: float = 0.6, height_ratio: float = 0.7) -> Tuple[int, int]:
        """
        Calculate overlay panel size as a percentage of screen size.

        Args:
            width_ratio: Panel width as ratio of screen width (default 60%)
            height_ratio: Panel height as ratio of screen height (default 70%)

        Returns:
            Tuple of (panel_width, panel_height) in pixels
        """
        panel_width = int(self.page_size[0] * width_ratio)
        panel_height = int(self.page_size[1] * height_ratio)
        return (panel_width, panel_height)

    def _draw_debug_bounding_boxes(self, overlay_panel: Image.Image) -> Image.Image:
        """
        Draw bounding boxes around all interactive elements for debugging.

        This scans the overlay panel and draws red rectangles around all
        clickable elements to help visualize where users need to click.

        Args:
            overlay_panel: Overlay panel image to annotate

        Returns:
            Annotated overlay panel with bounding boxes
        """
        from PIL import ImageDraw, ImageFont
        import logging

        logger = logging.getLogger(__name__)

        if not self._overlay_reader or not self._overlay_reader.manager:
            logger.warning("[DEBUG] No overlay reader available for debug visualization")
            return overlay_panel

        page = self._overlay_reader.manager.get_current_page()
        if not page:
            logger.warning("[DEBUG] No page available for debug visualization")
            return overlay_panel

        # Scan for all interactive elements
        panel_width, panel_height = overlay_panel.size
        link_regions = {}  # link_target -> (min_x, min_y, max_x, max_y)

        logger.info(f"[DEBUG] Scanning {panel_width}x{panel_height} overlay for interactive elements...")

        # Scan with fine granularity to find all interactive pixels
        for y in range(0, panel_height, 2):
            for x in range(0, panel_width, 2):
                result = page.query_point((x, y))
                if result and result.link_target:
                    if result.link_target not in link_regions:
                        link_regions[result.link_target] = [x, y, x, y]
                    else:
                        # Expand bounding box
                        link_regions[result.link_target][0] = min(link_regions[result.link_target][0], x)
                        link_regions[result.link_target][1] = min(link_regions[result.link_target][1], y)
                        link_regions[result.link_target][2] = max(link_regions[result.link_target][2], x)
                        link_regions[result.link_target][3] = max(link_regions[result.link_target][3], y)

        # Draw bounding boxes
        draw = ImageDraw.Draw(overlay_panel)

        try:
            font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 10)
        except:
            font = ImageFont.load_default()

        logger.info(f"[DEBUG] Found {len(link_regions)} interactive regions")

        for link_target, (min_x, min_y, max_x, max_y) in link_regions.items():
            # Draw red bounding box
            draw.rectangle(
                [min_x, min_y, max_x, max_y],
                outline=(255, 0, 0),
                width=2
            )

            # Draw label
            label = link_target[:20]  # Truncate if too long
            draw.text((min_x + 2, min_y - 12), label, fill=(255, 0, 0), font=font)

            logger.info(f"[DEBUG]   {link_target}: ({min_x}, {min_y}) to ({max_x}, {max_y})")

        return overlay_panel
