<?php
require_once 'AlertSQL.php';

class InventoryAlert extends AlertSQL
{

    CONST OP_MATCHES = 'matches';
    CONST OP_NOT_MATCH = 'not_match';
    CONST OP_CONTAINS = 'contains';
    CONST OP_NOT_CONTAIN = 'not_contain';
    CONST OP_REGEX_MATCHES = 'regex_matches';
    CONST OP_REGEX_NOT_MATCH = 'regex_not_match';
    CONST OP_IS_NOT_REPORTED = 'is_not_reported';
    CONST OP_IS_REPORTED = 'is_reported';
    CONST OP_LESS_THAN = '<';
    CONST OP_LESS_THAN_OR_EQUAL = '<=';
    CONST OP_GREATER_THAN = '>';
    CONST OP_GREATER_THAN_OR_EQUAL = '>=';
    CONST OP_EQUAL = '=';
    CONST OP_NOT_EQUAL = '!=';
    CONST OP_ILIKE = 'ILIKE';
    CONST OP_NOT_ILIKE = 'NOT ILIKE';

    const OPERATORS = [
        self::OP_MATCHES => self::OP_MATCHES ,
        self::OP_NOT_MATCH => self::OP_NOT_MATCH,
        self::OP_CONTAINS => self::OP_CONTAINS,
        self::OP_NOT_CONTAIN => self::OP_NOT_CONTAIN,
        self::OP_REGEX_MATCHES => self::OP_REGEX_MATCHES,
        self::OP_REGEX_NOT_MATCH => self::OP_REGEX_NOT_MATCH,
        self::OP_IS_NOT_REPORTED => self::OP_IS_NOT_REPORTED,
        self::OP_IS_REPORTED => self::OP_IS_REPORTED,
        self::OP_LESS_THAN => self::OP_LESS_THAN,
        self::OP_GREATER_THAN => self::OP_GREATER_THAN,
        self::OP_LESS_THAN_OR_EQUAL => self::OP_LESS_THAN_OR_EQUAL,
        self::OP_GREATER_THAN_OR_EQUAL => self::OP_GREATER_THAN_OR_EQUAL,
        self::OP_EQUAL => self::OP_EQUAL,
        self::OP_NOT_EQUAL => self::OP_NOT_EQUAL,
        self::OP_ILIKE => self::OP_MATCHES,
        self::OP_NOT_ILIKE => self::OP_NOT_MATCH
    ];

    const STRING_OPERATORS = [
        self::OP_IS_NOT_REPORTED,
        self::OP_IS_REPORTED,
        self::OP_EQUAL,
        self::OP_NOT_EQUAL,
        self::OP_MATCHES,
        self::OP_NOT_MATCH,
        self::OP_REGEX_MATCHES,
        self::OP_REGEX_NOT_MATCH
    ];

    const NUMERIC_OPERATORS = [
        self::OP_IS_NOT_REPORTED,
        self::OP_IS_REPORTED,
        self::OP_LESS_THAN,
        self::OP_LESS_THAN_OR_EQUAL,
        self::OP_GREATER_THAN,
        self::OP_GREATER_THAN_OR_EQUAL,
        self::OP_EQUAL,
        self::OP_NOT_EQUAL
    ];
    const INT_OPERATORS =  self::NUMERIC_OPERATORS;
    const REAL_OPERATORS =  self::NUMERIC_OPERATORS;

    const SLIST_OPERATORS = [
        self::OP_IS_NOT_REPORTED,
        self::OP_IS_REPORTED,
        self::OP_MATCHES,
        self::OP_NOT_MATCH,
        self::OP_CONTAINS,
        self::OP_NOT_CONTAIN
    ];

    function __construct()
    {
        parent::__construct();
    }


    /**
     * @param $alert
     * @param $rule
     * @param $limit
     * @param HttpClientInterface $restClient
     * @return array
     */
    function getInventoryHostListWithHeader($alert, $rule, $limit, HttpClientInterface $restClient)
    {

        $includes = $this->utils_getIncludesFromAlert($alert);
        $excludes = $this->utils_getExcludesFromAlert($alert);
        $dataToApi = [
            'select' => $this->getSelectForInventory($rule->inventoryConditions["columns"]),
            'filter' => $this->getFilterForInventory($rule->inventoryConditions["filters"]),
            'limit' => $limit,
            'hostContextInclude' => $includes,
            'hostContextExclude' => $excludes,
        ];

        $response = $restClient->post('/inventory', $dataToApi);
        $response = json_decode($response, JSON_OBJECT_AS_ARRAY);

        $result = array();
        $result['header'] = $response["data"][0]['header'];
        $result['rows'] = $response["data"][0]['rows'];
        return $result;
    }


    /**
     * @param $alert
     * @param $rule
     * @param HttpClientInterface $restClient
     * @return int
     */
    function getCountFailHosts($alert, $rule, HttpClientInterface $restClient)
    {
        $includes = $this->utils_getIncludesFromAlert($alert);
        $excludes = $this->utils_getExcludesFromAlert($alert);
        $dataToApi = [
            'select' => ['resultCount'],
            'filter' => $this->getFilterForInventory($rule->inventoryConditions["filters"]),
            'limit' => 99999999,
            'hostContextInclude' => $includes,
            'hostContextExclude' => $excludes,
        ];

        $response = $restClient->post('/inventory', $dataToApi);
        $response = json_decode($response, JSON_OBJECT_AS_ARRAY);

        return (int) reset($response["data"])['rows'][0][0];
    }

    /**
     * Return SQL string for inventory rule. Use this function to calculate alert stat MUST include hostkey only
     *
     * @param <object> $rule
     * @return <string>
     */
    function getStatSqlStringForInventory($rule)
    {
        // SQL espression should contain only 1 column in select
        // for example: SELECT DISTINCT Hosts.HostKey  FROM Hosts INNER JOIN INVENTORY ON INVENTORY.HostKey = Hosts.HostKey  LEFT JOIN inventory table_default_sys_arch ON Hosts.HostKey = table_default_sys_arch.HostKey AND table_default_sys_arch.keyname = 'default.sys.arch' WHERE table_default_sys_arch.value ILIKE '%6%' GROUP BY Hosts.hostkey , table_default_sys_arch.value
        // so we will remove everything until "FROM Hosts" and replace with SELECT DISTINCT  hosts.hostkey


        $SQL = $rule->inventoryConditions['SQLSTRING'] ?? '';

        // find SELECT and insert hostkey as first field
        $pos = stripos($SQL, 'FROM Hosts');

        if ($pos === false) {
            // something is wrong, return as it is
            return $SQL;
        }

        // we found SELECT - replace with SELECT hostkey
        $tmp = substr($SQL, $pos); // 15- strlen of SELECT DISTINCT

        $SQL = "SELECT DISTINCT hosts.hostkey " . $tmp;

        return $SQL;
    }

    function getStatForInventory($filters, $includeGroup, $excludeGroup, HttpClientInterface $restClient)
    {
        $dataToApi = [
            'select' => ['hostkey'],
            'filter' => $this->getFilterForInventory($filters),
            'limit' => 99999999,
            'hostContextInclude' => $includeGroup,
            'hostContextExclude' => $excludeGroup,
        ];

        $response = $restClient->post('/inventory', $dataToApi);
        $response = json_decode($response);

        $hosts = array_map(function ($item) {
            return $item[0];
        }, $response->data[0]->rows);

        return $hosts;
    }

    public function getFilterForInventory($filters)
    {
        $outputFilter = [];
        foreach ($filters as $filter) {
            $outputFilter[$filter['label']][self::OPERATORS[$filter['condition']]] = $this->getWrappedValue($filter['condition'],
                $filter['value']);
        }
        return $outputFilter;
    }

    private function getSelectForInventory($columns)
    {
        $output = [];
        foreach ($columns as $column) {
            $output[] = $column['label'];
        }
        return $output;
    }

    private function getWrappedValue($operator, $value)
    {
        $wraper = ['ILIKE' => '%', 'LIKE' => '%'];

        if (key_exists($operator, $wraper)) {
            return $wraper[$operator] . $value . $wraper[$operator];
        } else {
            return $value;
        }

    }

}