"""
SVG document model.

Represents a complete SVG document with metadata, dimensions, and element tree.
"""

import logging
from dataclasses import dataclass, field
from typing import Any

import numpy as np

from .elements import SVGElement
from .shapes import BoundingBox

logger = logging.getLogger(__name__)


@dataclass
class ViewBox:
    """
    SVG viewBox coordinate system.

    Attributes:
        min_x: Minimum x coordinate.
        min_y: Minimum y coordinate.
        width: ViewBox width.
        height: ViewBox height.
    """

    min_x: float
    min_y: float
    width: float
    height: float

    def __post_init__(self) -> None:
        """Validate viewBox parameters."""
        if self.width <= 0:
            raise ValueError(f"ViewBox width must be positive, got {self.width}")
        if self.height <= 0:
            raise ValueError(f"ViewBox height must be positive, got {self.height}")


@dataclass
class SVGDocument:
    """
    Represents a parsed SVG document.

    Attributes:
        width: Document width in user units (None if not specified).
        height: Document height in user units (None if not specified).
        viewBox: ViewBox coordinate system (None if not specified).
        root_element: Root SVG element containing all children.
        metadata: Document metadata (title, description, etc.).
    """

    width: float | None = None
    height: float | None = None
    viewBox: ViewBox | None = None
    root_element: SVGElement | None = None
    metadata: dict[str, str] = field(default_factory=dict)

    def render(
        self,
        width: int | None = None,
        height: int | None = None,
        scale: float = 1.0,
        background: tuple[int, int, int, int] | None = None,
        dpi: float = 96.0,
    ) -> "np.ndarray[Any, np.dtype[np.uint8]]":
        """
        Render this document to a numpy bitmap array.

        Args:
            width: Output bitmap width in pixels.
            height: Output bitmap height in pixels.
            scale: Scaling factor applied to SVG dimensions.
            background: Background color as (R, G, B, A) tuple.
            dpi: Dots per inch for unit conversion.

        Returns:
            Numpy array with shape (height, width, 4) and dtype uint8.
        """
        # Import here to avoid circular dependency
        from .renderer import render_document

        return render_document(self, width, height, scale, background, dpi)

    def get_bounding_box(self) -> BoundingBox:
        """
        Calculate bounding box of all content in document.

        Returns:
            BoundingBox containing all rendered elements.
        """
        if not self.root_element:
            return BoundingBox(0, 0, 0, 0)

        # Collect all shapes
        shapes = self.root_element.get_all_shapes()

        if not shapes:
            return BoundingBox(0, 0, 0, 0)

        # Calculate combined bounding box
        min_x = float("inf")
        min_y = float("inf")
        max_x = float("-inf")
        max_y = float("-inf")

        for shape_elem in shapes:
            if shape_elem.shape:
                bbox = shape_elem.shape.get_bounding_box()

                # Transform bounding box corners
                corners = [
                    (bbox.min_x, bbox.min_y),
                    (bbox.max_x, bbox.min_y),
                    (bbox.min_x, bbox.max_y),
                    (bbox.max_x, bbox.max_y),
                ]

                for x, y in corners:
                    tx, ty = shape_elem.transform.apply_to_point(x, y)
                    min_x = min(min_x, tx)
                    min_y = min(min_y, ty)
                    max_x = max(max_x, tx)
                    max_y = max(max_y, ty)

        if min_x == float("inf"):
            return BoundingBox(0, 0, 0, 0)

        return BoundingBox(min_x, min_y, max_x, max_y)

    def find_element_by_id(self, element_id: str) -> SVGElement | None:
        """
        Find element by its id attribute.

        Args:
            element_id: The id value to search for.

        Returns:
            SVGElement with matching id, or None if not found.
        """
        if not self.root_element:
            return None

        return self.root_element.find_element_by_id(element_id)

    def get_dimensions(self) -> tuple[float, float]:
        """
        Get document dimensions, inferring from viewBox if necessary.

        When width/height are not specified, defaults to 300x150 (SVG 2 spec default,
        matching browser and Inkscape behavior).

        Returns:
            Tuple of (width, height) in user units.
        """
        # Try explicit width/height attributes
        if self.width is not None and self.height is not None:
            return (self.width, self.height)

        # Try viewBox
        if self.viewBox is not None:
            return (self.viewBox.width, self.viewBox.height)

        # Default to 300x150 (SVG 2 spec default for root svg without dimensions)
        # This matches browser and Inkscape behavior
        return (300.0, 150.0)
