from collections.abc import Sequence
from dataclasses import KW_ONLY, dataclass, field
from datetime import datetime
from typing import Any

from genai_prices import types as genai_types

from pydantic_ai._genai_prices import calculate_price_for_usage
from pydantic_ai._utils import now_utc as _now_utc
from pydantic_ai.messages import BinaryImage
from pydantic_ai.usage import RequestUsage


@dataclass
class GeneratedImage:
    """One generated image with normalized content and provider metadata."""

    content: BinaryImage
    """The generated image as normalized binary content."""

    _: KW_ONLY

    revised_prompt: str | None = None
    """Provider-revised or enhanced prompt, if available."""

    output_format: str | None = None
    """Generated image output format, derived from the bytes the provider returned."""

    provider_details: dict[str, Any] | None = None
    """Provider-specific details for this generated image."""


@dataclass
class ImageGenerationResult:
    """The result of an image generation operation."""

    images: Sequence[GeneratedImage]
    """Generated images."""

    _: KW_ONLY

    prompt: str
    """The input prompt used for generation."""

    model_name: str
    """The name of the model that generated the images."""

    provider_name: str
    """The name of the provider."""

    timestamp: datetime = field(default_factory=_now_utc)
    """When the image generation request was made."""

    usage: RequestUsage = field(default_factory=RequestUsage)
    """Usage statistics for this request."""

    provider_details: dict[str, Any] | None = None
    """Provider-specific details from the response."""

    provider_response_id: str | None = None
    """Unique identifier for this response from the provider, if available."""

    provider_url: str | None = None
    """Provider API URL, if available."""

    @property
    def image(self) -> BinaryImage:
        """The first generated image. Use `images` when the request asked for more than one.

        A result always holds at least one image: the
        [`ImageGenerationModel.generate`][pydantic_ai.images.ImageGenerationModel.generate] contract
        requires an implementation with nothing to return to raise instead of returning an empty result.
        """
        return self.images[0].content

    def cost(self) -> genai_types.PriceCalculation:
        """Calculate the cost of the image generation request.

        Uses [`genai-prices`](https://github.com/pydantic/genai-prices) for pricing data.

        Models priced per token are covered, such as the GPT Image and Gemini image families. The
        Grok Imagine family raises `LookupError`: it has no entry in the pricing data, and there is
        no unit that counts generated images to price it with.

        Returns:
            A price calculation object with `total_price`, `input_price`, and other cost details.

        Raises:
            LookupError: If pricing data is not available for this model/provider.
        """
        assert self.model_name, 'Model name is required to calculate price'
        return calculate_price_for_usage(
            self.usage,
            model_name=self.model_name,
            provider_api_url=self.provider_url,
            provider_name=self.provider_name,
            genai_request_timestamp=self.timestamp,
        )
