<?php


class HealthDiagnosticModel extends BaseHealthDiagnostic
{
    private $timeCheckForBlueHost;
    private $hostsCollisionsThreshold;
    private $failedHosts = [];
    private $sqlBuilders = [];

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

        $settings = json_decode(cfapi_settings_get($this->userName), JSON_OBJECT_AS_ARRAY);
        $this->timeCheckForBlueHost = time() - ($settings['data'][0]['blueHostHorizon']);
        $this->hostsCollisionsThreshold = $settings['data'][0]['hostsCollisionsThreshold'] ?: 3;
        $this->setSqlBuilders();
    }

    public function getHealthStatus()
    {
        $permissions = (new CfRBAC())->getPermissionsByRoles((new CfUsers($this->userName))->getUserRoles());

        $neverCollectedResult = 'n/a';
        // if allowed to see not reported hosts then query via not restricted query API
        if (RbacAccessService::isActionAllowed('HostsNeverCollectedReport.post', $permissions)) {
            $neverCollectedResult = $this->getStatus(
                $this->sqlBuilders[self::HOSTS_NEVER_COLLECTED_TYPE],
                notRestrictedQuery: true
            );
        }

        $removeHostsReportResult = 'n/a';
        // if allowed to see removed hosts still report then query via not restricted query API
        if (RbacAccessService::isActionAllowed('deletedHostsReportHD.post', $permissions)) {
            $removeHostsReportResult = $this->getStatus(
                $this->sqlBuilders[self::DELETED_HOSTS_REPORT_TYPE],
                notRestrictedQuery: true
            );
        }

        $result = [
            self::DELETED_HOSTS_REPORT_TYPE => $removeHostsReportResult,
            self::HOSTS_NEVER_COLLECTED_TYPE => $neverCollectedResult,
            self::NOT_RECENTLY_COLLECTED_TYPE => $this->getStatus($this->sqlBuilders[self::NOT_RECENTLY_COLLECTED_TYPE]),
            self::SAME_IDENTITY_TYPE => $this->getStatus($this->sqlBuilders[self::SAME_IDENTITY_TYPE]),
            self::HOSTS_USING_SAME_NAME_TYPE => $this->getStatus($this->sqlBuilders[self::HOSTS_USING_SAME_NAME_TYPE]),
            self::NOT_RUN_RECENTLY_TYPE => $this->getStatus($this->sqlBuilders[self::NOT_RUN_RECENTLY_TYPE]),
            self::LAST_AGENT_RUN_UNSUCCESSFUL_TYPE => $this->getStatus($this->sqlBuilders[self::LAST_AGENT_RUN_UNSUCCESSFUL_TYPE]),
            'totalFailed' => sizeof($this->failedHosts),
            'total' => $this->getTotalHostCount()
        ];

        return $result;
    }

    private function setSqlBuilders()
    {
        $this->sqlBuilders = [
            self::DELETED_HOSTS_REPORT_TYPE => HealthDiagnosticSqlBuilderFactory::createSqlBuilder(self::DELETED_HOSTS_REPORT_TYPE, $this->userName),
            self::HOSTS_NEVER_COLLECTED_TYPE => HealthDiagnosticSqlBuilderFactory::createSqlBuilder(self::HOSTS_NEVER_COLLECTED_TYPE, $this->userName),
            self::NOT_RECENTLY_COLLECTED_TYPE => HealthDiagnosticSqlBuilderFactory::createSqlBuilder(self::NOT_RECENTLY_COLLECTED_TYPE, $this->userName)->setParams(['timeCheckForBlueHost' => $this->timeCheckForBlueHost]),
            self::SAME_IDENTITY_TYPE => HealthDiagnosticSqlBuilderFactory::createSqlBuilder(self::SAME_IDENTITY_TYPE, $this->userName)->setParams(['hostsCollisionsThreshold' => $this->hostsCollisionsThreshold]),
            self::HOSTS_USING_SAME_NAME_TYPE => HealthDiagnosticSqlBuilderFactory::createSqlBuilder(self::HOSTS_USING_SAME_NAME_TYPE, $this->userName),
            self::NOT_RUN_RECENTLY_TYPE => HealthDiagnosticSqlBuilderFactory::createSqlBuilder(self::NOT_RUN_RECENTLY_TYPE, $this->userName),
            self::LAST_AGENT_RUN_UNSUCCESSFUL_TYPE => HealthDiagnosticSqlBuilderFactory::createSqlBuilder(self::LAST_AGENT_RUN_UNSUCCESSFUL_TYPE, $this->userName)
        ];
    }

    public function getStatus(HealthDiagnosticSqlBuilderInterface $sqlBuilder, $notRestrictedQuery = false)
    {
        try {
            $data = $this->runQuery($sqlBuilder->getSql(), $notRestrictedQuery);
            $hostKeys = [];

            foreach ($data['rows'] as $row) {
                $hostKeys[] = $row[0];
            }

            $nonOverlappedHosts = array_diff($hostKeys, $this->failedHosts);
            $this->failedHosts = array_merge($this->failedHosts, $nonOverlappedHosts);
            $count = sizeof($nonOverlappedHosts);

            return $count;
        } catch (Exception $e) {
            // do not log access forbidden requests
            if (!($e instanceof CFAPIException) && Utils::CFAPIErrorCodeToHttpCode($e->getCode()) !== Response::FORBIDDEN) {
                syslog(LOG_ERR, $e->getTraceAsString());
            }
            throw $e;
        }
    }

    private function getTotalHostCount()
    {
        $data = $this->runQuery('SELECT count(hostkey) FROM hosts');
        return isset($data['rows'][0]) ? $data['rows'][0][0] : 0;
    }

    private function runQuery(string $sql, bool $notRestrictedQueryAPI = false)
    {
        $data = cfapi_query_post($this->userName, $sql, '', false, -1, -1, [], [], !$notRestrictedQueryAPI);
        $data = json_decode($data, JSON_OBJECT_AS_ARRAY);
        return $data['data'][0];
    }

}
