"""Thin wrapper around the OpenAI Responses API."""

from __future__ import annotations

import base64
import mimetypes
from pathlib import Path
import logging
from typing import Optional, Sequence, Union

from openai import OpenAI

try:
    from openai import APIError as OpenAIError  # type: ignore
except ImportError:  # pragma: no cover - fallback for older SDKs
    from openai.error import OpenAIError  # type: ignore

ImageInput = Union[str, Path]
logger = logging.getLogger(__name__)


class OpenAIResponsesClient:
    """Convenience wrapper around the OpenAI Responses API."""

    def generate_response(
        self,
        *,
        api_key: str,
        prompt: str,
        model: str,
        max_tokens: int = 32000,
        reasoning_effort: Optional[str] = None,
        images: Optional[Sequence[ImageInput]] = None,
    ) -> str:
        """Generate a response from the specified model.

        Args:
            api_key: Secret key used to authenticate with OpenAI.
            prompt: Natural-language instruction or query for the model.
            model: Identifier of the OpenAI model to target (for example, ``"gpt-4o"``).
            max_tokens: Cap for tokens across the entire exchange, defaults to 32000.
            reasoning_effort: Optional reasoning effort hint (``"low"``, ``"medium"``, or ``"high"``).
            images: Optional collection of image references (local paths or URLs).

        Returns:
            The text output produced by the model.

        Raises:
            ValueError: If required arguments are missing or the request payload is empty.
            OpenAIError: If the underlying OpenAI request fails.
        """
        if not api_key:
            raise ValueError("api_key must be provided.")
        if not prompt and not images:
            raise ValueError("At least one of prompt or images must be provided.")
        if not model:
            raise ValueError("model must be provided.")

        client = OpenAI(api_key=api_key)

        content_blocks = []
        if prompt:
            content_blocks.append({"type": "input_text", "text": prompt})

        if images:
            for image in images:
                content_blocks.append(self._to_image_block(image))

        if not content_blocks:
            raise ValueError("No content provided for response generation.")

        request_payload = {
            "model": model,
            "input": [
                {
                    "role": "user",
                    "content": content_blocks,
                }
            ],
            "max_output_tokens": max_tokens,
        }

        if reasoning_effort:
            request_payload["reasoning"] = {"effort": reasoning_effort}

        try:
            response = client.responses.create(**request_payload)
        except Exception as exc:  # Log and re-raise to preserve default behavior
            logger.exception("OpenAI Responses API request failed: %s", exc)
            raise

        output_text = response.output_text
        logger.info(
            "OpenAI generate_response succeeded: model=%s images=%d text_len=%d",
            model,
            len(images or []),
            len(output_text or ""),
        )
        return output_text

    @staticmethod
    def _to_image_block(image: ImageInput) -> dict:
        """Convert an image reference into an OpenAI Responses API block."""
        if isinstance(image, Path):
            return {
                "type": "input_image",
                "image_url": _encode_image_path(image),
            }

        if image.startswith(("http://", "https://", "data:")):
            return {
                "type": "input_image",
                "image_url": image,
            }

        return {
            "type": "input_image",
            "image_url": _encode_image_path(Path(image)),
        }

    def list_models(self, *, api_key: str) -> list[dict[str, Optional[str]]]:
        """Return the models available to the authenticated OpenAI account."""
        if not api_key:
            raise ValueError("api_key must be provided.")

        client = OpenAI(api_key=api_key)
        try:
            response = client.models.list()
        except Exception as exc:
            logger.exception("OpenAI list models failed: %s", exc)
            raise
        data = getattr(response, "data", []) or []

        models: list[dict[str, Optional[str]]] = []
        for model in data:
            model_id = getattr(model, "id", None)
            if model_id is None and isinstance(model, dict):
                model_id = model.get("id")
            if not model_id:
                continue

            display_name = getattr(model, "display_name", None)
            if display_name is None and isinstance(model, dict):
                display_name = model.get("display_name")

            models.append({"id": model_id, "display_name": display_name})

        logger.info("OpenAI list_models succeeded: count=%d", len(models))
        return models


def _encode_image_path(path: Path) -> str:
    """Generate a data URL for the provided image path."""
    data = path.read_bytes()
    encoded = base64.b64encode(data).decode("utf-8")
    mime_type = mimetypes.guess_type(path.name)[0] or "image/png"
    return f"data:{mime_type};base64,{encoded}"
