<?php

/**
 * Class Policy_model
 */
class Policy_model extends Cf_Model
{
    private $username;
    private $flagFileDirectoryPath;
    private $flagFilePath;
    private $policyDirPath = '/opt/cfengine/analyzer/policy/masterfiles';

    public function __construct()
    {
        $this->load->model('advancedreports_model');
        $this->flagFileDirectoryPath = FCPATH . '/analyzer';
        $this->flagFilePath = $this->flagFileDirectoryPath . '/pa.enabled';
    }

    public function setUsername($username)
    {
        $this->username = $username;
    }

    public function getPolicyFiles()
    {
        $files = $this->getRestClient()->get('/policy/files');
        return $files;
    }

    public function createFlagFile()
    {
        try {
            if (!is_dir($this->flagFileDirectoryPath)) {
                mkdir($this->flagFileDirectoryPath);
                chmod($this->flagFileDirectoryPath, 0770);
            }

            file_put_contents($this->flagFilePath, '');
            return true;
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getTraceAsString());
            throw new Exception($e->getMessage());
        }
    }

    public function isFlagFileCreated()
    {
        try {
            return file_exists($this->flagFilePath);
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getTraceAsString());
            throw new Exception($e->getMessage());
        }
    }

    public function isPolicyDirExists()
    {
        try {
            return is_dir($this->policyDirPath);
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getTraceAsString());
            throw new Exception($e->getMessage());
        }
    }

    public function getPolicyInfo($filePath, $promise = false, $hostkey = false)
    {
        try {
            $sql =
                'SELECT  array_agg(DISTINCT promiseoutcome::TEXT) as promiseoutcome,
                            linenumber as "line"
                            FROM "promiseexecutions"
                            WHERE policyfile LIKE \'%' .
                $this->db->escape_str($filePath) .
                '%\'';

            if ($promise) {
                $escapedPromises = array_map([$this->db, 'escape_str'], (array) $promise);
                $sql .= " AND promiseoutcome IN ('" . implode('\',\'', $escapedPromises) . "')";
            }

            if ($hostkey) {
                $sql .= " AND hostkey = '{$this->db->escape_str($hostkey)}'";
            }

            $sql .= ' GROUP BY linenumber';

            $records = $this->runQuery($sql);

            return $this->mapDataWithHeaders($records);
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getTraceAsString());
            throw new Exception($e->getMessage());
        }
    }

    public function getPolicyStatistics(
        $promise = false,
        $hostIncludes = [],
        $hostExcludes = [],
        $inventoryFilter = false,
    ) {
        try {
            $sql = 'SELECT
                           sum(case when promiseoutcome = \'KEPT\' then 1 else 0 end) as kept_count,
                           sum(case when promiseoutcome = \'NOTKEPT\' then 1 else 0 end) as notkept_count,
                           sum(case when promiseoutcome = \'REPAIRED\' then 1 else 0 end) as repaired_count,
                           policyfile
                    FROM "promiseexecutions" ';

            if (is_array($inventoryFilter) && sizeof($inventoryFilter) > 0) {
                $sql .= ' INNER JOIN inventory_cte ON inventory_cte.filtered_hostkey = promiseexecutions.hostkey';
            }

            $promiseExists = $promise && $promise != '%';

            if ($promiseExists) {
                $escapedPromises = array_map([$this->db, 'escape_str'], (array) $promise);
                $sql .= " WHERE promiseoutcome IN ('" . implode('\',\'', $escapedPromises) . "')";
            }

            $sql .= ' GROUP BY policyfile';

            $records = $this->runQuery($sql, $hostIncludes, $hostExcludes, $inventoryFilter);

            return $this->mapDataWithHeaders($records);
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), $e->getTraceAsString());
            throw new Exception($e->getMessage());
        }
    }

    protected function runQuery(
        string $sql,
        array $hostIncludes = [],
        array $hostExcludes = [],
        bool|array $inventoryFilter = [],
    ) {
        $records = $this->advancedreports_model->runQuery(
            $this->username,
            $sql,
            '',
            '',
            0,
            0,
            $hostIncludes,
            $hostExcludes,
            $inventoryFilter,
        );
        $result = ['rows' => $records['rows']];

        foreach ($records['header'] as $key => $header) {
            $result['headers'][$key] = $header['columnName'];
        }

        return $result;
    }

    private function mapDataWithHeaders($data)
    {
        $result = [];
        $headers = $data['headers'];
        foreach ($data['rows'] as $key => $row) {
            $mappedData = [];
            foreach ($row as $index => $item) {
                $mappedData[$headers[$index]] = $item;
            }
            $result[] = $mappedData;
        }

        return $result;
    }
}
