"""
image_utils.py — Image manipulation utilities for client video workflow.

Grid cell extraction, image resizing, and start frame preparation.
Pure functions with no pipeline dependencies — only PIL.
"""

import logging
from io import BytesIO
from pathlib import Path
from typing import Optional

from PIL import Image

logger = logging.getLogger(__name__)


def extract_grid_cell(
    grid_path: Path,
    row: int,
    col: int,
    grid_size: str = "2x2",
    output_path: Optional[Path] = None,
) -> Path:
    """Extract a single cell from a grid image.

    Args:
        grid_path: Path to the grid image (PNG or JPEG).
        row: Row index (0-based, top to bottom).
        col: Column index (0-based, left to right).
        grid_size: Grid layout string ("2x2", "3x3", "2x3").
        output_path: Where to save the extracted cell. If None,
            saves next to the grid as {stem}_r{row}c{col}.png.

    Returns:
        Path to the saved cell image.

    Raises:
        ValueError: If row/col are out of bounds for the grid size.
        FileNotFoundError: If grid_path doesn't exist.
    """
    if not grid_path.exists():
        raise FileNotFoundError(f"Grid image not found: {grid_path}")

    rows_count, cols_count = _parse_grid_size(grid_size)

    if row < 0 or row >= rows_count:
        raise ValueError(f"Row {row} out of bounds for {grid_size} grid (0-{rows_count - 1})")
    if col < 0 or col >= cols_count:
        raise ValueError(f"Col {col} out of bounds for {grid_size} grid (0-{cols_count - 1})")

    img = Image.open(grid_path)
    width, height = img.size

    cell_w = width // cols_count
    cell_h = height // rows_count

    left = col * cell_w
    upper = row * cell_h
    right = left + cell_w
    lower = upper + cell_h

    cell = img.crop((left, upper, right, lower))

    # Convert to RGB if needed (drop alpha)
    if cell.mode in ("RGBA", "P"):
        cell = cell.convert("RGB")

    if output_path is None:
        output_path = grid_path.parent / f"{grid_path.stem}_r{row}c{col}.png"

    output_path.parent.mkdir(parents=True, exist_ok=True)
    cell.save(str(output_path), format="PNG")
    logger.info("Extracted cell r%dc%d from %s → %s (%dx%d)",
                row, col, grid_path.name, output_path.name, cell.size[0], cell.size[1])
    return output_path


def quadrant_to_rowcol(quadrant: str, grid_size: str = "2x2") -> tuple[int, int]:
    """Convert a human-friendly quadrant name to (row, col).

    Supports:
        "top_left" / "tl" / "1" → (0, 0)
        "top_right" / "tr" / "2" → (0, 1)
        "bottom_left" / "bl" / "3" → (1, 0)
        "bottom_right" / "br" / "4" → (1, 1)
        For 3x3: "1"-"9" maps left-to-right, top-to-bottom.

    Raises:
        ValueError: If quadrant string is unrecognized.
    """
    rows_count, cols_count = _parse_grid_size(grid_size)

    # Named quadrants (2x2 only)
    _NAMED = {
        "top_left": (0, 0), "tl": (0, 0),
        "top_right": (0, 1), "tr": (0, 1),
        "bottom_left": (1, 0), "bl": (1, 0),
        "bottom_right": (1, 1), "br": (1, 1),
    }
    key = quadrant.lower().strip()
    if key in _NAMED:
        return _NAMED[key]

    # Numeric: 1-indexed, left-to-right, top-to-bottom
    try:
        idx = int(key) - 1  # Convert to 0-indexed
        if idx < 0 or idx >= rows_count * cols_count:
            raise ValueError(f"Cell index {key} out of bounds for {grid_size}")
        row = idx // cols_count
        col = idx % cols_count
        return row, col
    except ValueError:
        pass

    raise ValueError(
        f"Unrecognized quadrant: {quadrant!r}. "
        f"Use 'top_left'/'tl'/'1', 'top_right'/'tr'/'2', etc."
    )


def _parse_grid_size(grid_size: str) -> tuple[int, int]:
    """Parse "2x2", "3x3", "2x3" into (rows, cols)."""
    parts = grid_size.lower().split("x")
    if len(parts) != 2:
        raise ValueError(f"Invalid grid_size: {grid_size!r}. Expected 'RxC' (e.g., '2x2').")
    return int(parts[0]), int(parts[1])
