<?php

declare(strict_types=1);

namespace Gemini\Data;

use Gemini\Contracts\Arrayable;

/**
 * A datatype containing media that is part of a multi-part Content message.
 * A Part consists of data which has an associated datatype. A Part can only contain one of the accepted types in Part.data.
 * A Part must have a fixed IANA MIME type identifying the type and subtype of the media if the inlineData field is filled with raw bytes.
 *
 * https://ai.google.dev/api/caching#Part
 */
final class Part implements Arrayable
{
    /**
     * @param  string|null  $text  Inline text.
     * @param  Blob|null  $inlineData  Inline media bytes.
     * @param  UploadedFile|null  $fileData  Uploaded file information.
     * @param  FunctionCall|null  $functionCall  Function call.
     * @param  FunctionResponse|null  $functionResponse  Function call response.
     * @param  ExecutableCode|null  $executableCode  Code generated by the model that is meant to be executed.
     * @param  CodeExecutionResult|null  $codeExecutionResult  Result of executing the ExecutableCode.
     */
    public function __construct(
        public readonly ?string $text = null,
        public readonly ?Blob $inlineData = null,
        public readonly ?UploadedFile $fileData = null,
        public readonly ?FunctionCall $functionCall = null,
        public readonly ?FunctionResponse $functionResponse = null,
        public readonly ?ExecutableCode $executableCode = null,
        public readonly ?CodeExecutionResult $codeExecutionResult = null,
    ) {}

    /**
     * @param  array{ text: ?string, inlineData: ?array{ mimeType: string, data: string }, fileData: ?array{ fileUri: string, mimeType: string }, functionCall: ?array{ name: string, args: array<string, mixed>|null }, functionResponse: ?array{ name: string, response: array<string, mixed> }, executableCode?: array{ language: string, code: string }, codeExecutionResult?: array{ outcome: string, output: string } }  $attributes
     */
    public static function from(array $attributes): self
    {
        return new self(
            text: $attributes['text'] ?? null,
            inlineData: isset($attributes['inlineData']) ? Blob::from($attributes['inlineData']) : null,
            fileData: isset($attributes['fileData']) ? UploadedFile::from($attributes['fileData']) : null,
            functionCall: isset($attributes['functionCall']) ? FunctionCall::from($attributes['functionCall']) : null,
            functionResponse: isset($attributes['functionResponse']) ? FunctionResponse::from($attributes['functionResponse']) : null,
            executableCode: isset($attributes['executableCode']) ? ExecutableCode::from($attributes['executableCode']) : null,
            codeExecutionResult: isset($attributes['codeExecutionResult']) ? CodeExecutionResult::from($attributes['codeExecutionResult']) : null,
        );
    }

    public function toArray(): array
    {
        $data = [];

        if ($this->text !== null) {
            $data['text'] = $this->text;
        }

        if ($this->inlineData !== null) {
            $data['inlineData'] = $this->inlineData->toArray();
        }

        if ($this->fileData !== null) {
            $data['fileData'] = $this->fileData->toArray();
        }

        if ($this->functionCall !== null) {
            $data['functionCall'] = $this->functionCall->toArray();
        }

        if ($this->functionResponse !== null) {
            $data['functionResponse'] = $this->functionResponse->toArray();
        }

        if ($this->executableCode !== null) {
            $data['executableCode'] = $this->executableCode->toArray();
        }

        if ($this->codeExecutionResult !== null) {
            $data['codeExecutionResult'] = $this->codeExecutionResult->toArray();
        }

        return $data;
    }
}
