<?php
namespace boru\boruai\UI;

use boru\boruai\BoruAI;
use boru\boruai\Models\Response;
use boru\boruai\Models\ResponseHistory;
use boru\boruai\Models\Template;
use boru\boruai\UI\Actions\Ajax;
use boru\boruai\Vtiger\Vtiger;
use boru\dot\Dot;

class WebUI {
    private $vtigerVariables = [];
    private $user;
    private $reference;
    private $templateId;

    /** @var WebConfig */
    private $webConfig;

    private $baseDir;

    private $tools;

    private $pathPrefix = "";

    private $config = [
        "templates" => [
            "enabled" => true,
        ],
        "per_user_reference" => true
    ];

    private static $authCallback;
    private static $instance;

    private $request = [];
    private $body = [];

    private $connectors = [];
    private $actions = [];
    private $mergeData = [];
    private $mergeInstructions = [];

    private static $smarty;

    public static function forceAuth() {
        $_SESSION['AUThUSERID'] = 1;
    }
    public static function getInstance() {
        return self::$instance;
    }
    public function __construct($request,WebConfig $config=null) {
        $this->request = $request;
        $this->webConfig = $config ? $config : new WebConfig();
        $this->config["per_user_reference"] = $this->webConfig->force_user_reference ? true : false;
        if($this->webConfig->webPathPrefix) {
            $this->setPathPrefix($this->webConfig->webPathPrefix);
        }
        $this->vtigerVariables = $this->webConfig->vtiger();
        $user = User::login();
        $this->tools = new Tools();
        if(!$user) {
            if($this->vtiger("site_URL")) {
                header("Location: ".$this->vtiger("site_URL")."/index.php?module=Users&action=Login&return_url=".urlencode($_SERVER['REQUEST_URI']));
                exit();
            }
            throw new \Exception("User not logged in");
        }
        $this->user = $user;
        $input = file_get_contents("php://input");
        if($input) {
            $input = json_decode($input, true);
            if(is_array($input)) {
                $this->body = $input;
            }
        }
        if(!$this->webConfig->templateDir) {
            $this->webConfig->templateDir = __DIR__ . "/../../web/templates/";
        }
        static::$smarty = \boru\boruai\Tools\Smarty::instance($this->webConfig->templateDir);
        static::$instance = $this;
    }

    public static function authCallback($callback=null) {
        if($callback !== null) {
            static::$authCallback = $callback;
        }
        return static::$authCallback;
    }

    public function request($key=null,$default=false) {
        if($key) {
            return Dot::get($this->request, $key, $default);
        }
        return $this->request;
    }
    public function body($key=null,$default=false) {
        if($key) {
            return Dot::get($this->body, $key, $default);
        }
        return $this->body;
    }
    public function config($key,$value=null) {
        if($value !== null) {
            $this->config[$key] = $value;
        }
        return Dot::get($this->config, $key);
    }
    public function setConfig($key,$value) {
        Dot::set($this->config, $key, $value);
        return $this;
    }

    public function vtiger($key=null,$default=false) {
        if($key) {
            return Dot::get($this->vtigerVariables, $key, $default);
        }
        return $this->vtigerVariables;
    }

    public function getUser() {
        return $this->user;
    }
    public function getReference() {
        if($this->reference) {
            return $this->reference;
        }
        if($this->body("reference")) {
            $this->reference = $this->body("reference");
        } elseif($this->request("reference")) {
            $this->reference = $this->request("reference");
        } else {
            $references = Reference::forUser($this->user);
            if(is_array($references) && count($references)) {
                $this->reference = $references[0];
            } else {
                $this->reference = "default";
            }
        }
        return $this->reference;
    }
    public function getTemplateId() {
        if($this->templateId) {
            return $this->templateId;
        }
        if($this->body("templateId")) {
            $this->templateId = $this->body("templateId");
        } elseif($this->request("templateId")) {
            $this->templateId = $this->request("templateId");
        }
        return $this->templateId;
    }
    public function setTemplateId($templateId) {
        $this->templateId = $templateId;
    }
    public function getTemplateName() {
        if($this->getTemplateId()) {
            $template = Template::fromId($this->getTemplateId());
            if($template) {
                return $template->name();
            }
        }
        return "Instructions";
    }


    public function setPathPrefix($pathPrefix) {
        $this->pathPrefix = $pathPrefix;
    }
    public function getPathPrefix() {
        return $this->pathPrefix;
    }
    public function setBaseDir($baseDir) {
        $this->baseDir = $baseDir;
    }
    public function getBaseDir() {
        return $this->baseDir;
    }

    public function getReferencesList($limit=10) {
        if($this->config("per_user_reference")) {
            return Reference::forUser($this->getUser(), true,$limit);
        }
        return Reference::listReferences($limit);
    }


    public function addTool($tool,$enabled=false,$forced=false) {
        if(is_array($tool)) {
            foreach($tool as $t) {
                $this->addTool($t,$enabled,$forced);
            }
            return;
        }
        return $this->tools->add($tool,$enabled,$forced);
    }
    public function getTools($forChat=false) {
        if($forChat) {
            return $this->tools->getForChat();
        }
        return $this->tools->get();
    }
    public function enableTool($tool) {
        if(is_array($tool)) {
            foreach($tool as $t) {
                $this->enableTool($t);
            }
            return true;
        }
        return $this->tools->enableTool($tool);
    }
    public function disableTool($tool) {
        if(is_array($tool)) {
            foreach($tool as $t) {
                $this->disableTool($t);
            }
            return true;
        }
        return $this->tools->disableTool($tool);
    }

    public function mergeText($text) {
        if($this->mergeData) {
            //find any {{key}} in the text and replace with the value from $this->mergeData
            //use regex to find the keys
            preg_match_all("/{{(.*?)}}/",$text,$matches);
            foreach($matches[1] as $key) {
                $key = trim($key);
                $value = Dot::get($this->mergeData,$key,false);
                if($value) {
                    if(is_array($value)) {
                        //if associative, turn into key: value\n
                        if(array_values($value) !== $value) {
                            $valueString = "";
                            foreach($value as $k => $v) {
                                if(is_array($v)) {
                                    $v = json_encode($v,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
                                }
                                if(is_string($k)) {
                                    $valueString .= $k.": ".$v."\n";
                                } else {
                                    $valueString .= $v."\n";
                                }
                            }
                            $value = rtrim($valueString,"\n");
                        } else {
                            $value = implode(", ",$value);
                        }
                    }
                    $text = str_replace("{{".$key."}}",$value,$text);
                }
            }
        }
        return $text;
    }
    public function getInstructions($asString=false) {
        if($this->mergeInstructions) {
            $instructions = [];
            foreach($this->mergeInstructions as $key => $value) {
                if(is_array($value)) {
                    $value = implode(", ",$value);
                }
                $instructions[$key] = $value;
            }
            if($asString) {
                $instructions = $this->flattenInstructions($instructions);
            }
            return $instructions;
        } else {
            $instructions = $this->request("instructions");
            if(!$instructions) {
                $instructions = $this->body("instructions");
            }
            if($instructions) {
                return $this->flattenInstructions($instructions);
            }
        }
        return false;
    }
    private function flattenInstructions($instructions) {
        if($instructions) {
            if(is_string($instructions)) {
                return $this->mergeText($instructions);
            }
            if(is_array($instructions)) {
                $string = "";
                if(count($instructions) > 1) {
                    foreach($instructions as $key => $value) {
                        if(is_array($value)) {
                            $value = json_encode($value,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
                        }
                        if(is_string($key)) {
                            $string .= $key.": ".$value."\n";
                        } else {
                            $string .= $value."\n";
                        }
                    }
                } else {
                    $instructions = array_shift($instructions);
                    if(is_array($instructions)) {
                        $string = json_encode($instructions,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
                    } else {
                        $string = $instructions;
                    }
                }
                return $this->mergeText($string);
            }
        }
        return "";
    }
    public function getMergeData() {
        return $this->mergeData;
    }

    public function handleRequest($callDir=null) {
        $this->reference = $this->getReference();
        $action = $this->request("action");
        foreach($this->connectors as $connector) {
            $connector->handleRequest($this);
            if($connector->isTriggered()) {
                $this->mergeData[$connector->getRequestVar()] = $connector->getData();
                $instructions = $connector->getInstructions();
                if($instructions) {
                    $this->mergeInstructions[$connector->getRequestVar()] = $instructions;
                }
            }
        }
        if(!$action) {
            //include __DIR__."/../../web/webui.php";
            $file = $callDir ."/webui.php";
            if(!file_exists($file)) {
                throw new \Exception("File not found: ".$file);
            }
            include $file;
            return;
        }
        Ajax::handleRequest();
        return;
    }
    public function addConnector($requestVar, $callback=null) {
        if(is_array($requestVar)) {
            foreach($requestVar as $c) {
                $this->addConnector($c);
            }
            return;
        }
        if($requestVar instanceof Connector) {
            $this->connectors[] = $requestVar;
        } else {
            $connector = Connector::create($requestVar,$callback);
            $this->connectors[] = $connector;
        }
    }
    public function addAction($action,$class=null) {
        if(is_array($action)) {
            foreach($action as $a) {
                $this->addAction($a);
            }
            return;
        }
        if($action instanceof Ajax) {
            $this->actions[] = $action;
        } else {
            if($class === null) {
                $class = "boru\\boruai\\UI\\Actions\\" . ucfirst($action)."Action";
            }
            if(!class_exists($class)) {
                throw new \Exception("Action class does not inherit Ajax or not found: ".$class);
            }
            $this->actions[$action] = $class;
        }
    }
    public function getActions() {
        return $this->actions;
    }
}