"""
Interactive and queryable image for pyWebLayout.

Provides an InteractiveImage class that combines Image with Interactable
and Queriable capabilities, allowing images to respond to tap events with
proper bounding box detection.
"""

from typing import Optional, Callable, Tuple
import numpy as np

from .block import Image
from ..core.base import Interactable, Queriable


class InteractiveImage(Image, Interactable, Queriable):
    """
    An image that can be interacted with and queried for hit detection.

    This combines pyWebLayout's Image block with Interactable and Queriable
    capabilities, allowing the image to:
    - Have a callback that fires when tapped
    - Know its rendered position (origin)
    - Detect if a point is within its bounds

    Example:
        >>> img = InteractiveImage(
        ...     source="cover.png",
        ...     alt_text="Book Title",
        ...     callback=lambda point: "/path/to/book.epub"
        ... )
        >>> # After rendering, origin is set automatically
        >>> # Check if tap is inside
        >>> result = img.interact((120, 250))
        >>> # Returns "/path/to/book.epub" if inside, None if outside
    """

    def __init__(
        self,
        source: str = "",
        alt_text: str = "",
        width: Optional[int] = None,
        height: Optional[int] = None,
        callback: Optional[Callable] = None
    ):
        """
        Initialize an interactive image.

        Args:
            source: The image source URL or path
            alt_text: Alternative text for accessibility
            width: Optional image width in pixels
            height: Optional image height in pixels
            callback: Function to call when image is tapped (receives point coordinates)
        """
        # Initialize Image
        Image.__init__(
            self,
            source=source,
            alt_text=alt_text,
            width=width,
            height=height)

        # Initialize Interactable
        Interactable.__init__(self, callback=callback)

        # Initialize position tracking
        self._origin = np.array([0, 0])  # Will be set during rendering
        self.size = (width or 0, height or 0)  # Will be updated during rendering

    def interact(self, point: np.generic) -> Optional[any]:
        """
        Handle interaction at the given point.

        Only triggers the callback if the point is within the image bounds.

        Args:
            point: The coordinates of the interaction (x, y)

        Returns:
            The result of the callback if point is inside, None otherwise
        """
        # Check if point is inside this image
        if self.in_object(point):
            # Point is inside, trigger callback
            if self._callback is not None:
                return self._callback(point)

        return None

    def in_object(self, point: np.generic) -> bool:
        """
        Check if a point is within the image bounds.

        Args:
            point: The coordinates to check (x, y)

        Returns:
            True if point is inside the image, False otherwise
        """
        point_array = np.array(point)
        relative_point = point_array - self._origin
        return np.all((0 <= relative_point) & (relative_point < self.size))

    @classmethod
    def create_and_add_to(
        cls,
        parent,
        source: str,
        alt_text: str = "",
        width: Optional[int] = None,
        height: Optional[int] = None,
        callback: Optional[Callable] = None
    ) -> 'InteractiveImage':
        """
        Create an interactive image and add it to a parent block.

        This is a convenience method that mimics the Image.create_and_add_to API
        but creates an InteractiveImage instead.

        Args:
            parent: Parent block to add this image to
            source: The image source URL or path
            alt_text: Alternative text for accessibility
            width: Optional image width in pixels
            height: Optional image height in pixels
            callback: Function to call when image is tapped

        Returns:
            The created InteractiveImage instance
        """
        img = cls(
            source=source,
            alt_text=alt_text,
            width=width,
            height=height,
            callback=callback
        )

        # Add to parent using its add_block method
        if hasattr(parent, 'add_block'):
            parent.add_block(img)
        elif hasattr(parent, 'add_child'):
            parent.add_child(img)
        elif hasattr(parent, '_children'):
            parent._children.append(img)
        elif hasattr(parent, '_blocks'):
            parent._blocks.append(img)

        return img

    def set_rendered_bounds(self, origin: Tuple[int, int], size: Tuple[int, int]):
        """
        Set the rendered position and size of this image.

        This should be called by the renderer after it places the image.

        Args:
            origin: (x, y) coordinates of top-left corner
            size: (width, height) of the rendered image
        """
        self._origin = np.array(origin)
        self.size = size
