<?php
namespace boru\boruai\Models;

use boru\boruai\BoruAI;
use boru\dhprocess\Task;
use boru\dhprocess\TaskQueue;
use boru\query\Query;
use boru\query\Entity;
use boru\query\Factory;
use boru\query\models\EntityDefinition;
use boru\query\models\Value;

class Queue extends Entity {
    private $entity;
    public static function entityDefinition() {
        $tableName = BoruAI::table('queue');
        $query = Query::create()->select('*')->from($tableName);
        return new EntityDefinition([
            "className" => static::class,
            "name" => "Queue",
            "queryBase" => $query,
            "jsonFields" => ['options'],
            'createdField' => 'enqueued',
        ]);
    }
    public function __construct($idOrData=null) {
        parent::__construct($idOrData);
        if($this->get('entityid',0)>0) {
            $this->loadEntity($this->get('entity'),$this->get('entityid'));
        }
    }

    public function loadEntity($entityName,$entityId) {
        $this->entity = Factory::instanceById($entityName,$entityId);
    }

    public function run($threaded=false) {
        if($threaded === true) {
            $options = [];
        } else {
            $options = ["numWorkers"=>$threaded];
        }
        BoruAI::initTaskQueue($options);
        $action = $this->get('action');
        $this->set('status',1);
        $this->save();
        $args = $this->get('options',[]);
        if(!is_array($args) || empty($args)) {
            $args = [];
            $args = [$this->get('entityid')];
        }
        if($threaded) {
            $task = TaskQueue::Task($action,$args);
            $task->name($action.":".$this->get('entity').":".$this->get('entityid'));
            $task->onDone(function($task) {
                $this->set('status',2);
                $this->set('dequeued',date('Y-m-d H:i:s'));
                $this->save();
            });
            $task->onError(function($task) {
                $this->set('status',3);
                $this->save();
            });
        } else {
            //command = BoruAI::run_$action($args);
            try {
                $result = call_user_func_array([BoruAI::class,"run_".$action],$args);
                $this->set('status',2);
                $this->set('dequeued',Value::functionValue("NOW()"));
                $this->save();
            } catch(\Exception $e) {
                $this->set('status',3);
                $this->save();
                echo $e->getMessage()."\n";
            }
        }
    }

    /**
     * Get a session by id
     * @param string $id
     * @return ChatSession|false
     */
    public static function fromId($id) {
        $sessions = Factory::search("Document",[0,1],["id","=",$id]);
        if($sessions) {
            return new self($sessions[0]->id());
        }
        return false;
    }

    /**
     * List all sessions
     * @param int $limit
     * @param int $offset
     * @return ChatSession[]
     */
    public static function listQueue($limit=25,$offset=0) {
        return Factory::search("Queue",[$offset,$limit],[1,"=",1]);
    }

    public static function isQueued($entity,$action,$status=0) {
        $query = Factory::query('Queue',"SELECT");
        $query->where("status","=",0);
        if(is_object($entity)) {
            $query->where("entity","=",$entity->property("name"));
            $query->where("entityid","=",$entity->id());
        } else {
            $query->where("entity","=",$entity);
            $query->where("entityid","=",0);
        }
        $query->where("action","=",$action);
        return $query->toCount() > 0;
    }

    public static function queue($entity,$action,$options=[],$force=false) {
        if(static::isQueued($entity,$action) && !$force) {
            return false;
        }
        $queue = Factory::instance('Queue');
        $queue->set("status",Value::functionValue(0));
        if(is_object($entity)) {
            $queue->set('entity',$entity->property("name"));
            $queue->set('entityid',$entity->id());
        } else {
            $queue->set('entity',$entity);
            $queue->set('entityid',0);
        }
        $queue->set('action',$action);
        if(!is_array($options)) {
            $options = [];
        }
        $queue->set('options',$options);
        $queue->save();
        return $queue;
    }

    public static function next($action=null) {
        $query =Factory::query('Queue',"SELECT");
        $query->limit(1);
        $query->where("status","=",0);
        if($action) {
            $query->where("action","=",$action);
        }
        $query->orderBy('enqueued','ASC');
        foreach($query->toRows() as $row) {
            $queue = new Queue($row->get("id"));
            return $queue;
        }
        return false;
    }

    public static function clean($status=null) {
        //get a count first
        $query = Factory::query('Queue',"SELECT");
        if($status === null) {
            $query->where("status",">=",2);
        } else {
            $query->where("status","=",$status);
        }
        $query->limit(1);
        $count = $query->toCount();
        if($count == 0) {
            return false;
        }
        $query = Factory::query('Queue',"DELETE");
        if($status === null) {
            $query->where("status",">=",2);
        } else {
            $query->where("status","=",$status);
        }
        $query->run();
        return $count;
    }


    public static function process($action=null,$limit=0,$threaded=false,$visual=true) {
        $options = [];
        if(!$visual) {
            $options = ["visualize"=>false];
        }
        BoruAI::initTaskQueue($options);
        $i=0;
        while(($queue = static::next($action)) !==false && ($limit==0 || $i<$limit)) {
            $queue->run($threaded);
            $i++;
        }
        if($threaded) {
            TaskQueue::wait();
        }
    }
}