<?php
namespace boru\boruai\Models;

use boru\dot\Dot;

class Tool {
    private static $tools = [];

    private $name;
    private $arguments = [];
    private $tool_call_id;
    private $callback;

    private $endpointType = "chat";

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

    public function __construct($input = [],$name=null) {
        //openai tool callback:
        if(
                isset($input["type"]) && $input["type"] == "function"
            && isset($input["id"])
            && isset($input["function"])
            && isset($input["function"]["name"])
            && isset($input["function"]["arguments"])
        ) {
            $this->name = $input["function"]["name"];
            $this->arguments = $input["function"]["arguments"];
            $this->tool_call_id = $input["id"];
        } elseif(
            isset($input["type"]) && $input["type"] == "function_call"
            && isset($input["name"])
            && isset($input["arguments"])
            && isset($input["call_id"])
        ) {
            $this->name = $input["name"];
            $this->arguments = $input["arguments"];
            $this->tool_call_id = $input["call_id"];
            $this->forResponse();
        
        } else {
            $this->name = $name;
            $this->arguments = $input;
        }
        if(isset(static::$tools[$this->name])) {
            $this->callback = static::$tools[$this->name];
        }
        if(is_string($this->arguments) && substr($this->arguments,0,1) == "{") {
            $this->arguments = json_decode($this->arguments,true);
        }elseif(is_string($this->arguments) && substr($this->arguments,0,1) == "[") {
            $this->arguments = json_decode($this->arguments,true);
        }
    }
    public static function register($name,$callback) {
        static::$tools[$name] = $callback;
    }

    /**
     * Attach a context object for this tool invocation.
     *
     * @param \boru\boruai\Models\Tool\ToolContext|null $context
     * @return $this
     */
    public function setContext($context)
    {
        $this->context = $context;
        return $this;
    }

    public function context($dotKey=null,$default=null) {
        if($this->context) {
            return $this->context->get($dotKey,$default);
        }
        return $default;
    }

    /**
     * Get the context object for this tool invocation, if any.
     *
     * @return \boru\boruai\Models\Tool\ToolContext|null
     */
    public function getContext()
    {
        return $this->context;
    }

    public function forChat() {
        $this->endpointType = "chat";
        return $this;
    }
    public function forResponse() {
        $this->endpointType = "response";
        return $this;
    }
    public function endpointType($endpointType=null) {
        if($endpointType !== null) {
            $this->endpointType = $endpointType;
            return $this;
        }
        return $this->endpointType;
    }

    public function name($name=null) {
        if($name !== null) {
            $this->name = $name;
            return $this;
        }
    }
    public function arguments($arguments=null) {
        if($arguments !== null) {
            $this->arguments = $arguments;
            return $this;
        }
    }
    public function tool_call_id($tool_call_id=null) {
        if($tool_call_id !== null) {
            $this->tool_call_id = $tool_call_id;
            return $this;
        }
    }
    public function callback($callback=null) {
        if($callback !== null) {
            $this->callback = $callback;
            return $this;
        }
    }

    public function run() {
        if($this->callback) {
            $output = call_user_func($this->callback,$this);
            if($output !== false) {
                if(is_array($output) || is_object($output)) {
                    $output = json_encode($output,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
                }
                return $this->output($output);
            }
        }
        return null;
    }

    private function output($output) {
        if($this->endpointType == "chat") {
            return [
                "tool_call_id"=>$this->tool_call_id,
                "output"=>$output
            ];
        }

        return [
            "type"=> "function_call_output",
            "call_id"=>$this->tool_call_id,
            "output"=>$output
        ];
    }

    public function get($key=null,$default=null) {
        if($key === null) {
            return $this->arguments;
        }
        return Dot::get($this->arguments,$key,$default);
    }
    public function set($key,$value) {
        return Dot::set($this->arguments,$key,$value);
    }

    public static function printArray($array,&$output=[]) {
        if(is_array($array)) {
            foreach($array as $key=>$value) {
                if(is_array($value)) {
                    $output[] = "";
                    if(!is_numeric($key)) {
                        
                        $output[] = "$key: ";
                    }
                    static::printArray($value,$output);
                } else {
                    $output[] = "$key: $value";
                }
            }
        } else {
            $output[] = $array;
        }
        return implode("\n",$output);
    }

    public static function printDebug($output) {
        
    }


}