<?php
require_once APPPATH . 'libraries/Cf_REST_Controller.php';

/**
 * @rbacName Modify all users' rules
 * @rbacDescription View, Update, Delete
 * @rbacGroup Widgets
 * @rbacAlias widgets.rules_admin
 */

class Rulesapi extends Cf_REST_Controller
{
    public $username = '';

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

        $this->load->model('rules_model');
        $this->load->model('host_model');
        $this->username = $this->session->userdata('username');
    }

    //$rulesIds - can be urlencoded comma separated list
    public function rules_get($rulesIds = '')
    {
        $filter = [];
        $filterIds = [];
        //$filter['username'] = $this->session->userdata('username');

        if (!empty($rulesIds)) {
            $rulesIds = explode(',', urldecode($rulesIds));

            foreach ($rulesIds as $item => $id) {
                $rulesIds[$item] = $id;
            }

            $filterIds = array_values($rulesIds);
        }

        try {
            $result = $this->rules_model->get_rules_with_ids($filterIds, $filter, ['name' => 'asc']);

            if (empty($result)) {
                $message = $this->lang->line('dashboard_rule_not_exist');
                log_message('debug', 'Failed retrieving rule, error: ' . $message);
                respond_internal_error($message);
                return;
            }

            $json = json_encode($result);
            respond_ok($json);
        } catch (Exception $e) {
            $message = $e->getMessage();
            log_message('debug', 'Failed retrieving rules, error: ' . $message);
            respond_internal_error($message);
        }
    }

    public function rules_by_export_ids_post()
    {
        $postData = json_decode($this->request->body, true);
        $exportIds = $postData['exportIds'] ?? [];
        $rules = $this->rules_model->get_rules_with_export_ids($exportIds, $this->username);
        if (sizeof($rules) > 0) {
            respond_ok(json_encode($rules));
        } else {
            respond_not_found();
        }
    }

    /**
     * Save rule
     * Allow overwrite only by id
     * same name not allowed  - show error
     *
     *
     * @param <string> $id  - id of the rule. If not provided - assume insert, if provided - assume update
     * @return
     */
    public function saverule_post($id = '')
    {
        $data = json_decode($this->request->body, true);

        $data['username'] = $this->session->userdata('username');
        $is_admin = isActionAllowed('widgets.rules_admin');

        if (isset($data['hostcontexts']) && is_array($data['hostcontexts'])) {
            $data['hostcontexts'] = json_encode($data['hostcontexts']);
        }

        $overwrite = false;
        if (!empty($id)) {
            $overwrite = true;
        }

        try {
            $obj = $this->rules_model->saverule($data, $overwrite, $is_admin);
        } catch (Exception $e) {
            respond_internal_error($e->getMessage());
            return;
        }

        if (!$obj) {
            //not able to get inserted rule
            $errors = $this->rules_model->getErrors();
            $data = json_encode($errors);
            respond_internal_error($data);
            return;
        } else {
            $data = [];
            $data[0] = [
                'ruleid' => $obj->id,
                'text' => $this->lang->line('dashboard_rule_save_success'),
                'errorCode' => 200,
            ];
            respond_ok(json_encode($data));
            return;
        }
    }

    public function validateCustomSql_post()
    {
        $data = json_decode($this->request->body, true);
        $obj = $this->rules_model->setRestClient($this->getRestClient());
        $obj = $this->rules_model->validateCustomSql($data['sql']);
        respond_ok(json_encode($obj));
    }

    public function deleterule_delete($id)
    {
        $username = $this->session->userdata('username');
        $is_admin = isActionAllowed('widgets.rules_admin');

        try {
            $result = $this->rules_model->deleterule($id, $username, $is_admin);
            $json = json_encode($result);
            respond_ok($json);
        } catch (Exception $e) {
            $message = $e->getMessage();
            log_message('debug', 'Unable to delete rule. Error: ' . $message);
            respond_internal_error($message);
        }
    }

    public function statistics_post($id)
    {
        $username = $this->session->userdata('username');
        $data = json_decode($this->request->body, true);

        $includes = isset($data['includes']) ? $data['includes'] : [];
        $excludes = isset($data['excludes']) ? $data['excludes'] : [];

        [$rule] = $this->rules_model->get_rules_with_ids([$id], [], ['severity' => 'asc']);

        if (isset($rule->hostcontexts['includes'])) {
            $includes = array_merge($includes, $rule->hostcontexts['includes']);
        }
        $failed = $this->rules_model->getRuleFailHosts($rule, $username, $includes, $excludes);

        $total = null;
        if (sizeof($includes) > 0 || sizeof($excludes) > 0) {
            $total = (int) $this->host_model->getHostCountByContext($username, $includes, $excludes);
        }

        $json = json_encode(['failHosts' => $failed, 'total' => $total]);
        respond_ok($json);
    }

    public function batch_statistics_post()
    {
        $this->load->driver('cache', ['adapter' => 'file']);
        $username = $this->session->userdata('username');
        $data = json_decode($this->request->body, true);
        $cacheTTL = $this->config->item('compliance_cache_ttl_in_seconds');
        $ids = $data['ids'] ?? [];
        $includes = $data['hostContextInclude'] ?? [];
        $inventoryFilter = $data['filter'] ?? [];
        $hostFilter = $data['hostFilter'] ?? [];
        $excludes = $data['excludes'] ?? [];
        $total = (int) $this->host_model->getHostCountByContext(
            $username,
            $includes,
            $excludes,
            inventoryFilter: $inventoryFilter,
            hostFilter: $hostFilter,
            fetchDataViaAPI: false,
        );
        $rules = $this->rules_model->get_rules_with_ids($ids);
        $results = [];

        foreach ($rules as $rule) {
            $mergedIncludes = [
                ...$rule->filterdata->filters->hostContextInclude ?? ($rule->hostcontexts['includes'] ?? []),
                ...$includes,
            ];
            $filterHash = base64_encode(
                json_encode($mergedIncludes) . json_encode($rule->filterdata) . json_encode($inventoryFilter),
            );
            $checksFailuresKey = "{$this->username}_{$rule->id}_{$filterHash}_checks_failures";

            if ($this->cache->get($checksFailuresKey) !== false) {
                $failed = $this->cache->get($checksFailuresKey);
            } else {
                $failed = $this->rules_model->getRuleFailHosts(
                    $rule,
                    $username,
                    $mergedIncludes,
                    $excludes,
                    inventoryFilter: $inventoryFilter,
                    hostFilter: $hostFilter,
                    fetchDataViaAPI: false,
                );
                $this->cache->save($checksFailuresKey, $failed, $cacheTTL);
            }

            $filteredTotal = null;
            // if the rule does not have includes then use common total hosts count
            if (isset($rule->filterdata) || !empty($inventoryFilter) || !empty($mergedIncludes)) {
                $totalChecksHostsKey = "{$this->username}_{$filterHash}_checks_total";
                if ($this->cache->get($totalChecksHostsKey) !== false) {
                    $filteredTotal = $this->cache->get($totalChecksHostsKey);
                } else {
                    $filter = $rule->filterdata->filters->filter ?? [];
                    $ruleHostFilter = $rule->filterdata->filters->hostFilter ?? [];
                    $filteredTotal = (int) $this->host_model->getHostCountByContext(
                        $username,
                        $mergedIncludes,
                        $excludes,
                        inventoryFilter: arrayDeepMerge($inventoryFilter, $filter),
                        hostFilter: arrayDeepMerge($hostFilter, $ruleHostFilter),
                        fetchDataViaAPI: false,
                    );
                    $this->cache->save($totalChecksHostsKey, $filteredTotal, $cacheTTL);
                }
            }

            $results[] = ['ruleId' => $rule->id, 'failHosts' => $failed, 'total' => $filteredTotal ?? $total];
        }

        respond_ok(json_encode($results));
    }
}
