"""Shared utilities for AI (Claude Vision) interactions."""

import base64
import json
import sys
import time
from typing import Callable, TypeVar

MAX_RETRIES = 2
RETRY_BASE_DELAY = 2  # seconds

T = TypeVar("T")


def encode_image_base64(image_path: str) -> str:
    """Return the base64-encoded content of *image_path*."""
    with open(image_path, "rb") as fh:
        return base64.standard_b64encode(fh.read()).decode("utf-8")


def strip_markdown_fences(raw: str) -> str:
    """Strip markdown code fences from *raw* if present."""
    if raw.startswith("```"):
        raw = raw.split("```")[1]
        if raw.startswith("json"):
            raw = raw[4:]
        raw = raw.strip()
    return raw


def call_claude_vision_with_retry(
    client: object,
    model: str,
    image_data: str,
    prompt: str,
    parse_response: Callable[[str], T],
    context: str = "AI",
) -> T:
    """Send a vision request to Claude with retry logic.

    Parameters
    ----------
    client:
        An ``anthropic.Anthropic`` client instance.
    model:
        Claude model name.
    image_data:
        Base64-encoded JPEG image data.
    prompt:
        Text prompt to send alongside the image.
    parse_response:
        Callable that receives the stripped JSON string and returns a parsed
        result. Should raise on invalid responses so the retry loop catches it.
    context:
        Short label used in retry log messages (e.g. ``"AI frame selection"``).

    Returns
    -------
    Whatever *parse_response* returns on success.

    Raises
    ------
    RuntimeError
        If all retries are exhausted.
    """
    last_error: Exception | None = None
    for attempt in range(MAX_RETRIES):
        try:
            message = client.messages.create(  # type: ignore[union-attr]
                model=model,
                max_tokens=256,
                messages=[
                    {
                        "role": "user",
                        "content": [
                            {
                                "type": "image",
                                "source": {
                                    "type": "base64",
                                    "media_type": "image/jpeg",
                                    "data": image_data,
                                },
                            },
                            {"type": "text", "text": prompt},
                        ],
                    }
                ],
            )
            raw = message.content[0].text.strip()
            raw = strip_markdown_fences(raw)
            return parse_response(raw)

        except Exception as exc:  # noqa: BLE001
            last_error = exc
            if attempt < MAX_RETRIES - 1:
                delay = RETRY_BASE_DELAY * (2 ** attempt)
                print(
                    f"   ⚠️  {context} attempt {attempt + 1} failed: {exc}. "
                    f"Retrying in {delay}s…",
                    file=sys.stderr,
                )
                time.sleep(delay)

    raise RuntimeError(
        f"{context} failed after {MAX_RETRIES} attempts: {last_error}"
    )
