<?php

declare(strict_types=1);

namespace Anthropic\Beta\Messages;

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

/**
 * @phpstan-import-type BetaContentBlockVariants from \Anthropic\Beta\Messages\BetaContentBlock
 * @phpstan-import-type BetaContainerShape from \Anthropic\Beta\Messages\BetaContainer
 * @phpstan-import-type BetaContentBlockShape from \Anthropic\Beta\Messages\BetaContentBlock
 * @phpstan-import-type BetaContextManagementResponseShape from \Anthropic\Beta\Messages\BetaContextManagementResponse
 * @phpstan-import-type BetaDiagnosticsShape from \Anthropic\Beta\Messages\BetaDiagnostics
 * @phpstan-import-type BetaRefusalStopDetailsShape from \Anthropic\Beta\Messages\BetaRefusalStopDetails
 * @phpstan-import-type BetaUsageShape from \Anthropic\Beta\Messages\BetaUsage
 *
 * @phpstan-type BetaMessageShape = array{
 *   id: string,
 *   container: null|BetaContainer|BetaContainerShape,
 *   content: list<BetaContentBlockShape>,
 *   contextManagement: null|BetaContextManagementResponse|BetaContextManagementResponseShape,
 *   diagnostics: null|BetaDiagnostics|BetaDiagnosticsShape,
 *   model: string|Model|value-of<Model>,
 *   role: 'assistant',
 *   stopDetails: null|BetaRefusalStopDetails|BetaRefusalStopDetailsShape,
 *   stopReason: null|BetaStopReason|value-of<BetaStopReason>,
 *   stopSequence: string|null,
 *   type: 'message',
 *   usage: BetaUsage|BetaUsageShape,
 * }
 */
final class BetaMessage implements BaseModel
{
    /** @use SdkModel<BetaMessageShape> */
    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 ?BetaContainer $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<BetaContentBlockVariants> $content
     */
    #[Required(list: BetaContentBlock::class)]
    public array $content;

    /**
     * Context management response.
     *
     * Information about context management strategies applied during the request.
     */
    #[Required('context_management')]
    public ?BetaContextManagementResponse $contextManagement;

    /**
     * Response envelope for request-level diagnostics. Present (possibly
     * null) whenever the caller supplied `diagnostics` on the request.
     */
    #[Required]
    public ?BetaDiagnostics $diagnostics;

    /**
     * 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 ?BetaRefusalStopDetails $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<BetaStopReason>|null $stopReason
     */
    #[Required('stop_reason', enum: BetaStopReason::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 BetaUsage $usage;

    /**
     * `new BetaMessage()` is missing required properties by the API.
     *
     * To enforce required parameters use
     * ```
     * BetaMessage::with(
     *   id: ...,
     *   container: ...,
     *   content: ...,
     *   contextManagement: ...,
     *   diagnostics: ...,
     *   model: ...,
     *   stopDetails: ...,
     *   stopReason: ...,
     *   stopSequence: ...,
     *   usage: ...,
     * )
     * ```
     *
     * Otherwise ensure the following setters are called
     *
     * ```
     * (new BetaMessage)
     *   ->withID(...)
     *   ->withContainer(...)
     *   ->withContent(...)
     *   ->withContextManagement(...)
     *   ->withDiagnostics(...)
     *   ->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 BetaTextBlock && 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 BetaContainer|BetaContainerShape|null $container
     * @param list<BetaContentBlockShape> $content
     * @param BetaContextManagementResponse|BetaContextManagementResponseShape|null $contextManagement
     * @param BetaDiagnostics|BetaDiagnosticsShape|null $diagnostics
     * @param string|Model|value-of<Model> $model
     * @param BetaRefusalStopDetails|BetaRefusalStopDetailsShape|null $stopDetails
     * @param BetaStopReason|value-of<BetaStopReason>|null $stopReason
     * @param BetaUsage|BetaUsageShape $usage
     */
    public static function with(
        string $id,
        BetaContainer|array|null $container,
        array $content,
        BetaContextManagementResponse|array|null $contextManagement,
        BetaDiagnostics|array|null $diagnostics,
        Model|string $model,
        BetaRefusalStopDetails|array|null $stopDetails,
        BetaStopReason|string|null $stopReason,
        ?string $stopSequence,
        BetaUsage|array $usage,
    ): self {
        $self = new self;

        $self['id'] = $id;
        $self['container'] = $container;
        $self['content'] = $content;
        $self['contextManagement'] = $contextManagement;
        $self['diagnostics'] = $diagnostics;
        $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 BetaContainer|BetaContainerShape|null $container
     */
    public function withContainer(BetaContainer|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<BetaContentBlockShape> $content
     */
    public function withContent(array $content): self
    {
        $self = clone $this;
        $self['content'] = $content;

        return $self;
    }

    /**
     * Context management response.
     *
     * Information about context management strategies applied during the request.
     *
     * @param BetaContextManagementResponse|BetaContextManagementResponseShape|null $contextManagement
     */
    public function withContextManagement(
        BetaContextManagementResponse|array|null $contextManagement
    ): self {
        $self = clone $this;
        $self['contextManagement'] = $contextManagement;

        return $self;
    }

    /**
     * Response envelope for request-level diagnostics. Present (possibly
     * null) whenever the caller supplied `diagnostics` on the request.
     *
     * @param BetaDiagnostics|BetaDiagnosticsShape|null $diagnostics
     */
    public function withDiagnostics(
        BetaDiagnostics|array|null $diagnostics
    ): self {
        $self = clone $this;
        $self['diagnostics'] = $diagnostics;

        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 BetaRefusalStopDetails|BetaRefusalStopDetailsShape|null $stopDetails
     */
    public function withStopDetails(
        BetaRefusalStopDetails|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 BetaStopReason|value-of<BetaStopReason>|null $stopReason
     */
    public function withStopReason(BetaStopReason|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 BetaUsage|BetaUsageShape $usage
     */
    public function withUsage(BetaUsage|array $usage): self
    {
        $self = clone $this;
        $self['usage'] = $usage;

        return $self;
    }
}
