<?php
namespace boru\ai\Schema;

use boru\ai\Asset\Attachment;
use boru\ai\Asset\ProviderAssetBinding;

class Request
{
    /** @var string */
    protected $provider;

    /** @var string */
    protected $model;

    /** @var string */
    protected $instructions;

    /** @var Message[] */
    protected $messages = array();

    /** @var array */
    protected $tools = array();

    /** @var ToolResult[] */
    protected $toolResults = array();

    /** @var Attachment[] */
    protected $attachments = array();

    /** @var ProviderAssetBinding[] */
    protected $assetBindings = array();

    /** @var array */
    protected $options = array();

    /** @var array */
    protected $metadata = array();

    public function provider($value = null)
    {
        if ($value !== null) {
            $this->provider = (string)$value;
            return $this;
        }
        return $this->provider;
    }

    public function model($value = null)
    {
        if ($value !== null) {
            $this->model = (string)$value;
            return $this;
        }
        return $this->model;
    }

    public function instructions($value = null)
    {
        if ($value !== null) {
            $this->instructions = (string)$value;
            return $this;
        }
        return $this->instructions;
    }

    public function addMessage($message, $role = 'user')
    {
        if ($message instanceof Message) {
            $this->messages[] = $message;
            return $this;
        }

        if (is_string($message)) {
            $this->messages[] = new Message($role, array($message));
            return $this;
        }

        if (is_array($message)) {
            $msgRole = isset($message['role']) ? $message['role'] : $role;
            $parts = isset($message['parts']) ? $message['parts'] : (isset($message['content']) ? $message['content'] : array());
            $metadata = isset($message['metadata']) && is_array($message['metadata']) ? $message['metadata'] : array();
            $this->messages[] = new Message($msgRole, $parts, $metadata);
            return $this;
        }

        throw new \InvalidArgumentException('Invalid message');
    }

    public function messages($messages = null)
    {
        if ($messages !== null) {
            $this->messages = array();
            if (is_array($messages)) {
                foreach ($messages as $message) {
                    $this->addMessage($message);
                }
            } else {
                $this->addMessage($messages);
            }
            return $this;
        }
        return $this->messages;
    }

    public function addTool($tool)
    {
        $this->tools[] = $tool;
        return $this;
    }

    public function tools($tools = null)
    {
        if ($tools !== null) {
            $this->tools = is_array($tools) ? $tools : array($tools);
            return $this;
        }
        return $this->tools;
    }

    public function addToolResult($toolResult)
    {
        if (!$toolResult instanceof ToolResult) {
            throw new \InvalidArgumentException('Tool result must be a ToolResult instance');
        }
        $this->toolResults[] = $toolResult;
        return $this;
    }

    public function toolResults($toolResults = null)
    {
        if ($toolResults !== null) {
            $this->toolResults = array();
            foreach ((array)$toolResults as $toolResult) {
                $this->addToolResult($toolResult);
            }
            return $this;
        }
        return $this->toolResults;
    }

    public function addAttachment(Attachment $attachment)
    {
        $this->attachments[] = $attachment;
        return $this;
    }

    public function attachments($attachments = null)
    {
        if ($attachments !== null) {
            $this->attachments = array();
            foreach ((array)$attachments as $attachment) {
                if ($attachment instanceof Attachment) {
                    $this->attachments[] = $attachment;
                }
            }
            return $this;
        }
        return $this->attachments;
    }

    public function addAssetBinding(ProviderAssetBinding $binding)
    {
        $this->assetBindings[] = $binding;
        return $this;
    }

    public function assetBindings($bindings = null)
    {
        if ($bindings !== null) {
            $this->assetBindings = array();
            foreach ((array)$bindings as $binding) {
                if ($binding instanceof ProviderAssetBinding) {
                    $this->assetBindings[] = $binding;
                }
            }
            return $this;
        }
        return $this->assetBindings;
    }

    public function findAssetBinding($assetId, $provider = null)
    {
        foreach ($this->assetBindings as $binding) {
            if ($binding->assetId() !== $assetId) {
                continue;
            }
            if ($provider !== null && $binding->provider() !== $provider) {
                continue;
            }
            return $binding;
        }
        return false;
    }

    public function option($key, $value = null)
    {
        if (func_num_args() > 1) {
            $this->options[$key] = $value;
            return $this;
        }
        return isset($this->options[$key]) ? $this->options[$key] : null;
    }

    public function options($options = null)
    {
        if ($options !== null) {
            $this->options = is_array($options) ? $options : array();
            return $this;
        }
        return $this->options;
    }

    public function metadata($key = null, $value = null)
    {
        if ($key === null) {
            return $this->metadata;
        }
        if (is_array($key)) {
            $this->metadata = $key;
            return $this;
        }
        if (func_num_args() > 1) {
            $this->metadata[$key] = $value;
            return $this;
        }
        return isset($this->metadata[$key]) ? $this->metadata[$key] : null;
    }

    public function copy()
    {
        $copy = new self();
        $copy->provider($this->provider());
        $copy->model($this->model());
        $copy->instructions($this->instructions());
        $copy->messages($this->messages());
        $copy->tools($this->tools());
        $copy->toolResults($this->toolResults());
        $copy->attachments($this->attachments());
        $copy->assetBindings($this->assetBindings());
        $copy->options($this->options());
        $copy->metadata($this->metadata());
        return $copy;
    }

    public function toArray()
    {
        $messages = array();
        foreach ($this->messages() as $message) {
            $messages[] = $message instanceof Message ? $message->toArray() : $message;
        }

        $toolResults = array();
        foreach ($this->toolResults() as $toolResult) {
            $toolResults[] = $toolResult instanceof ToolResult ? $toolResult->toArray() : $toolResult;
        }

        $attachments = array();
        foreach ($this->attachments() as $attachment) {
            $attachments[] = $attachment instanceof Attachment ? $attachment->toArray() : $attachment;
        }

        $assetBindings = array();
        foreach ($this->assetBindings() as $binding) {
            $assetBindings[] = $binding instanceof ProviderAssetBinding ? $binding->toArray() : $binding;
        }

        return array(
            'provider' => $this->provider(),
            'model' => $this->model(),
            'instructions' => $this->instructions(),
            'messages' => $messages,
            'tools' => $this->tools(),
            'tool_results' => $toolResults,
            'attachments' => $attachments,
            'asset_bindings' => $assetBindings,
            'options' => $this->options(),
            'metadata' => $this->metadata(),
        );
    }
}
