<?php

class report_model extends Cf_Model
{
    private $userTimeZone;

    public const AVAILABLE_TYPE = 'AVAILABLE';

    public function __construct()
    {
        parent::__construct();
        $this->load->model('advancedreports_model');
    }

    public function setUserTimeZone($timeZone)
    {
        $this->userTimeZone = $timeZone;
    }

    public function getUserTimeZone()
    {
        return $this->userTimeZone;
    }

    /**
     *
     * @param type $username
     * @param type $hostkey
     * @param type $search
     * @param type $inclist
     * @param type $exlist
     * @param type $rows
     * @param type $page_number
     * @return type array
     */
    public function getClassReport(
        $username,
        $search,
        $inclist,
        $exlist,
        $from,
        $to,
        $rows = 50,
        $page_number = 1,
        $hosts_only = false,
        $options = [],
    ) {
        try {
            $whereArray = [];

            $selectFields = 'hosts.hostname as "Host name", Contexts.ContextName as "Context Name"';
            $sortField = '';

            if (!empty($options['returnTimestamp'])) {
                $selectFields .= ',Contexts.DefineTimeStamp as "Last occured"';
                $sortField = 'Last occured';
            } else {
                $logTime = $this->_getTimeField('Contexts.DefineTimeStamp', 'Last occured');
                $selectFields .= ',' . $logTime['statement'];
                $sortField = $logTime['alias'];
            }

            if ($hosts_only) {
                $selectFields = 'hosts.hostname,
                hosts.hostkey,
                hosts.ipaddress';
            }

            $fromTable = 'Contexts';

            if (!empty($search)) {
                $escapedSearch = $this->db->escape_str($search);
                $whereArray[] = sprintf('Contexts.ContextName ~ ("%s")', $escapedSearch);
            }
            if (!empty($from)) {
                $whereArray[] = sprintf('Contexts.DefineTimeStamp >=%s', $from);
            }
            if (!empty($to)) {
                $whereArray[] = sprintf('Contexts.DefineTimeStamp <=%s', $to);
            }
            $join = ['table' => 'Contexts', 'column' => 'hostkey'];

            $selOption = [
                'distinct' => true,
                'select' => $selectFields,
                'from' => $fromTable,
                'where' => $whereArray,
                'joinHost' => $join,
            ];

            $query = $this->compileSelectQuery($selOption, $inclist, $exlist);

            $skip = ($page_number - 1) * $rows;
            $data = $this->advancedreports_model->runQuery(
                $username,
                $query,
                $sortField,
                true,
                $skip,
                $rows,
                $inclist,
                $exlist,
            );
            if (is_array($data)) {
                return $data;
            } else {
                throw new Exception($this->getErrorsString());
            }
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }
    }

    /**
     * [getPatches description]
     * @param  [type]  $username       [description]
     * @param  [type]  $hostkey        [description]
     * @param  [type]  $search         [description]
     * @param  [type]  $version        [description]
     * @param  [type]  $arch           [description]
     * @param  [type]  $type           [description]
     * @param  [type]  $inclist        [description]
     * @param  [type]  $exlist         [description]
     * @param  integer $rows           [description]
     * @param  integer $page_number    [description]
     * @param  boolean $hosts_only     [description]
     * @param  array   $fileGeneration [description]
     * @return [type]                  [description]
     */
    public function getPatches(
        $username,
        $hostkey,
        $search,
        $version,
        $arch,
        $type,
        $inclist,
        $exlist,
        $rows = 50,
        $page_number = 1,
        $hosts_only = false,
    ) {
        try {
            $whereArray = [];

            $selectFields =
                'hosts.hostname as "Host name",softwareupdates.patchname as "Update name",softwareupdates.patchversion as "Update version",softwareupdates.patcharchitecture as "Architecture",softwareupdates.patchreporttype as "Update type"';

            if ($hosts_only) {
                $selectFields = 'hosts.hostname,
                hosts.hostkey,
                hosts.ipaddress';
            }

            $fromTable = 'softwareupdates';

            if (!empty($search)) {
                $escapedSearch = $this->db->escape_str($search);
                $whereArray[] = sprintf("softwareupdates.patchname  ~ '%s'", $escapedSearch);
            }
            if (!empty($version)) {
                $escapedVersion = $this->db->escape_str($version);
                $whereArray[] = sprintf("softwareupdates.patchversion ~ '%s'", $escapedVersion);
            }
            if (!empty($arch)) {
                $escapedArch = $this->db->escape_str($arch);
                $whereArray[] = sprintf("softwareupdates.patcharchitecture  ~ '%s'", $escapedArch);
            }

            if (!empty($type)) {
                $escapedType = $this->db->escape_str($type);
                $whereArray[] = sprintf("softwareupdates.patchreporttype = '%s'", $escapedType);
            }
            if (!empty($hostkey)) {
                $whereArray[] = sprintf("softwareupdates.hostkey = '%s'", $hostkey);
            }

            $join = ['table' => 'softwareupdates', 'column' => 'hostkey'];

            $selOption = [
                'distinct' => true,
                'select' => $selectFields,
                'from' => $fromTable,
                'where' => $whereArray,
                'joinHost' => $join,
                'orderBy' => 'softwareupdates.patchname',
            ];

            $query = $this->compileSelectQuery($selOption);

            $skip = ($page_number - 1) * $rows;
            $data = $this->advancedreports_model->runQuery(
                $username,
                $query,
                false,
                false,
                $skip,
                $rows,
                $inclist,
                $exlist,
            );
            if (is_array($data)) {
                return $data;
            } else {
                throw new Exception($this->getErrorsString());
            }
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }
    }

    /**
     *
     * @param type $username
     * @param type $search
     * @param type $cause_rx
     * @param type $from
     * @param type $to
     * @param type $type
     * @param type $inclist
     * @param type $exlist
     * @param type $rows
     * @param type $page_number
     * @param type $hosts_only
     * @param type $fileGeneration
     * @return type
     * @throws Exception
     */
    public function getPromisesLog(
        $username,
        $hostkey,
        $search,
        $cause_rx,
        $from,
        $to,
        $type,
        $inclist,
        $exlist,
        $rows = 50,
        $page_number = 1,
        $hosts_only = false,
        $options = [],
    ) {
        try {
            $whereArray = [];

            $selectFields =
                'hosts.hostname, PromiseLogs.PromiseHandle,PromiseLogs.PromiseStatus,PromiseLogs.PromiseStatusReport';

            if (!empty($options['returnTimestamp'])) {
                $selectFields .= ',PromiseLogs.TimeStamp as "TimeStamp"';
                $sortField = 'Time';
            } else {
                $logTime = $this->_getTimeField('PromiseLogs.TimeStamp', 'TimeStamp');
                $selectFields .= ',' . $logTime['statement'];
                $sortField = $logTime['alias'];
            }

            if ($hosts_only) {
                $selectFields = 'hosts.hostname,
                hosts.hostkey,
                hosts.ipaddress';
            }

            $fromTable = 'PromiseLogs';

            if (!empty($search)) {
                $whereArray[] = sprintf('PromiseLogs.PromiseHandle ~ ("%s")', $search);
            }
            if (!empty($cause_rx)) {
                $whereArray[] = sprintf('PromiseLogs.PromiseStatusReport ~ ("%s")', $cause_rx);
            }
            if (!empty($from)) {
                $whereArray[] = sprintf('PromiseLogs.TimeStamp >=%s', $from);
            }
            if (!empty($to)) {
                $whereArray[] = sprintf('PromiseLogs.TimeStamp <=%s', $to);
            }
            if (!empty($type)) {
                $whereArray[] = sprintf('PromiseLogs.PromiseStatus ="%s"', $type);
            }

            if (!empty($hostkey)) {
                $whereArray[] = sprintf('PromiseLogs.hostkey = "%s"', $hostkey);
            }

            $join = ['table' => 'PromiseLogs', 'column' => 'hostkey'];

            $selOption = [
                'distinct' => true,
                'select' => $selectFields,
                'from' => $fromTable,
                'where' => $whereArray,
                'joinHost' => $join,
            ];

            $query = $this->compileSelectQuery($selOption);
            $skip = ($page_number - 1) * $rows;
            $data = $this->advancedreports_model->runQuery(
                $username,
                $query,
                $sortField,
                true,
                $skip,
                $rows,
                $inclist,
                $exlist,
            );
            if (is_array($data)) {
                return $data;
            } else {
                throw new Exception($this->getErrorsString());
            }
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }
    }

    /**
     *
     * @param type $username
     * @param type $hostkey
     * @param type $search
     * @param type $version
     * @param type $arch
     * @param type $inclist
     * @param type $exlist
     * @param type $rows
     * @param type $page_number
     * @return type array
     */
    public function getsoftwareInstalled(
        $username,
        $hostkey,
        $search,
        $version,
        $arch,
        $inclist,
        $exlist,
        $rows = 50,
        $page_number = 1,
        $hosts_only = false,
    ) {
        try {
            $whereArray = [];

            if (!empty($search)) {
                $whereArray[] = sprintf("software.softwarename ~ '%s'", $search);
            }
            if (!empty($version)) {
                $whereArray[] = sprintf("software.softwareversion ~ '%s'", $version);
            }
            if (!empty($arch)) {
                $whereArray[] = sprintf("software.softwarearchitecture ~ '%s')", $arch);
            }
            if (!empty($hostkey)) {
                $whereArray[] = sprintf("software.hostkey = '%s'", $hostkey);
            }

            $selectFields = 'hosts.hostname as "Host name",
                software.softwarename as "Software name",
                software.softwareversion as "Software version",
                software.softwarearchitecture as "Software architecture"';

            if ($hosts_only) {
                $selectFields = 'hosts.hostname as "Host name",
                hosts.hostkey as "Host key",
                hosts.ipaddress as "Ip address"';
            }
            $from = 'software';
            $join = ['table' => 'software', 'column' => 'hostkey'];

            $selOption = [
                'distinct' => true,
                'select' => $selectFields,
                'from' => $from,
                'where' => $whereArray,
                'joinHost' => $join,
                'orderBy' => 'software.softwarename',
            ];

            $query = $this->compileSelectQuery($selOption);

            $this->advancedreports_model->setRestClient($this->getRestClient());
            $skip = ($page_number - 1) * $rows;
            $data = $this->advancedreports_model->runQuery(
                $username,
                $query,
                false,
                false,
                $skip,
                $rows,
                $inclist,
                $exlist,
            );
            if (is_array($data)) {
                return $data;
            } else {
                throw new Exception($this->getErrorsString());
            }
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }
    }

    /**
     *
     * @param type $username
     * @param type $scope
     * @param type $lval
     * @param type $rval
     * @param type $type
     * @param type $inclist
     * @param type $exlist
     * @param type $rows
     * @param type $page_number
     * @param type $hosts_only
     * @return type array
     */
    public function getVariablesReport(
        $username,
        $scope,
        $lval,
        $rval,
        $type,
        $inclist,
        $exlist,
        $rows = 50,
        $page_number = 1,
        $hosts_only = false,
        $fileGeneration = [],
    ) {
        try {
            $whereArray = [];

            $selectFields = 'variables.bundle,
                             variables.variablename,
                             variables.variablevalue,
                             variables.variabletype';

            if ($hosts_only) {
                $selectFields = 'hosts.hostname,
                hosts.hostkey,
                hosts.ipaddress';
            }

            $from = 'variables';

            if (!empty($scope)) {
                $escapedScope = $this->db->escape_str($scope);
                $whereArray[] = sprintf("variables.bundle = '%s'", $escapedScope);
            }
            if (!empty($lval)) {
                $escapedLval = $this->db->escape_str($lval);
                $whereArray[] = sprintf("variables.variablename ILIKE '%s%%'", $escapedLval);
            }
            if (!empty($rval)) {
                $escapedRval = $this->db->escape_str($rval);
                $whereArray[] = sprintf("variables.variablevalue ILIKE '%s%%'", $escapedRval);
            }
            if (!empty($type)) {
                $escapedType = $this->db->escape_str($type);
                $whereArray[] = sprintf('variables.variabletype = "%s"', $escapedType);
            }

            $join = ['table' => 'variables', 'column' => 'hostkey'];

            $selOption = [
                'distinct' => true,
                'select' => $selectFields,
                'from' => $from,
                'where' => $whereArray,
                'joinHost' => $join,
            ];

            $query = $this->compileSelectQuery($selOption);

            $skip = ($page_number - 1) * $rows;
            $data = $this->advancedreports_model->runQuery(
                $username,
                $query,
                '',
                false,
                $skip,
                $rows,
                $inclist,
                $exlist,
            );
            if (is_array($data)) {
                return $data;
            } else {
                throw new Exception($this->getErrorsString());
            }
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }
    }

    /**
     * @param string $hostKey
     * @param  array $select
     * @return array
     * @throws Exception
     */
    public function getHostVariablesData($hostKey, $select)
    {
        try {
            $dataToApi = [
                'select' => $select,
                'filter' => ['hostkey' => ['matches' => $hostKey]],
                'limit' => 1,
            ];

            $response = $this->getRestClient()->post('/inventory', $dataToApi);

            $response = json_decode($response);
            $result = $response->data[0]->rows;
            return is_array($result) && isset($result[0]) ? $result[0] : false;
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getMessage() . ' ' . $e->getFile() . ' line:' . $e->getLine());
            throw $e;
        }
    }

    /**
     * Returns a query based on passed parameters
     * @param array $statement components of query (select,from,where,groupby etc)
     * @param array $inclist
     * @param array $exlist
     * @return string compiled SQL query
     */
    public function compileSelectQuery($statement = [], $inclist = [], $exlist = [])
    {
        $this->load->database();
        $incStatement = [];
        $excStatement = [];

        if (!empty($statement['distinct'])) {
            $this->db->distinct();
        }

        $this->db->select($statement['select'], false);
        $this->db->from($statement['from']);
        if (!empty($statement['joinHost'])) {
            $tableJoin = $statement['joinHost']['table'] . '.' . $statement['joinHost']['column'];
            $this->db->join('hosts', sprintf('hosts.hostkey = %s', $tableJoin));
        }

        if (!empty($statement['join'])) {
            foreach ((array) $statement['join'] as $joinStatement) {
                $this->db->join($joinStatement['table'], $joinStatement['condition'], $joinStatement['type']);
            }
        }

        if (!empty($statement['where'])) {
            foreach ((array) $statement['where'] as $where) {
                $this->db->where($where);
            }
        }

        if (!empty($statement['groupBy'])) {
            $this->db->group_by($statement['groupBy']);
        }

        if (!empty($statement['orderBy'])) {
            $direction = !empty($statement['orderDirection']) ? $statement['orderDirection'] : 'ASC';
            $this->db->order_by($statement['orderBy'], $direction);
        }

        $query = $this->db->get_compiled_select();
        return $query;
    }

    /**
     * Packs the data from SQL in format as desired.
     * Also calculates the number of related hosts of the query.
     * @param string $username
     * @param array $data data from SQL query
     * @return array
     */
    public function _prepareResultFromSqlData($username, $data)
    {
        $related = 0;
        $result['meta'] = [
            'count' => $data['rowCount'],
            'header' => $data['header'],
            'related' => $related,
            'query' => $data['query'],
        ];
        $result['data'] = $data['rows'];
        return $result;
    }

    /**
     *  Strips down the 'SHA=' part from key if present
     *  else returns unaltered hostkey
     * @param type $hostkey
     */
    public function _cleanHostKey($hostkey)
    {
        $key = trim($hostkey);
        if (strncmp($key, 'SHA=', 10)) {
            $key = substr($key, 4);
        }
        return $key;
    }

    /**
     * For formatting unix timestamp to Readable date
     * @param type $field
     * @param type $alias
     * @param type $timeZone
     * @return type
     */
    public function _getTimeField($field, $alias, $timeZone = 'local')
    {
        $userTimeZone = $this->getUserTimeZone();
        $offset = getTimeZoneOffset($timeZone, 'Z', $userTimeZone);
        $offsetFormat = getTimeZoneOffset($timeZone, 'P', $userTimeZone);
        $alias .= sprintf(' (GMT %s)', $offsetFormat);
        $string = sprintf(
            'strftime("%%d-%%m-%%Y %%H:%%M",%s,"unixepoch","%s seconds") as "%s"',
            $field,
            $offset,
            $alias,
        );
        $res['statement'] = $string;
        $res['alias'] = $alias;
        return $res;
    }
}
