<?php

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

class Host extends Cf_REST_Controller
{
    public function __construct()
    {
        parent::__construct();
        $this->username = $this->session->userdata('username');
        $this->load->model('host_model');
        $this->load->library('Cf_api_formatter');
        $this->host_model->setRestClient($this->getRestClient());
    }

    public function count_get()
    {
        try {
            $count = $this->host_model->getHostCountByContext(
                $this->username,
                $this->param_includes(),
                $this->param_excludes(),
            );
            $this->respond_ok((string) $count);
        } catch (CFModExceptionRBAC $e) {
            $this->respond_forbidden();
        }
    }

    /**
     * Returns the count for given data with node information
     * Use to return count for all node in single request
     * Accepts post data with node id and includes parameters
     * @return json  with node id and count
     */
    public function updateCount_post()
    {
        $data = $this->param('data', true);

        $outputArray = [];
        try {
            foreach ((array) $data[0] as $nodes) {
                $id = $nodes['id'];
                $includes =
                    isset($nodes['includes']) && !empty($nodes['includes']) ? explode(',', $nodes['includes']) : [];
                $count = $this->host_model->getHostCountByContext($this->username, $includes, []);
                $out = ['id' => $id, 'count' => $count];
                $outputArray[] = $out;
            }
        } catch (HttpClient_Forbidden $exception) {
            $this->respond(403, $exception->getMessage());
        }

        $this->respond(200, json_encode($outputArray));
    }

    public function byColor_get($color = null)
    {
        if (is_null($color) || !in_array($color, ['red', 'green', 'black', 'blue'], true)) {
            $this->respond_not_found();
            return;
        }
        try {
            $result = $this->host_model->getHostByColorFromPromiseStatus(
                $color,
                $this->username,
                $this->param_includes(),
                $this->param_excludes(),
            );
            $hosts = [];
            foreach ($result['rows'] ?? [] as $r) {
                $hosts[] = [
                    'hostkey' => $r[0] ?? null,
                    'hostname' => $r[1] ?? null,
                    'ip' => $r[2] ?? null,
                ];
            }
            $this->respond_ok(
                json_encode([
                    'hosts' => $hosts,
                    'total' => $result['rowCount'] ?? count($hosts),
                ]),
            );
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), 'Error getting hosts by color: ' . $e->getMessage());
            $this->respond_internal_error('Failed to load hosts');
        }
    }

    public function countCompliance_get()
    {
        try {
            $hostcount = $this->host_model->getCombinedStatusForRedAndGreen(
                $this->username,
                $this->param_includes(),
                $this->param_excludes(),
            );
            $this->respond_ok(json_encode($hostcount));
        } catch (Exception $e) {
            log_message(
                log_level_for_exception($e),
                'Error getting compliance status with message :: ' . $e->getTraceAsString(),
            );
            $this->respond_internal_error('Error getting compliance status.');
        }
    }

    public function countConnectivity_get()
    {
        try {
            $blueData = $this->host_model->getHostByColorFromPromiseStatusCount(
                'blue',
                $this->username,
                $this->param_includes(),
                $this->param_excludes(),
            );
            $blackData = $this->host_model->getHostByColorFromPromiseStatusCount(
                'black',
                $this->username,
                $this->param_includes(),
                $this->param_excludes(),
            );

            $hostcount = [
                'blue' => (int) $blueData,
                'black' => (int) $blackData,
            ];
            $this->respond_ok(json_encode($hostcount));
        } catch (Exception $e) {
            log_message(
                log_level_for_exception($e),
                'Error getting connectivity status with message :: ' . $e->getTraceAsString(),
            );
            $this->respond_internal_error('Error getting connectivity status.');
        }
    }

    /**
     * Return number of hosts which are not connected ("blue") or not able to get report - "black"
     *
     * Note -include/exclude should not supported here!
     *
     */
    public function connectivityHealth_get()
    {
        try {
            $this->load->model('health_diagnostic_model');
            $this->health_diagnostic_model->setUserName($this->username);
            $this->health_diagnostic_model->setRestClient($this->getRestClient());
            $data = $this->health_diagnostic_model->getHealthStatus();
            $this->respond_ok(json_encode($data));
        } catch (Exception $e) {
            log_message(
                log_level_for_exception($e),
                'Error getting connectivity status with message :: ' . $e->getTraceAsString(),
            );
            $this->respond_internal_error('Error getting connectivity status.');
        }
    }

    public function info_get($hostKey = null)
    {
        if (is_null($hostKey)) {
            $this->respond_not_found();
        }

        try {
            $result = $this->host_model->getHostInfo($this->username, $hostKey, true);
            if (!is_null($result)) {
                $result['cfengineRoles'] =
                    $this->host_model->getHostVariableDataByName($hostKey, 'CFEngine roles') ?? '';
                $this->respond_ok(json_encode($result));
            } else {
                $this->respond_not_found();
            }
        } catch (CFModExceptionRBAC $e) {
            $this->respond_forbidden();
        }
    }

    public function policyExecution_get($hostKey = null)
    {
        if (is_null($hostKey)) {
            $this->respond_not_found();
            return;
        }

        try {
            $result = $this->host_model->getPolicyExecutionData($this->username, $hostKey);
            $this->respond_ok(json_encode($result ?: ['header' => [], 'rows' => [], 'rowCount' => 0]));
        } catch (CFModExceptionRBAC $e) {
            $this->respond_forbidden();
        }
    }

    public function sharedGroups_get($hostKey = null)
    {
        if (is_null($hostKey)) {
            $this->respond_not_found();
            return;
        }

        if (!isActionAllowed('host-groups-shared.get')) {
            $this->respond_ok(json_encode(['accessDenied' => true, 'groups' => []]));
            return;
        }

        try {
            $groups = $this->host_model->getSharedGroupsByHostkey($hostKey);
            $this->respond_ok(json_encode(['groups' => $groups ?: []]));
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), 'Failed to load shared groups: ' . $e->getMessage());
            $this->respond_ok(json_encode(['groups' => [], 'error' => true]));
        }
    }

    public function statusMeasured_get($hostKey = null)
    {
        if (is_null($hostKey)) {
            $this->respond_not_found();
            return;
        }

        try {
            $load = $this->host_model->getHostVariable(
                $this->username,
                $hostKey,
                'cfe_autorun_inventory_loadaverage',
                'value',
            );
            $free = $this->host_model->getHostVariableDataByName($hostKey, 'Disk free (%)');

            $this->respond_ok(
                json_encode([
                    'load' => $load,
                    'free' => $free,
                ]),
            );
        } catch (CFModExceptionRBAC $e) {
            $this->respond_forbidden();
        }
    }

    public function softwareSummary_get($hostKey = null)
    {
        if (is_null($hostKey)) {
            $this->respond_not_found();
            return;
        }

        try {
            $this->load->model(['report_model', 'assets_model']);

            $installed = $this->report_model->getSoftwareInstalled(
                $this->username,
                $hostKey,
                null,
                null,
                null,
                [],
                [],
                1,
                1,
                false,
            );
            $available = $this->report_model->getPatches(
                $this->username,
                $hostKey,
                null,
                null,
                null,
                'AVAILABLE',
                [],
                [],
                1,
                1,
                false,
            );

            $this->respond_ok(
                json_encode([
                    'installedCount' => $installed['rowCount'] ?? 0,
                    'availableCount' => $available['rowCount'] ?? 0,
                    'installedState' => base64_encode(
                        json_encode($this->assets_model->getSoftwareInstalledParams($hostKey)),
                    ),
                    'availableState' => base64_encode(
                        json_encode($this->assets_model->getAvailableSoftwareParams($hostKey)),
                    ),
                ]),
            );
        } catch (CFModExceptionRBAC $e) {
            $this->respond_forbidden();
        }
    }

    public function delhosts_post()
    {
        $hostKeysArray = $this->param('delhosts');
        try {
            foreach ((array) $hostKeysArray as $hostkey) {
                $this->host_model->deleteHost($hostkey);
            }
            $this->cache->clean();
            $this->respond_ok();
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), 'Failed deleting hosts. ' . $e->getTraceAsString());
            $this->respond_internal_error($e->getMessage());
        }
    }

    public function delete_host_permanently_delete($hostkey)
    {
        try {
            $this->host_model->deletePermanently($hostkey);
            $this->cache->clean();
            $this->respond_ok();
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), 'Failed deleting host permanently. ' . $e->getTraceAsString());
            $this->respond_internal_error($e->getMessage());
        }
    }

    public function restore_deleted_host_post($hostkey)
    {
        try {
            $this->host_model->restoreDeletedHost($hostkey);
            $this->cache->clean();
            $this->respond_ok();
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), 'Failed restoring deleted host. ' . $e->getTraceAsString());
            $this->respond_internal_error($e->getMessage());
        }
    }

    public function searchHost_get()
    {
        try {
            $query = $this->param('query', true);
            $limit = $this->param('limit') ? intval($this->param('limit')) : 7;
            $offset = $this->param('offset') ? intval($this->param('offset')) : 0;
            $withMeta = $this->param('withMeta') && $this->param('withMeta') === 'true';
            $hostsData = $this->host_model->searchHost($this->username, $query, $limit, $offset);
            $hostsDataMerged = $this->cf_api_formatter->merge_columns_with_rows(
                $hostsData['header'],
                $hostsData['rows'],
            );

            $result = $withMeta
                ? [
                    'data' => $hostsDataMerged,
                    'meta' => ['rowCount' => $hostsData['rowCount'], 'offset' => $offset, 'limit' => $limit],
                ]
                : $hostsDataMerged;

            log_message('debug', 'Searching host by query:: ' . $query);

            $this->respond_ok(json_encode($result));
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), 'Error searching host with message :: ' . $e->getTraceAsString());
            $this->respond_internal_error('Error search host.');
        }
    }

    public function searchHostsByInventory_get()
    {
        try {
            $query = $this->db->escape_str($this->param('query')) ?? null;
            $ipMask = $this->param('ip_mask');
            $limit = $this->param('limit') ? intval($this->param('limit')) : 7;
            $offset = $this->param('offset') ? intval($this->param('offset')) : 0;

            if ($ipMask === 'true') {
                $inventoryFilters['IPv4 addresses'] = ['ip_mask' => $query];
            } else {
                $inventoryFilters = [
                    'hostkey' => ['matches' => $query],
                    'Host name' => ['or|matches' => $query],
                    'IPv4 addresses' => ['or|contains' => $query],
                    'MAC addresses' => ['or|contains' => $query],
                ];
            }

            $hostsData = $this->host_model->searchHostByInventory($this->username, $inventoryFilters, $limit, $offset);
            $hostsDataMerged = $this->cf_api_formatter->merge_columns_with_rows(
                $hostsData['header'],
                $hostsData['rows'],
            );

            $result = [
                'data' => $hostsDataMerged,
                'meta' => ['rowCount' => $hostsData['rowCount'], 'offset' => $offset, 'limit' => $limit],
            ];

            log_message('debug', 'searchHostsByInventory_get(), query is ' . $query);

            $this->respond_ok(json_encode($result));
        } catch (Exception $e) {
            log_message(log_level_for_exception($e), 'Error searching hosts by inventory :: ' . $e->getTraceAsString());
            $this->respond_internal_error('Error search host.');
        }
    }

    public function hostsByInventoryFilter_post()
    {
        try {
            $filter = $_REQUEST['filter'] ?? null;
            $limit = $this->param('limit') ? intval($this->param('limit')) : -1;
            $offset = $this->param('offset') ? intval($this->param('offset')) : 0;

            $hostsData = $this->host_model->searchHostByInventory($this->username, $filter, $limit, $offset);
            $hostsDataMerged = $this->cf_api_formatter->merge_columns_with_rows(
                $hostsData['header'],
                $hostsData['rows'],
            );

            $result = [
                'data' => $hostsDataMerged,
                'meta' => ['rowCount' => $hostsData['rowCount'], 'offset' => $offset, 'limit' => $limit],
            ];

            $this->respond_ok(json_encode($result));
        } catch (Exception $e) {
            log_message(
                log_level_for_exception($e),
                'Error getting host by inventory filter with message :: ' . $e->getTraceAsString(),
            );
            $this->respond_internal_error('Error getting hosts.');
        }
    }

    public function unfilteredCount_get()
    {
        try {
            $hostsData = $this->host_model->getUnfilteredHostsCount();
            $this->respond_ok(json_encode($hostsData));
        } catch (HttpClient_Forbidden $e) {
            log_message('debug', 'Forbidden unfilteredCount_get request:: ' . $e->getMessage());
            $this->respond_forbidden($e->getMessage());
        } catch (Exception $e) {
            log_message(
                log_level_for_exception($e),
                'Error while getting unfiltered hosts count:: ' . $e->getTraceAsString(),
            );
            $this->respond_internal_error($e->getMessage());
        }
    }

    public function totalHostsCount_get()
    {
        try {
            $count = $this->host_model->getTotalHostsCount($this->username);
            $this->respond_ok(json_encode(['hostCount' => intval($count)]));
        } catch (Exception $e) {
            log_message(
                log_level_for_exception($e),
                'Error while getting total hosts count:: ' . $e->getTraceAsString(),
            );
            $this->respond_internal_error($e->getMessage());
        }
    }
}
