<?php

namespace FR\V1_0\Models;

use events\BaseEvents;
use events\EventService;
use FR\V1_0\Entities\RemoteHub\RemoteHub;

/**
 * Class SetupHubModel
 * @package FR\V1_0\Models
 */
class SetupHubModel
{
    public const ENABLE_REQUEST_SENT = 'enable_request_sent';
    public const ENABLE_AS_HUB_KEY = 'enable_as';
    public const ROLE_FIELD = 'role';
    public const TABLE_NAME = 'federated_reporting_settings';
    public const SETUP_STATUS_PATH = '/opt/cfengine/federation/cfapache/setup-status.json';
    public const SETUP_STATUS_PUBLICKEY_KEY = 'transport_ssh_public_key';
    public const SETUP_STATUS_ENABLED_KEY = 'configured';
    public const SETUP_STATUS_SSH_FINGERPRINT_KEY = 'transport_ssh_server_fingerprint';
    public const TARGET_STATE = 'target_state';

    /**
     * @var \PDO
     */
    private $cfPdo;

    private $role;
    private $user;

    /**
     * SetupHubModel constructor.
     *
     * @param \PDO $cfPdo
     * @param string $role
     */
    public function __construct(\PDO $cfPdo, string $user, $role = false)
    {
        $this->cfPdo = $cfPdo;
        $this->role = $role;
        $this->user = $user;
    }

    /**
     * @return bool
     */
    public function isConfigured()
    {
        return $this->getValueFromStatusFile(self::SETUP_STATUS_ENABLED_KEY);
    }

    /**
     * @return bool
     */
    public function getPublicKey()
    {
        return $this->getValueFromStatusFile(self::SETUP_STATUS_PUBLICKEY_KEY);
    }

    /**
     * @return bool
     */
    public function getRole()
    {
        return $this->getValueFromStatusFile(self::ROLE_FIELD);
    }

    /**
     * @return mixed
     */
    public function getSshFingerPrint()
    {
        return $this->getValueFromStatusFile(self::SETUP_STATUS_SSH_FINGERPRINT_KEY);
    }

    private function getValueFromStatusFile($key)
    {
        $result = false;

        if (file_exists(self::SETUP_STATUS_PATH)) {
            $statusContent = json_decode(file_get_contents(self::SETUP_STATUS_PATH), JSON_OBJECT_AS_ARRAY);
            $result = isset($statusContent[$key]) ? $statusContent[$key] : false;
        }

        return $result;
    }

    public function getValueByKey($key)
    {
        $sql = sprintf('SELECT value FROM %s WHERE key = ?', self::TABLE_NAME);
        $stmt = $this->cfPdo->prepare($sql);
        $stmt->execute([$key]);
        $result = $stmt->fetch(\PDO::FETCH_ASSOC);

        if (!$result) {
            return false;
        }

        return $result['value'];
    }

    /**
     * @return bool
     * @throws \Exception
     */
    public function enable()
    {
        $this->setSettingsValue(self::ENABLE_AS_HUB_KEY, $this->role);
        $this->setSettingsValue(self::ENABLE_REQUEST_SENT, 1);
        $this->setTargetState(RemoteHub::TARGET_STATE_ON);
    }

    public function getHubRole()
    {
        return $this->getValueByKey(self::ENABLE_AS_HUB_KEY);
    }

    /**
     * @return string
     */
    public function getTargetState()
    {
        return $this->getValueByKey(self::TARGET_STATE);
    }

    /**
     * @param $state
     *
     * @return bool
     * @throws \Exception
     */
    public function setTargetState($state)
    {
        $result = $this->setSettingsValue(self::TARGET_STATE, $state);
        EventService::dispatchEvent(BaseEvents::CHANGED_HUB_STATE, ['state' => $state, 'user' => $this->user]);
        return $result;
    }

    private function setSettingsValue($key, $value)
    {
        $sql = sprintf('INSERT INTO  %s (value, key) VALUES (:value, :key)  ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value', self::TABLE_NAME);
        $stmt = $this->cfPdo->prepare($sql);
        $stmt->execute(['key' => $key, 'value' => $value]);

        if ((int)$stmt->errorCode() > 0) {
            syslog(LOG_ERR, __CLASS__ . '::' . __METHOD__ . ': database error while updating - ' . implode(
                ' - ',
                $stmt->errorInfo()
            ));
            throw new \Exception('Database error, please see error log.');
        }

        return true;
    }
}
