<?php

declare(strict_types=1);

namespace Anthropic\Messages;

use Anthropic\Core\Attributes\Required;
use Anthropic\Core\Concerns\SdkModel;
use Anthropic\Core\Contracts\BaseModel;
use Anthropic\Lib\Contracts\StructuredOutputModel;

/**
 * @phpstan-import-type ContentBlockVariants from \Anthropic\Messages\ContentBlock
 * @phpstan-import-type ContainerShape from \Anthropic\Messages\Container
 * @phpstan-import-type ContentBlockShape from \Anthropic\Messages\ContentBlock
 * @phpstan-import-type RefusalStopDetailsShape from \Anthropic\Messages\RefusalStopDetails
 * @phpstan-import-type UsageShape from \Anthropic\Messages\Usage
 *
 * @phpstan-type MessageShape = array{
 *   id: string,
 *   container: null|Container|ContainerShape,
 *   content: list<ContentBlockShape>,
 *   model: string|Model|value-of<Model>,
 *   role: 'assistant',
 *   stopDetails: null|RefusalStopDetails|RefusalStopDetailsShape,
 *   stopReason: null|StopReason|value-of<StopReason>,
 *   stopSequence: string|null,
 *   type: 'message',
 *   usage: Usage|UsageShape,
 * }
 */
final class Message implements BaseModel
{
    /** @use SdkModel<MessageShape> */
    use SdkModel;

    /**
     * Conversational role of the generated message.
     *
     * This will always be `"assistant"`.
     *
     * @var 'assistant' $role
     */
    #[Required]
    public string $role = 'assistant';

    /**
     * Object type.
     *
     * For Messages, this is always `"message"`.
     *
     * @var 'message' $type
     */
    #[Required]
    public string $type = 'message';

    /**
     * Unique object identifier.
     *
     * The format and length of IDs may change over time.
     */
    #[Required]
    public string $id;

    /**
     * Information about the container used in the request (for the code execution tool).
     */
    #[Required]
    public ?Container $container;

    /**
     * Content generated by the model.
     *
     * This is an array of content blocks, each of which has a `type` that determines its shape.
     *
     * Example:
     *
     * ```json
     * [{"type": "text", "text": "Hi, I'm Claude."}]
     * ```
     *
     * If the request input `messages` ended with an `assistant` turn, then the response `content` will continue directly from that last turn. You can use this to constrain the model's output.
     *
     * For example, if the input `messages` were:
     * ```json
     * [
     *   {"role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun"},
     *   {"role": "assistant", "content": "The best answer is ("}
     * ]
     * ```
     *
     * Then the response `content` might be:
     *
     * ```json
     * [{"type": "text", "text": "B)"}]
     * ```
     *
     * @var list<ContentBlockVariants> $content
     */
    #[Required(list: ContentBlock::class)]
    public array $content;

    /**
     * The model that will complete your prompt.
     *
     * See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options.
     *
     * @var string|value-of<Model> $model
     */
    #[Required(enum: Model::class)]
    public string $model;

    /**
     * Structured information about a refusal.
     */
    #[Required('stop_details')]
    public ?RefusalStopDetails $stopDetails;

    /**
     * The reason that we stopped.
     *
     * This may be one the following values:
     * * `"end_turn"`: the model reached a natural stopping point
     * * `"max_tokens"`: we exceeded the requested `max_tokens` or the model's maximum
     * * `"stop_sequence"`: one of your provided custom `stop_sequences` was generated
     * * `"tool_use"`: the model invoked one or more tools
     * * `"pause_turn"`: we paused a long-running turn. You may provide the response back as-is in a subsequent request to let the model continue.
     * * `"refusal"`: when streaming classifiers intervene to handle potential policy violations
     *
     * In non-streaming mode this value is always non-null. In streaming mode, it is null in the `message_start` event and non-null otherwise.
     *
     * @var value-of<StopReason>|null $stopReason
     */
    #[Required('stop_reason', enum: StopReason::class)]
    public ?string $stopReason;

    /**
     * Which custom stop sequence was generated, if any.
     *
     * This value will be a non-null string if one of your custom stop sequences was generated.
     */
    #[Required('stop_sequence')]
    public ?string $stopSequence;

    /**
     * Billing and rate-limit usage.
     *
     * Anthropic's API bills and rate-limits by token counts, as tokens represent the underlying cost to our systems.
     *
     * Under the hood, the API transforms requests into a format suitable for the model. The model's output then goes through a parsing stage before becoming an API response. As a result, the token counts in `usage` will not match one-to-one with the exact visible content of an API request or response.
     *
     * For example, `output_tokens` will be non-zero, even for an empty string response from Claude.
     *
     * Total input tokens in a request is the summation of `input_tokens`, `cache_creation_input_tokens`, and `cache_read_input_tokens`.
     */
    #[Required]
    public Usage $usage;

    /**
     * `new Message()` is missing required properties by the API.
     *
     * To enforce required parameters use
     * ```
     * Message::with(
     *   id: ...,
     *   container: ...,
     *   content: ...,
     *   model: ...,
     *   stopDetails: ...,
     *   stopReason: ...,
     *   stopSequence: ...,
     *   usage: ...,
     * )
     * ```
     *
     * Otherwise ensure the following setters are called
     *
     * ```
     * (new Message)
     *   ->withID(...)
     *   ->withContainer(...)
     *   ->withContent(...)
     *   ->withModel(...)
     *   ->withStopDetails(...)
     *   ->withStopReason(...)
     *   ->withStopSequence(...)
     *   ->withUsage(...)
     * ```
     */
    public function __construct()
    {
        $this->initialize();
    }

    /**
     * Gets the parsed structured output from the message content.
     *
     * When using output_config with a StructuredOutputModel class, this method
     * returns the parsed model instance from the first content block that has
     * a parsed value. If no parsed content is found, returns null.
     *
     * @return StructuredOutputModel|array{error: string}|null
     */
    public function parsedOutput(): mixed
    {
        foreach ($this->content as $block) {
            if ($block instanceof TextBlock && null !== $block->parsed) {
                return $block->parsed;
            }
        }

        return null;
    }

    /**
     * Construct an instance from the required parameters.
     *
     * You must use named parameters to construct any parameters with a default value.
     *
     * @param Container|ContainerShape|null $container
     * @param list<ContentBlockShape> $content
     * @param string|Model|value-of<Model> $model
     * @param RefusalStopDetails|RefusalStopDetailsShape|null $stopDetails
     * @param StopReason|value-of<StopReason>|null $stopReason
     * @param Usage|UsageShape $usage
     */
    public static function with(
        string $id,
        Container|array|null $container,
        array $content,
        Model|string $model,
        RefusalStopDetails|array|null $stopDetails,
        StopReason|string|null $stopReason,
        ?string $stopSequence,
        Usage|array $usage,
    ): self {
        $self = new self;

        $self['id'] = $id;
        $self['container'] = $container;
        $self['content'] = $content;
        $self['model'] = $model;
        $self['stopDetails'] = $stopDetails;
        $self['stopReason'] = $stopReason;
        $self['stopSequence'] = $stopSequence;
        $self['usage'] = $usage;

        return $self;
    }

    /**
     * Unique object identifier.
     *
     * The format and length of IDs may change over time.
     */
    public function withID(string $id): self
    {
        $self = clone $this;
        $self['id'] = $id;

        return $self;
    }

    /**
     * Information about the container used in the request (for the code execution tool).
     *
     * @param Container|ContainerShape|null $container
     */
    public function withContainer(Container|array|null $container): self
    {
        $self = clone $this;
        $self['container'] = $container;

        return $self;
    }

    /**
     * Content generated by the model.
     *
     * This is an array of content blocks, each of which has a `type` that determines its shape.
     *
     * Example:
     *
     * ```json
     * [{"type": "text", "text": "Hi, I'm Claude."}]
     * ```
     *
     * If the request input `messages` ended with an `assistant` turn, then the response `content` will continue directly from that last turn. You can use this to constrain the model's output.
     *
     * For example, if the input `messages` were:
     * ```json
     * [
     *   {"role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun"},
     *   {"role": "assistant", "content": "The best answer is ("}
     * ]
     * ```
     *
     * Then the response `content` might be:
     *
     * ```json
     * [{"type": "text", "text": "B)"}]
     * ```
     *
     * @param list<ContentBlockShape> $content
     */
    public function withContent(array $content): self
    {
        $self = clone $this;
        $self['content'] = $content;

        return $self;
    }

    /**
     * The model that will complete your prompt.
     *
     * See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options.
     *
     * @param string|Model|value-of<Model> $model
     */
    public function withModel(Model|string $model): self
    {
        $self = clone $this;
        $self['model'] = $model;

        return $self;
    }

    /**
     * Conversational role of the generated message.
     *
     * This will always be `"assistant"`.
     *
     * @param 'assistant' $role
     */
    public function withRole(string $role): self
    {
        $self = clone $this;
        $self['role'] = $role;

        return $self;
    }

    /**
     * Structured information about a refusal.
     *
     * @param RefusalStopDetails|RefusalStopDetailsShape|null $stopDetails
     */
    public function withStopDetails(
        RefusalStopDetails|array|null $stopDetails
    ): self {
        $self = clone $this;
        $self['stopDetails'] = $stopDetails;

        return $self;
    }

    /**
     * The reason that we stopped.
     *
     * This may be one the following values:
     * * `"end_turn"`: the model reached a natural stopping point
     * * `"max_tokens"`: we exceeded the requested `max_tokens` or the model's maximum
     * * `"stop_sequence"`: one of your provided custom `stop_sequences` was generated
     * * `"tool_use"`: the model invoked one or more tools
     * * `"pause_turn"`: we paused a long-running turn. You may provide the response back as-is in a subsequent request to let the model continue.
     * * `"refusal"`: when streaming classifiers intervene to handle potential policy violations
     *
     * In non-streaming mode this value is always non-null. In streaming mode, it is null in the `message_start` event and non-null otherwise.
     *
     * @param StopReason|value-of<StopReason>|null $stopReason
     */
    public function withStopReason(StopReason|string|null $stopReason): self
    {
        $self = clone $this;
        $self['stopReason'] = $stopReason;

        return $self;
    }

    /**
     * Which custom stop sequence was generated, if any.
     *
     * This value will be a non-null string if one of your custom stop sequences was generated.
     */
    public function withStopSequence(?string $stopSequence): self
    {
        $self = clone $this;
        $self['stopSequence'] = $stopSequence;

        return $self;
    }

    /**
     * Object type.
     *
     * For Messages, this is always `"message"`.
     *
     * @param 'message' $type
     */
    public function withType(string $type): self
    {
        $self = clone $this;
        $self['type'] = $type;

        return $self;
    }

    /**
     * Billing and rate-limit usage.
     *
     * Anthropic's API bills and rate-limits by token counts, as tokens represent the underlying cost to our systems.
     *
     * Under the hood, the API transforms requests into a format suitable for the model. The model's output then goes through a parsing stage before becoming an API response. As a result, the token counts in `usage` will not match one-to-one with the exact visible content of an API request or response.
     *
     * For example, `output_tokens` will be non-zero, even for an empty string response from Claude.
     *
     * Total input tokens in a request is the summation of `input_tokens`, `cache_creation_input_tokens`, and `cache_read_input_tokens`.
     *
     * @param Usage|UsageShape $usage
     */
    public function withUsage(Usage|array $usage): self
    {
        $self = clone $this;
        $self['usage'] = $usage;

        return $self;
    }
}
