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

use boru\boruai\Openai\OpenAI;

class ToolFunction extends BaseModel {
    private static $tableName;
    private $id;
    private $type = "function";
    private $assistantId;
    private $name;
    private $description;
    private $callable;
    private $parameters;
    private $strict = true;

    public static function tableName($tableName=null) {
        if($tableName) {
            static::$tableName = $tableName;
        }
        if(static::$tableName === null) {
            static::$tableName = OpenAI::table("tools","boru_openai_tools");
        }
        return static::$tableName;
    }

    public function toArray() {
        $return = [
            "name"=>$this->name(),
            "description"=>$this->description(),
            "parameters"=>$this->parameters()
        ];
        if($this->strict) {
            $return["strict"] = $this->strict;
        }
    }

    public function id($id=null) {
        if ($id !== null) {
            $this->id = $id;
        }
        return $this->id;
    }
    public function type($type=null) {
        if ($type !== null) {
            $this->type = $type;
        }
        return $this->type;
    }
    public function assistantId($assistantId=null) {
        if ($assistantId !== null) {
            $this->assistantId = $assistantId;
        }
        return $this->assistantId;
    }
    public function name($name=null) {
        if ($name !== null) {
            $this->name = $name;
        }
        return $this->name;
    }
    public function description($description=null) {
        if ($description !== null) {
            $this->description = $description;
        }
        return $this->description;
    }
    public function callable($callable=null) {
        if ($callable !== null) {
            if(is_string($callable) && !empty($callable)) {
                $callable = json_decode($callable,true);
                if(!$callable) {
                    throw new \Exception("Callable must be a valid JSON string or an array");
                }
            }
            $this->callable = $callable;
        }
        return $this->callable;
    }
    public function strict($strict=null) {
        if ($strict !== null) {
            $this->strict = $strict ? true : false;
        }
        return $this->strict ? true : false;
    }
    public function parameters($parameters=null) {
        if ($parameters !== null) {
            if(!is_array($parameters)) {
                $parameters = json_decode($parameters,true);
                if(!$parameters) {
                    throw new \Exception("Parameters must be an array or a valid JSON string");
                }
            }
            $this->parameters = $parameters;
        }
        return $this->parameters;
    }

    public function loadFromId($id) {
        $db = OpenAI::db();
        $result = $db->query("SELECT * FROM ".static::tableName()." WHERE id=?",[$id]);
        while($row = $db->next($result)) {
            $this->setFromData($row->asArray());
        }
        return $this;
    }

    public function save() {
        $db = OpenAI::db();
        if ($this->id()) {
            $db->query("UPDATE ".static::tableName()." SET `name`=?, `assistant_id`=?, `description`=?, `callable`=?, `parameters`=?, `strict`=? WHERE id=?", [
                $this->name(), $this->assistantId(), $this->description(), json_encode($this->callable()), json_encode($this->parameters()), $this->strict(), $this->id()
            ]);
        } else {
            $db->query("INSERT INTO ".static::tableName()." (`name`, `assistant_id`, `description`, `callable`, `parameters`, `strict`) VALUES (?, ?, ?, ?, ?, ?)", [
                $this->name(), $this->assistantId(), $this->description(), json_encode($this->callable()), json_encode($this->parameters()), $this->strict()
            ]);
            $this->id = $db->lastInsertId();
            $this->loadFromId($this->id);
        }
        return $this;
    }

    public function run($arguments) {
        $callable = $this->callable();
        if(is_callable($callable)) {
            return $callable($arguments);
        }
        throw new \Exception("Callable is not a valid function");
    }

    public static function fromName($name,$assistantId=null) {
        $db = OpenAI::db();
        if($assistantId) {
            $result = $db->query("SELECT * FROM ".static::tableName()." WHERE `name`=? AND `assistant_id`=?",[$name,$assistantId]);
        } else {
            $result = $db->query("SELECT * FROM ".static::tableName()." WHERE `name`=?",[$name]);
        }
        while($row = $db->next($result)) {
            $tool = new ToolFunction($row->asArray());
            return $tool;
        }
        return false;
    }
    public static function fromId($id) {
        $tool = new ToolFunction();
        return $tool->loadFromId($id);
    }
    public static function fromToolCall($toolCall,$assistantId=null) {
        if(
               isset($toolCall["type"]) && $toolCall["type"] == "function"
            && isset($toolCall["id"])
            && isset($toolCall["function"])
            && isset($toolCall["function"]["name"])
            && isset($toolCall["function"]["arguments"])
        ) {
            $output = static::runToolCall($toolCall["function"]["name"],$toolCall["function"]["arguments"],$assistantId);
            if($output === false) {
                return false;
            }
            return [
                "tool_call_id"=>$toolCall["id"],
                "output"=>$output
            ];
        }
        return false;
    }
    public static function runToolCall($name,$arguments,$assistantId=null) {
        $tool = static::fromName($name,$assistantId);
        if($tool) {
            return $tool->run($arguments);
        }
        return false;
    }
}