"""Layout composition engine — responsive grid-based positioning system.

Replaces hardcoded Inches values with a relative coordinate system
that adapts to slide dimensions and item counts. All positions are
computed from fractions of the slide area, with configurable margins,
gaps, and alignment.

Usage:
    grid = LayoutGrid(
        slide_width=13.333, slide_height=7.5,
        margin=Margin(left=0.75, right=0.75, top=1.6, bottom=0.5),
    )
    cells = grid.auto_columns(count=3, row_top=0.0, row_height=1.0)
    for cell in cells:
        pos = cell.to_position()  # → Position(left, top, width, height)
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Literal, Optional, Sequence

from pptx_mcp.models.slides import Position


@dataclass(frozen=True)
class Margin:
    """Slide margins in inches."""

    left: float = 0.75
    right: float = 0.75
    top: float = 1.6  # below title area
    bottom: float = 0.5


@dataclass(frozen=True)
class Cell:
    """A positioned rectangle within the content area, in inches."""

    left: float
    top: float
    width: float
    height: float

    def to_position(self) -> Position:
        """Convert to a SlideElement Position model."""
        return Position(left=self.left, top=self.top, width=self.width, height=self.height)

    def inset(self, padding: float) -> Cell:
        """Return a new Cell inset by padding on all sides."""
        return Cell(
            left=self.left + padding,
            top=self.top + padding,
            width=max(0.1, self.width - 2 * padding),
            height=max(0.1, self.height - 2 * padding),
        )

    def split_vertical(self, ratio: float = 0.5) -> tuple[Cell, Cell]:
        """Split into top/bottom cells at the given ratio."""
        h_top = self.height * ratio
        top_cell = Cell(self.left, self.top, self.width, h_top)
        bottom_cell = Cell(self.left, self.top + h_top, self.width, self.height - h_top)
        return top_cell, bottom_cell

    def split_horizontal(self, ratio: float = 0.5) -> tuple[Cell, Cell]:
        """Split into left/right cells at the given ratio."""
        w_left = self.width * ratio
        left_cell = Cell(self.left, self.top, w_left, self.height)
        right_cell = Cell(self.left + w_left, self.top, self.width - w_left, self.height)
        return left_cell, right_cell


@dataclass
class LayoutGrid:
    """Responsive grid system for a single slide.

    All coordinates are in inches. The usable content area is
    determined by subtracting margins from the total slide dimensions.
    """

    slide_width: float = 13.333
    slide_height: float = 7.5
    margin: Margin = field(default_factory=Margin)

    @property
    def content_left(self) -> float:
        return self.margin.left

    @property
    def content_top(self) -> float:
        return self.margin.top

    @property
    def content_width(self) -> float:
        return self.slide_width - self.margin.left - self.margin.right

    @property
    def content_height(self) -> float:
        return self.slide_height - self.margin.top - self.margin.bottom

    @property
    def content_area(self) -> Cell:
        """The full usable content area as a Cell."""
        return Cell(
            left=self.content_left,
            top=self.content_top,
            width=self.content_width,
            height=self.content_height,
        )

    def columns(
        self,
        count: int,
        gap: float = 0.4,
        row_top: Optional[float] = None,
        row_height: Optional[float] = None,
    ) -> list[Cell]:
        """Divide the content area into equal-width columns.

        Args:
            count: Number of columns (1-6).
            gap: Horizontal gap between columns in inches.
            row_top: Top of the row in inches (absolute). Defaults to content_top.
            row_height: Row height in inches. Defaults to full content_height.

        Returns:
            List of Cell objects, one per column.
        """
        if count < 1:
            return []
        top = row_top if row_top is not None else self.content_top
        height = row_height if row_height is not None else self.content_height
        total_gap = gap * (count - 1)
        col_width = (self.content_width - total_gap) / count
        cells: list[Cell] = []
        for i in range(count):
            left = self.content_left + i * (col_width + gap)
            cells.append(Cell(left=left, top=top, width=col_width, height=height))
        return cells

    def rows(
        self,
        count: int,
        gap: float = 0.3,
        col_left: Optional[float] = None,
        col_width: Optional[float] = None,
    ) -> list[Cell]:
        """Divide the content area into equal-height rows.

        Args:
            count: Number of rows.
            gap: Vertical gap between rows in inches.
            col_left: Left edge in inches (absolute). Defaults to content_left.
            col_width: Width in inches. Defaults to full content_width.

        Returns:
            List of Cell objects, one per row.
        """
        if count < 1:
            return []
        left = col_left if col_left is not None else self.content_left
        width = col_width if col_width is not None else self.content_width
        total_gap = gap * (count - 1)
        row_height = (self.content_height - total_gap) / count
        cells: list[Cell] = []
        for i in range(count):
            top = self.content_top + i * (row_height + gap)
            cells.append(Cell(left=left, top=top, width=width, height=row_height))
        return cells

    def grid(
        self,
        n_cols: int,
        n_rows: int,
        h_gap: float = 0.4,
        v_gap: float = 0.3,
    ) -> list[list[Cell]]:
        """Create a 2D grid of cells.

        Returns:
            List of rows, each containing a list of Cell objects.
        """
        total_h_gap = h_gap * (n_cols - 1)
        total_v_gap = v_gap * (n_rows - 1)
        col_width = (self.content_width - total_h_gap) / n_cols
        row_height = (self.content_height - total_v_gap) / n_rows

        result: list[list[Cell]] = []
        for r in range(n_rows):
            row: list[Cell] = []
            top = self.content_top + r * (row_height + v_gap)
            for c in range(n_cols):
                left = self.content_left + c * (col_width + h_gap)
                row.append(Cell(left=left, top=top, width=col_width, height=row_height))
            result.append(row)
        return result

    def auto_layout(
        self,
        count: int,
        max_cols: int = 4,
        h_gap: float = 0.4,
        v_gap: float = 0.3,
    ) -> list[Cell]:
        """Automatically arrange items into a grid, choosing the best layout.

        Picks columns/rows to minimize wasted space.

        Args:
            count: Number of items to lay out.
            max_cols: Maximum columns allowed.
            h_gap: Horizontal gap.
            v_gap: Vertical gap.

        Returns:
            Flat list of Cell objects, row-major order.
        """
        if count <= 0:
            return []
        if count <= max_cols:
            return self.columns(count, gap=h_gap)

        # Pick the grid that minimizes empty cells
        n_cols = min(count, max_cols)
        n_rows = (count + n_cols - 1) // n_cols
        grid = self.grid(n_cols, n_rows, h_gap=h_gap, v_gap=v_gap)
        return [cell for row in grid for cell in row][:count]

    def centered_cell(
        self,
        width: float,
        height: float,
        vertical_offset: float = 0.0,
    ) -> Cell:
        """Return a cell centered horizontally in the content area.

        Args:
            width: Cell width in inches.
            height: Cell height in inches.
            vertical_offset: Offset from vertical center (positive = down).
        """
        left = self.content_left + (self.content_width - width) / 2
        top = self.content_top + (self.content_height - height) / 2 + vertical_offset
        return Cell(left=left, top=top, width=width, height=height)

    def full_width_row(
        self,
        top: float,
        height: float,
    ) -> Cell:
        """Return a full-width cell at the specified vertical position."""
        return Cell(left=self.content_left, top=top, width=self.content_width, height=height)

    def side_by_side(
        self,
        left_ratio: float = 0.5,
        gap: float = 0.4,
        row_top: Optional[float] = None,
        row_height: Optional[float] = None,
    ) -> tuple[Cell, Cell]:
        """Split content area into left and right panels with a custom ratio.

        Args:
            left_ratio: Fraction of width for the left panel (0.0-1.0).
            gap: Gap between panels in inches.
            row_top: Top position. Defaults to content_top.
            row_height: Height. Defaults to content_height.

        Returns:
            Tuple of (left_cell, right_cell).
        """
        top = row_top if row_top is not None else self.content_top
        height = row_height if row_height is not None else self.content_height
        usable = self.content_width - gap
        left_w = usable * left_ratio
        right_w = usable * (1 - left_ratio)
        left_cell = Cell(self.content_left, top, left_w, height)
        right_cell = Cell(self.content_left + left_w + gap, top, right_w, height)
        return left_cell, right_cell
