<?php declare(strict_types=1);
namespace openvk\Web\Presenters;
use Chandler\Signaling\SignalManager;
use Chandler\MVC\SimplePresenter;
use Chandler\Session\Session;
use Chandler\Security\Authenticator;
use Latte\Engine as TemplatingEngine;
use openvk\Web\Models\Repositories\Users;

abstract class OpenVKPresenter extends SimplePresenter
{
    protected $banTolerant   = false;
    protected $errorTemplate = "@error";
    protected $user = NULL;
    
    private function calculateQueryString(array $data): string
    {
        $rawUrl = "tcp+stratum://fakeurl.net$_SERVER[REQUEST_URI]"; #HTTP_HOST can be tainted
        $url    = (object) parse_url($rawUrl);
        $path   = $url->path;
        
        return "$path?" . http_build_query(array_merge($_GET, $data));
    }
    
    protected function flash(string $type, string $title, ?string $message = NULL, ?int $code = NULL): void
    {
        Session::i()->set("_error", json_encode([
            "type"  => $type,
            "title" => $title,
            "msg"   => $message,
            "code"  => $code,
        ]));
    }
    
    protected function flashFail(string $type, string $title, ?string $message = NULL, ?int $code = NULL): void
    {
        $this->flash($type, $title, $message, $code);
        $referer = $_SERVER["HTTP_REFERER"] ?? "/";
        
        header("HTTP/1.1 302 Found");
        header("Location: $referer");
        exit;
    }
    
    protected function assertUserLoggedIn(bool $returnUrl = true): void
    {
        if(is_null($this->user)) {
            $loginUrl = "/login";
            if($returnUrl && $_SERVER["REQUEST_METHOD"] === "GET") {
                $currentUrl = function_exists("get_current_url") ? get_current_url() : $_SERVER["REQUEST_URI"];
                $loginUrl  .= "?jReturnTo=" . rawurlencode($currentUrl);
            }
            
            $this->flash("err", "Недостаточно прав", "Чтобы просматривать эту страницу, нужно зайти на сайт.");
            header("HTTP/1.1 302 Found");
            header("Location: $loginUrl");
            exit;
        }
    }
    
    protected function hasPermission(string $model, string $action, int $context): bool
    {
        if(is_null($this->user)) {
            if($model !== "user") {
                $this->flash("info", "Недостаточно прав", "Чтобы просматривать эту страницу, нужно зайти на сайт.");
                
                header("HTTP/1.1 302 Found");
                header("Location: /login");
                exit;
            }
            
            return ($action === "register" || $action === "login");
        }
        
        return (bool) $this->user->raw->can($action)->model($model)->whichBelongsTo($context === -1 ? null : $context);
    }
    
    protected function assertPermission(string $model, string $action, int $context, bool $throw = false): void
    {
        if($this->hasPermission($model, $action, $context)) return;
        
        if($throw)
            throw new SecurityPolicyViolationException("Permission error");
        else
            $this->flashFail("err", "Недостаточно прав", "У вас недостаточно прав чтобы выполнять это действие.");
    }
    
    protected function assertCaptchaCheckPassed(): void
    {
        if(!check_captcha($_POST["captcha"]))
            $this->flashFail("err", "Неправильно введены символы", "Пожалуйста, убедитесь, что вы правильно заполнили поле с капчей.");
    }
    
    protected function signal(object $event): bool
    {
        return (SignalManager::i())->triggerEvent($event, $this->user->id);
    }
    
    protected function logEvent(string $type, array $data): bool
    {
        $db = eventdb();
        if(!$db)
            return false;
        
        $data = array_merge([
            "timestamp" => time(),
            "verified"  => (int) true,
        ], $data);
        $columns = implode(", ", array_map(function($col) {
            return "`" . addslashes($col) . "`";
        }, array_keys($data)));
        $values  = implode(", ", array_map(function($val) {
            return "'" . addslashes((string) (int) $val) . "'";
        }, array_values($data)));
        
        $db->getConnection()->query("INSERT INTO " . $type . "s($columns) VALUES ($values);");
        
        return true;
    }
    
    /**
     * @override
     */
    protected function sendmail(string $to, string $template, array $params = []): void
    {
        parent::sendmail($to, __DIR__ . "/../../Email/$template", $params);
    }
    
    function getTemplatingEngine(): TemplatingEngine
    {
        $latte = parent::getTemplatingEngine();
        $latte->addFilter("translate", function($s) {
            return tr($s);
        });
        
        return $latte;
    }
    
    function onStartup(): void
    {
        $user = Authenticator::i()->getUser();
        
        if(!is_null($user)) {
            $this->user = (object) [];
            $this->user->raw             = $user;
            $this->user->identity        = (new Users)->getByChandlerUser($user);
            $this->user->id              = $this->user->identity->getId();
            $this->template->thisUser    = $this->user->identity;
            $this->template->userTainted = $user->isTainted();
            
            if($this->user->identity->isBanned() && !$this->banTolerant) {
                header("HTTP/1.1 403 Forbidden");
                $this->getTemplatingEngine()->render(__DIR__ . "/templates/@banned.xml", [
                    "thisUser" => $this->user->identity,
                ]);
                exit;
            }
            
            $this->user->identity->setOnline(time());
            $this->user->identity->save();
        }
        
        setlocale(LC_TIME, ...(explode(";", tr("__locale"))));
        
        parent::onStartup();
    }
    
    function onBeforeRender(): void
    {
        parent::onBeforeRender();
        
        if(!is_null(Session::i()->get("_error"))) {
            $this->template->flashMessage = json_decode(Session::i()->get("_error"));
            Session::i()->set("_error", NULL);
        }
    }
}