<?php

/**
 * @uri /host/([^\/]+)/vital/([^\/]+) 4
 */
class HostVital extends CfProtectedResource
{
    /**
     * @param $request
     * @param $host_id
     * @param $vital_id
     *
     * @rbacName Get host vital
     * @rbacGroup Hosts
     * @rbacAlias host-vital.get
     * @rbacAllowedByDefault
     *
     * @return Response
     * @throws ResponseException
     */
    public function get($request, string $host_id, string $vital_id)
    {
        $user = $this->username;

        $response = new Response($request);
        $payload = cfapi_host_vital_get(
            $user,
            $host_id,
            $vital_id,
            DefaultParameters::from(),
            DefaultParameters::to()
        );
        if ($payload) {
            $response->code = Response::OK;
            $response->body = $payload;
        } else {
            $response->code = Response::NOTFOUND;
        }

        return $response;
    }
}

/**
 * @uri /host/(.+)/context 3
 */
class HostContextList extends CfProtectedResource
{
    /**
     * @param $request
     * @param $host_id
     *
     * @rbacName Get host classes
     * @rbacGroup Hosts
     * @rbacAlias host-context-list.get
     * @rbacAllowedByDefault
     *
     * @return Response
     */
    public function get($request, string $host_id)
    {
        $user = $this->username;

        $response = new Response($request);
        $payload = cfapi_host_context_list($user, $host_id);
        if ($payload) {
            $response->code = Response::OK;
            $response->body = $payload;
        } else {
            $response->code = Response::NOTFOUND;
        }

        return $response;
    }
}

/**
 * @uri /host/(.+)/vital 2
 */
class HostVitalList extends CfProtectedResource
{
    /**
     * @param $request
     * @param $host_id
     *
     * @rbacName Get host vital list
     * @rbacGroup Hosts
     * @rbacAlias host-vital-list.get
     * @rbacAllowedByDefault
     *
     * @return Response
     */
    public function get($request, string $host_id)
    {
        $user = $this->username;

        $response = new Response($request);
        $payload = cfapi_host_vital_list($user, $host_id);
        if ($payload) {
            $response->code = Response::OK;
            $response->body = $payload;
        } else {
            $response->code = Response::NOTFOUND;
        }

        return $response;
    }
}

/**
 * @uri /host/:host_id 1
 */
class Host extends CfProtectedResource
{
    /**
     * @param $request
     * @param $host_id
     *
     * @rbacName View host
     * @rbacGroup Hosts
     * @rbacAlias host.get
     * @rbacAllowedByDefault
     *
     * @return Response
     */
    public function get($request, string $host_id)
    {
        $user = $this->username;

        $response = new Response($request);
        $payload = cfapi_host_get($user, $host_id);
        if ($payload) {
            $response->code = Response::OK;
            $response->body = $payload;
        } else {
            $response->code = Response::NOTFOUND;
        }

        return $response;
    }

    /**
     * @param $request
     * @param $host_id
     *
     * @rbacName Delete
     * @rbacGroup Hosts
     * @rbacAlias host.delete
     *
     * @return Response
     */
    public function delete($request, string $host_id)
    {
        $user = $this->username;
        $response = new Response($request);

        disabledOnSuperhub('Host deletion', $this->username);

        /**
         * If a user belongs to a group that allowed to delete any host then do not check host presence.
         * Otherwise, there is no way to delete not reported hosts, because host won't be found
         * due to database RBAC.
         **/
        $isAllowedToDeleteAnyHosts = RbacAccessService::isActionAllowed('any-host.delete', (new CfRBAC())->getPermissionsByRoles((new CfUsers($this->username))->getUserRoles()));
        $shouldDelete = $isAllowedToDeleteAnyHosts || cfapi_host_get($user, $host_id);
        // checkHostAccessAndReturnName() will fail if the host has not reported, in that case use the host_id instead of hostname for audit logs
        try {
            $hostname = $this->checkHostAccessAndReturnName($user, $host_id);
        } catch (Exception) {
            $hostname = $host_id;
        }
        if ($shouldDelete) {
            if (cfapi_host_delete($user, $host_id)) {
                $response->code = Response::ACCEPTED;
                AuditLogService::register([
                    AuditLogFields::ACTOR => $this->username,
                    AuditLogFields::OBJECT_TYPE => AuditLogObjectTypes::HOST,
                    AuditLogFields::OBJECT_ID => $host_id,
                    AuditLogFields::OBJECT_NAME => $hostname,
                    AuditLogFields::ACTION => AuditLogActions::DELETE,
                    AuditLogFields::DETAILS => ["Deleted the $host_id host."]
                ]);
            } else {
                $response->code = Response::INTERNALSERVERERROR;
            }
        } else {
            $response->code = Response::NOTFOUND;
        }

        return $response;
    }
}

/**
 * @uri /host 0
 */
class HostList extends CfProtectedResource
{
    /**
     * @param $request
     *
     * @rbacName Host list
     * @rbacGroup Hosts
     * @rbacAlias hostlist.get
     * @rbacAllowedByDefault
     *
     * @return Response
     * @throws ResponseException
     */
    public function get($request)
    {
        $user = $this->username;

        $context_include = array();
        if (Utils::queryParam('context-include')) {
            $context_include = [Utils::queryParam('context-include')];
        }

        $context_exclude = array();
        if (Utils::queryParam('context-exclude')) {
            $context_exclude = [Utils::queryParam('context-exclude')];
        }

        $response = new Response($request);
        $response->body = cfapi_host_list(
            $user,
            $context_include,
            $context_exclude,
            DefaultParameters::page(),
            DefaultParameters::count()
        );
        $response->code = Response::OK;

        return $response;
    }

    /**
     * @param $request
     *
     * @rbacName Host list
     * @rbacGroup Hosts
     * @rbacAlias hostlist.get
     * @rbacAllowedByDefault
     *
     * @return Response
     * @throws ResponseException
     */
    public function post($request)
    {
        $user = $this->username;
        $data = Utils::getValidJsonData($request->data);

        $context_include = array();
        if (isset($data->{"context-include"}) && !empty($data->{"context-include"})) {
            $context_include = [$data->{"context-include"}];
        }

        $context_exclude = array();
        if (isset($data->{"context-exclude"}) && !empty($data->{"context-exclude"})) {
            $context_exclude = [$data->{"context-exclude"}];
        }

        $response = new Response($request);
        $response->body = cfapi_host_list(
            $user,
            $context_include,
            $context_exclude,
            DefaultParameters::page(),
            DefaultParameters::count()
        );
        $response->code = Response::OK;

        return $response;
    }
}

/**
 * @uri /host-count 0
 */
class HostsCount extends CfProtectedResource
{
    /**
     * @param $request
     *
     * @rbacName Get host count
     * @rbacGroup Hosts
     * @rbacAlias host-count.post
     * @rbacAllowedByDefault
     *
     * @return Response
     * @throws ResponseException
     */
    public function post($request)
    {
        $data = Utils::getValidJsonData($request->data);

        $this->validate($data);
        $model = new CFHostCount(
            $data->from,
            $data->to,
            $data->period
        );

        $model->setRestClient(new PestHttpClient(API_URL));
        $response = new Response($request);

        try {
            $data = $model->getCountByPeriod();
        } catch (Pest_Forbidden $e) {
            $response->code = Response::FORBIDDEN;
            return $response;
        }

        $response->body = json_encode($data, JSON_PRETTY_PRINT);
        $response->code = Response::OK;

        return $response;
    }

    private function validate($data)
    {

        $errors = [];
        if ($data->period === null || !isset(CFHostCount::$periodsMap[strtolower($data->period)])) {
            $errors[] = 'Selected period is wrong. Period can be: ' . implode(', ', array_keys(CFHostCount::$periodsMap));
        }

        if ($data->from === null) {
            $errors[] = 'From cannot be blank';
        } else {
            Validator::date($data->from, label: 'from');
        }

        if ($data->to === null) {
            $errors[] = 'To cannot be blank';
        } else {
            Validator::date($data->to, label: 'to');
        }


        if (!empty($errors)) {
            throw new Exception(implode("\n", $errors));
        }

    }
}

/**
 * @uri /unfiltered-hosts-count 0
 */
class UnfilteredHostsCount extends CfProtectedResource
{
    /**
     * @param $request
     *
     * @rbacName Get unfiltered hosts count
     * @rbacDescription Hosts count information with no RBAC applied
     * @rbacGroup Hosts
     * @rbacAlias unfiltered-host-count.get
     *
     * @return Response
     */
    public function get($request)
    {
        $countModel = new UnfilteredHostCountModel(CfdbPdo::getInstance()->getConnection());

        $response = new Response($request);
        $response->body = json_encode($countModel->getResult(), JSON_PRETTY_PRINT);
        $response->code = Response::OK;

        return $response;
    }
}

/**
 * @uri /hosts/by-class 0
 */
class HostsByClass extends CfProtectedResource
{
    public function get($request)
    {
        $response = new Response($request);
        $return = [];
        $contextInclude = Utils::queryParam('context-include', null);
        $format = Utils::queryParam('format', 'json');
        $inventoryFile = Utils::queryParam('inventoryFile', 'false');
        $withInventory = Utils::queryParam('withInventory', 'false');

        $model = new CFHostByClass($this->username, $inventoryFile === 'true', $withInventory === 'true');

        if ($withInventory === 'true' && $inventoryFile !== 'true') {
            $return['_meta'] =  ['hostvars' => $model->getHostsInventories($contextInclude)];
        }

        $hosts = $model->getHosts($contextInclude);
        $return = array_merge($return, $hosts);

        switch ($format) {
            case 'yaml':
                $return =  json_decode(json_encode($return), true); // convert nested objects into arrays
                $body = yaml_emit($return);
                break;
            case 'json':
            default:
                // in case of inventoryFile=true set JSON_FORCE_OBJECT flag which converts empty array to objects [] => {}
                $body = json_encode($return, ($inventoryFile == 'true' ? JSON_FORCE_OBJECT : null));
        }

        $response->body = $body;
        $response->code = Response::OK;

        return $response;
    }
}

/**
 * @uri /hosts/deleted 0
 */
class DeletedHosts extends CfProtectedResource
{
    /**
     * @param $request
     *
     * @rbacName Get deleted hosts list
     * @rbacDescription  No RBAC applied
     * @rbacGroup Hosts
     * @rbacAlias hosts-deleted.get
     *
     * @return Response
     */
    public function get($request)
    {
        $deletedHost = new CfDeletedHost(CfdbPdo::getInstance()->getConnection());

        $limit = Utils::queryParam('limit', null);
        $skip = Utils::queryParam('skip', null);
        $response = new Response($request);

        $body = json_encode($deletedHost->getHosts($limit, $skip));
        $response->body = $body;
        $response->code = Response::OK;

        return $response;
    }
}

/**
 * @uri /hosts/restore-deleted/:hostkey
 */
class RestoreDeletedHosts extends CfProtectedResource
{
    /**
     * @param $request
     *
     * @rbacName Restore deleted hosts database entries
     * @rbacDescription  No RBAC applied
     * @rbacGroup Hosts
     * @rbacAlias hosts-undelete.post
     *
     * @return Response
     */
    public function post($request, $hostkey)
    {
        $response = new Response($request);
        $deletedHost = new CfDeletedHost(CfdbPdo::getInstance()->getConnection());

        if (!$deletedHost->getDeletedHost($hostkey)) {
            $response->code = Response::NOTFOUND;
            return $response;
        }

        $deletedHost->restoreDeletedHost($hostkey);
        $response->code = Response::OK;

        AuditLogService::register([
            AuditLogFields::ACTOR => $this->username,
            AuditLogFields::OBJECT_TYPE => AuditLogObjectTypes::HOST,
            AuditLogFields::OBJECT_ID => $hostkey,
            AuditLogFields::ACTION => AuditLogActions::UPDATE,
            AuditLogFields::DETAILS => ["Restored deleted the `$hostkey` host."]
        ]);

        return $response;
    }
}

/**
 * @uri /hosts/delete-permanently/:hostkey
 */
class DeletePermanentlyHosts extends CfProtectedResource
{
    /**
     * @param $request
     *
     * @rbacName Delete hosts permanently
     * @rbacDescription  No RBAC applied
     * @rbacGroup Hosts
     * @rbacAlias hosts-delete-permanently.delete
     *
     * @return Response
     */
    public function delete($request, $hostkey)
    {
        $response = new Response($request);
        $deletedHost = new CfDeletedHost(CfdbPdo::getInstance()->getConnection());

        if (!$deletedHost->getDeletedHost($hostkey)) {
            $response->code = Response::NOTFOUND;
            return $response;
        }

        $deletedHost->permanentlyDelete($hostkey);
        $response->code = Response::OK;

        AuditLogService::register([
            AuditLogFields::ACTOR => $this->username,
            AuditLogFields::OBJECT_TYPE => AuditLogObjectTypes::HOST,
            AuditLogFields::OBJECT_ID => $hostkey,
            AuditLogFields::ACTION => AuditLogActions::DELETE,
            AuditLogFields::DETAILS => ["Permanently deleted the `$hostkey` host."]
        ]);

        return $response;
    }
}
