<?php

class YandexIoT
{
    private string $clientId;
    private string $clientSecret;
    private ?string $accessToken = null;
    private ?string $refreshToken = null;

    public function __construct(
        string $clientId,
        string $clientSecret,
        ?string $accessToken = null,
        ?string $refreshToken = null
    ) {
        $this->clientId = $clientId;
        $this->clientSecret = $clientSecret;
        $this->accessToken = $accessToken;
        $this->refreshToken = $refreshToken;
    }

    /**
     * @throws JsonException
     */
    public function initFromFile(string $file): self
    {
        if (!file_exists($file)) {
            throw new RuntimeException('Файл для инициализации не найден');
        }
        $data = json_decode(file_get_contents($file), true, 512, JSON_THROW_ON_ERROR);
        $this->accessToken  = $data['access_token'] ?? null;
        $this->refreshToken = $data['access_token'] ?? null;
        return $this;
    }

    /**
     * Отправка запроса
     *
     * @param string $url
     * @param array $headers
     * @param array|null $data
     * @param string|null $method
     * @return array
     * @throws JsonException
     */
    public static function sendRequest(string $url, array $headers = [], ?array $data = null, ?string $method = null): array
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);

        if (!empty($method)) {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        }

        if (!empty($data)) {
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
        }

        curl_setopt($ch, CURLOPT_FAILONERROR, 0);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_VERBOSE, 0);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        $result = curl_exec($ch);
        $result = json_decode($result, true, 512, JSON_THROW_ON_ERROR);
        curl_close($ch);
        return $result;
    }

    /**
     * @throws JsonException
     */
    public function sendYandexRequest(
        string $function,
        array $additionalHeaders = [],
        ?array $data = null,
        ?string $method = null
    ): array {
        $apiUrl = 'https://api.iot.yandex.net';
        $url = $apiUrl . $function;
        $headers = array_merge(['Authorization: Bearer ' . $this->getAccessToken()], $additionalHeaders);
        return (self::sendRequest($url, $headers, $data, $method));
    }

    /**
     * @throws JsonException
     */
    public function getPowerState(string $deviceId): bool
    {
        $function = '/v1.0/devices/' . $deviceId;
        $result = $this->sendYandexRequest($function);
        if (isset($result['capabilities'])) {
            foreach ($result['capabilities'] as $capability) {
                if ($capability['type'] === 'devices.capabilities.on_off') {
                    return (int)$capability['state']['value'] === 1;
                }
            }
        }
        return false;
    }

    /**
     * @throws JsonException
     */
    public function getProps(string $deviceId): array
    {
        $res = [];
        $function = '/v1.0/devices/' . $deviceId;
        $result = $this->sendYandexRequest($function);

        // состояние по умолчанию
        $res['state'] = false;

        if (isset($result['capabilities'])) {
            foreach ($result['capabilities'] as $capability) {
                if ($capability['type'] === 'devices.capabilities.on_off' && (int)$capability['state']['value'] === 1) {
                    $res['state'] = true;
                    break;
                }
            }
        }

        if (isset($result['properties'])) {
            foreach ($result['properties'] as $prop) {
                $res[$prop['state']['instance']] = $prop['state']['value'];
            }
        }
        return $res;
    }

    /**
     * @throws JsonException
     */
    public function getInfo(): array
    {
        $function = '/v1.0/user/info';
        return $this->sendYandexRequest($function);
    }

    /**
     * @throws JsonException
     */
    public function setPowerState(string $deviceId, bool $state): bool
    {
        $function = '/v1.0/devices/actions';
        $headers = ['Content-Type: application/json'];
        $data = [
            'devices' => [
                'actions' => [
                    'state' => [
                        'instance' => 'on',
                        'value'    => $state ? 'true' : 'false',
                    ],
                    'type'  => 'devices.capabilities.on_off',
                ],
                'id'      => $deviceId,
            ],
        ];
        $result = $this->sendYandexRequest($function, $headers, $data);
        return isset($result['status']) && $result['status'] === 'ok';
    }

    public function getAccessToken(): ?string
    {
        return $this->accessToken;
    }

    public function getRefreshToken(): ?string
    {
        return $this->refreshToken;
    }

    /**
     * @throws JsonException
     */
    public function updateToken(): bool
    {
        $url = 'https://oauth.yandex.ru/token';
        $auth = base64_encode($this->clientId . ':' . $this->clientSecret);
        $headers = [
            'Authorization: Basic ' . $auth,
            'application/x-www-form-urlencoded'
        ];
        $data = [
            'grant_type' => 'refresh_token',
            'refresh_token' => $this->getRefreshToken(),
        ];
        $response = self::sendRequest($url, $headers, $data);
        if (isset($response['access_token'])) {
            $this->accessToken = '';
            $this->refreshToken = '';
            return true;
        }

        return false;
    }
}