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

/**
 * Class Api
 */
class Api extends Cf_REST_Controller
{

    const SUPERHUB_ROLE = 'superhub';
    const FEEDER_ROLE = 'feeder';
    /**
     * Api constructor.
     */
    function __construct()
    {
        parent::__construct();
        $this->getProductName();
        $this->load->model('hub_management/setup_hub_model');
        $this->load->model('hub_management/remote_hub_model');
        $this->load->model('hub_model');
    }

    /**
     * @throws Exception
     */
    public function enableSuperhub_get()
    {
        try {
            $this->setup_hub_model->enable(self::SUPERHUB_ROLE);
            $this->respond(200);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to enable superhub. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    /**
     * @throws Exception
     */
    public function enableFeeder_get()
    {
        try {
            $this->setup_hub_model->enable(self::FEEDER_ROLE);
            $this->respond(200);
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to enable feeder. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    /**
     * @throws Exception
     */
    public function hubStatus_get()
    {
        try {
            $status = $this->setup_hub_model->getStatus();
            $this->respond(200, json_encode($status));
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to get hub status. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    /**
     * @throws Exception
     */
    public function hubInfo_get()
    {
        try {
            $info = $this->hub_model->getHubInfo();
            $this->respond(200, json_encode($info));
            return;
        } catch (Exception $exception) {
            log_message('error', 'Unable to get gub info. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    /**
     * @param $data array
     *
     * @return HttpClientInterface
     * @throws Exception
     */
    private function createRemoteHubRestClient(array $data, $authCode = null)
    {
        $headers = [];
        if (!empty($authCode)) {
            $headers[Cf_RestInstance::CF_2FA_TOKEN_HEADER] = $authCode;
        }

        // if skipSslVerification param exists and equals to 'true' then skip ssl certificate verification
        $verifySSL = !((isset($data['skipSslVerification']) && $data['skipSslVerification'] === 'true'));
        $restClient = new Http_client([
            'base_url' => getSchemeHostFromUrl($data['api_url']) . '/api/',
            'verify' => $verifySSL,
            'headers' => $headers,
            'curl' => [
                CURLOPT_CERTINFO => true
            ]
        ]);
        $restClient->setupAuth($data['userName'], $data['password'], 'basic');
        return $restClient;
    }

    /**
     * @throws Exception
     */
    public function addFeeder_post()
    {
        try {
            $post = $this->input->post();
            $authCode = $this->input->post('authCode', null);
            /**
             * @var $restClient HttpClientInterface
             */
            $restClient = $this->createRemoteHubRestClient($post, $authCode);

            $errors = $this->validateFeederUrl($restClient);

            if (!empty($errors) && is_array($errors)) {
                $this->respond(406, json_encode($errors));
            }

            /**
             * Set up new instance of remote_hub_model with rest client authenticated to remote feeder hub
             */
            $this->load->model('hub_management/remote_hub_model', 'feeder_remote_hub_model');
            $this->feeder_remote_hub_model->setRestClient($restClient);

            /**
             * Add superhub hub to remote feeder
             */
            $superHubStatus = $this->setup_hub_model->getStatus();
            $superHubData = $this->hub_model->getHubInfo();
            $superHubData = array_merge($superHubData, [
                'server' => "https://{$_SERVER['SERVER_NAME']}",
                'sshKey' => $superHubStatus['transport_ssh_public_key'],
                'sshFingerprint' => $superHubStatus['ssh_fingerprint'],
                'transportMethod' =>  $this->input->post('transport_method')['value'],
                'target_state' => $this->input->post('target_state'), // available values are: `on`,`paused`.
                'role' => 'superhub'
            ]);
            $this->feeder_remote_hub_model->addRemoteHub($superHubData);

            /**
             * Add feeder hub to current superhub
             */
            $hubData = $this->hub_model->getHubInfo($restClient);
            $hubData = array_merge($hubData, [
                'server' => getSchemeHostFromUrl($post['api_url']),
                'sshKey' => $hubData['fr']['transport_ssh_public_key'],
                'sshFingerprint' => $hubData['fr']['ssh_fingerprint'],
                'transportMethod' =>  $this->input->post('transport_method')['value'],
                'target_state' => $this->input->post('target_state'), // available values are: `on`,`paused`.
                'role' => 'feeder'
            ]);
            $this->remote_hub_model->addRemoteHub($hubData);

            $this->respond(201);
        } catch (Exception $exception) {
            log_message('error', 'Unable to add remote hub. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }

    /**
     * @param $restClient
     *
     * @throws Exception
     */
    private function validateFeederUrl($restClient)
    {
        try {
            $apiData = json_decode($restClient->get('/'), JSON_OBJECT_AS_ARRAY);
        } catch (\GuzzleHttp\Exception\RequestException $exception) {
            $hc = $exception->getHandlerContext();
            // if curl error number is 60 then certificate is not valid
            if (isset($hc['errno']) && $hc['errno'] == CURLE_SSL_CACERT) {
                return [
                    'error' => 'ssl_cert',
                    'data' => getCertificateInfo(getSchemeHostFromUrl($restClient->baseURL))
                ];
            } else {
                throw $exception;
            }
        }

        if ($apiData == null) {
            throw new Exception('Wrong URL, CFEngine Enterprise API not found.
            Please ensure that the URL contains the only domain name or IP address.');
        }

        if (!isset($apiData['data'][0]['hub'])) {
            throw new Exception('Remote hub does not have Federated reporting API.');
        }
    }

    /**
     * @param $id
     *
     * @throws Exception
     */
    public function editFeeder_post($id)
    {
        try {
            $post = $this->input->post();
            $authCode = $this->input->post('authCode', null);
            /**
             * Set up new instance of remote_hub_model with rest client authenticated to remote feeder hub
             */
            $restClient = $this->createRemoteHubRestClient($post, $authCode);
            $this->load->model('hub_management/remote_hub_model', 'feeder_remote_hub_model');
            $this->feeder_remote_hub_model->setRestClient($restClient);

            $errors = $this->validateFeederUrl($restClient);

            if (!empty($errors) && is_array($errors)) {
                $this->respond(406, json_encode($errors));
            }

            /**
             * Get superhub data and change enable then update it on remote feeder
             */
            $superHub = $this->feeder_remote_hub_model->getSuperHub();
            if (!$superHub) {
                throw new Exception('Superhub is not found in the feeder database.');
            }
            $superHub['transport']['mode'] = $this->input->post('transport_method')['value'];
            $this->feeder_remote_hub_model->updateRemoteHub($superHub['id'], $superHub);
            $this->feeder_remote_hub_model->updateTargetState($this->input->post('target_state'));

            /**
             * Get feeder data and change then update it on current superhub
             */
            $remoteHub = $this->remote_hub_model->getRemoteHub($id);
            $remoteHub['target_state'] = $this->input->post('target_state');
            $remoteHub['transport']['mode'] = $this->input->post('transport_method')['value'];
            $this->remote_hub_model->updateRemoteHub($id, $remoteHub);

            $this->respond(201);
        } catch (Exception $exception) {
            log_message('error', 'Unable to edit remote hub. Error: ' . $exception->getMessage());
            throw $exception;
        }
    }


    public function remoteHubs_get()
    {
        $remoteHubs = $this->remote_hub_model->getRemoteHubs();
        $this->respond(200, json_encode($remoteHubs));
    }

    /**
     * @param $id
     */
    public function remoteHub_get($id)
    {
        $remoteHubs = $this->remote_hub_model->getRemoteHub($id);
        $this->respond(200, json_encode($remoteHubs));
    }

    /**
     * @param $id
     */
    public function superHub_get()
    {
        $superHub = $this->remote_hub_model->getSuperHub();
        $this->respond(200, json_encode($superHub));
    }

}
