<?php
namespace boru\boruai\Models\Tool;

use boru\boruai\Models\Response;
use boru\boruai\Openai\Response\Parts\FunctionCall;
use boru\boruai\Openai\Response\Parts\OutputMessage;

class ToolHandler {
    private $haveToolCalls = false;

    private $toolCalls = [];
    private $toolResponses = [];

    /** @var Response|null */
    private $response = null;

    /** @var \boru\boruai\Models\Tool\ToolContext|null */
    private $toolContext = null;

    /**
     * 
     * @param Response $response
     */
    public function __construct($response=null, \boru\boruai\Models\Tool\ToolContext $toolContext = null) {
        $this->response($response);
        if ($toolContext !== null) {
            $this->toolContext = $toolContext;
        }
    }
    /**
     * 
     * @param Response|null $response 
     * @return Response|null 
     */
    public function response($response=null) {
        if($response!==null) {
            $this->response = $response;
        }
        return $this->response;
    }

    /**
     * Get or set the ToolContext used for this handler.
     *
     * @param \boru\boruai\Models\Tool\ToolContext|null $context
     * @return \boru\boruai\Models\Tool\ToolContext|null
     */
    public function toolContext($context = null)
    {
        if ($context !== null) {
            $this->toolContext = $context;
        }
        return $this->toolContext;
    }
    public function haveToolCalls($haveFunctionCalls=null) {
        if($haveFunctionCalls!==null) {
            $this->haveToolCalls = $haveFunctionCalls ? true : false;
        }
        return $this->haveToolCalls ? true : false;
    }

    /**
     * Handles a message or array of messages, processes any tool calls, and stores tool call responses
     * @param OutputMessage|OutputMessage[] $messages 
     * @param array $options 
     * @return string 
     */
    public function handle($messages, $options=[]) {
        foreach($messages as $message) {
            // 1) Echo back reasoning items emitted by the model
            if ($message instanceof OutputMessage && method_exists($message, 'toArray')) {
                $arr = $message->toArray();
                if (isset($arr['type']) && $arr['type'] === 'reasoning') {
                    // IMPORTANT: add verbatim; do not edit id/summary
                    $this->addToInput($arr);
                    continue;
                }
            }
            if($message instanceof FunctionCall) {
                $this->addToolMessage($message);
                $functionData = $message->toArray();
                $functionData['type'] = 'function_call.started';
                $this->makeEvent($functionData);
                //Output::outLine("Function Call ID: ",$message->id());
                //Output::outLine("Function Call Name: ",$message->name());
                $data = $message->run($this->toolContext());
                if($data) {
                    //if $data is an array of arrays.. eg [[output => "value"]], take the first element
                    if (is_array($data) && isset($data[0]) && is_array($data[0]) && isset($data[0]['output'])) {
                        $data = $data[0];
                    }
                    $this->haveToolCalls(true);
                    $this->addToolResult($message->id(),$data);
                    $functionData['type'] = 'function_call.completed';
                    $functionData['output'] = isset($data["output"]) ? $data["output"] : null;
                    $this->makeEvent($functionData);
                } else {
                    $functionData['type'] = 'function_call.failed';
                    $this->makeEvent($functionData);
                }
            }
        }
    }

    public function addToolMessage($message) {
        if (!$message instanceof FunctionCall) {
            return;
        }

        $this->toolCalls[$message->id()] = $message;

        // 4. Add function_call as usual
        $this->addToInput($message);
    }

    public function addToolResult($functionCallId,$result) {
        $this->toolResponses[$functionCallId] = $result;
        $this->addToInput($result);
    }

    public function addToInput($data) {
        $response = $this->response();
        if($response) {
            $response->addToInput($data);
            return true;
        }
        return false;
    }
    public function makeEvent($data) {
        $response = $this->response();
        if($response) {
            $response->makeEvent($data);
            return true;
        }
        return false;
    }

    private function makeAutoReasoning($toolName) {
        return [
            "id" => "rs_" . uniqid(),
            "type" => "reasoning",
            "summary" => [[
                "type" => "summary_text",
                "text" => "Tool call required: {$toolName}"
            ]]
        ];
    }
}