"""Crop position selection for poster format (1:1 square crop)."""

import json

CROP_POSITIONS = ["left", "center-left", "center", "center-right", "right"]

from .ai_utils import encode_image_base64, call_claude_vision_with_retry


def apply_crop_position(
    image_width: int,
    image_height: int,
    position: str,
) -> tuple[int, int, int, int]:
    """
    Calculate the bounding box for a 1:1 square crop of the given image.

    The crop height equals *image_height* (full height), giving a square of
    side *image_height*.  The horizontal position within the frame is
    controlled by *position*.

    Parameters
    ----------
    image_width:
        Width of the source image in pixels.
    image_height:
        Height of the source image in pixels.
    position:
        One of ``left``, ``center-left``, ``center``, ``center-right``,
        ``right``.

    Returns
    -------
    (left, upper, right, lower) bounding box suitable for ``PIL.Image.crop()``.

    Raises
    ------
    ValueError
        If *position* is not one of the five allowed values.
    """
    if position not in CROP_POSITIONS:
        raise ValueError(
            f"Invalid crop position '{position}'. "
            f"Must be one of: {', '.join(CROP_POSITIONS)}"
        )

    crop_size = image_height  # square side = full height
    max_offset = image_width - crop_size

    # Five evenly-spaced positions from 0 to max_offset
    step_index = CROP_POSITIONS.index(position)
    x_offset = (step_index * max_offset) // (len(CROP_POSITIONS) - 1)

    return (x_offset, 0, x_offset + crop_size, image_height)


def select_crop_with_ai(
    image_path: str,
    api_key: str,
    model: str,
) -> tuple[str, str]:
    """
    Ask Claude Vision to suggest the best 1:1 crop position for a poster.

    Parameters
    ----------
    image_path:
        Path to the high-res frame image.
    api_key:
        Anthropic API key.
    model:
        Claude model name.

    Returns
    -------
    (crop_position, reasoning)
        *crop_position* is one of the five allowed values.
        *reasoning* is the AI explanation.

    Raises
    ------
    RuntimeError
        If the AI call fails after all retries or returns an invalid response.
    """
    try:
        import anthropic
    except ImportError:
        raise RuntimeError(
            "The 'anthropic' package is required for AI crop selection. "
            "Install it with: pip install anthropic"
        )

    image_data = encode_image_base64(image_path)

    prompt = (
        "You are analysing an image to determine the best 1:1 square crop position "
        "for a portrait-format poster. The image is wider than it is tall. "
        "A square crop covering the full height will be taken at one of five "
        "horizontal positions: left, center-left, center, center-right, right. "
        "Choose the position that best captures the main subject or the most "
        "visually interesting area.\n\n"
        "Respond ONLY with a valid JSON object on a single line, with no additional text:\n"
        '{"crop_position": "<one of: left, center-left, center, center-right, right>", '
        '"reasoning": "<brief explanation>"}'
    )

    client = anthropic.Anthropic(api_key=api_key)

    def _parse(raw: str) -> tuple[str, str]:
        data = json.loads(raw)
        crop_position = str(data["crop_position"])
        reasoning = str(data["reasoning"])
        if crop_position not in CROP_POSITIONS:
            raise ValueError(f"AI returned invalid crop position: {crop_position!r}")
        return crop_position, reasoning

    return call_claude_vision_with_retry(client, model, image_data, prompt, _parse, "AI crop selection")
