<?php
// PHP 8.0+, with HTTPS support enabled for stream wrappers.
class Result {
    public mixed $data;
    public ?array $error;
    public array $meta;
    public ?float $exec_time;
    public function __construct(array $body) {
        $this->data = $body['data'] ?? null;
        $this->error = $body['error'] ?? null;
        $this->meta = $body['meta'] ?? [];
        $this->exec_time = isset($body['exec_time']) ? (float)$body['exec_time'] : null;
    }
}
class SinglebaseClient {
    public function __construct(private string $api_key, private string $api_url, private ?string $bearer = null) {}
    public function call(string $operation, array $payload, ?array $options = null): Result {
        $headers = ['Content-Type: application/json', 'X-API-KEY: ' . $this->api_key];
        if ($this->bearer !== null) $headers[] = 'Authorization: Bearer ' . $this->bearer;
        $body = ['operation' => $operation, 'payload' => (object)$payload];
        if ($options !== null) $body['options'] = (object)$options;
        $context = stream_context_create(['http' => [
            'method' => 'POST', 'header' => implode("\r\n", $headers),
            'content' => json_encode($body, JSON_THROW_ON_ERROR),
            'timeout' => 30, 'ignore_errors' => true, 'follow_location' => 0
        ]]);
        $raw = file_get_contents($this->api_url, false, $context);
        if ($raw === false) throw new RuntimeException('HTTP transport failed');
        $decoded = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
        if (!is_array($decoded)) throw new RuntimeException('Expected a JSON API envelope');
        $status = 0;
        foreach ($http_response_header ?? [] as $line) {
            if (preg_match('/^HTTP\/\S+\s+(\d{3})/', $line, $m)) $status = (int)$m[1];
        }
        if ($status >= 400 && !isset($decoded['error'])) throw new RuntimeException('HTTP ' . $status . ' without an API error envelope');
        return new Result($decoded);
    }
}
