<?php

require_once __DIR__ . '/constants.php';

class SupabaseClient {
    private string $baseUrl;
    private string $key;
    private ?string $userToken;

    public function __construct(?string $userToken = null, bool $useServiceKey = false) {
        $this->baseUrl = SUPABASE_REST;
        $this->key = $useServiceKey ? SUPABASE_SERVICE_KEY : SUPABASE_ANON_KEY;
        $this->userToken = $userToken;
    }

    private function headers(): array {
        $h = [
            'apikey: ' . $this->key,
            'Content-Type: application/json',
            'Prefer: return=representation'
        ];
        if ($this->userToken) {
            $h[] = 'Authorization: Bearer ' . $this->userToken;
        } else {
            $h[] = 'Authorization: Bearer ' . $this->key;
        }
        return $h;
    }

    public function get(string $table, array $params = []): array {
        $url = $this->baseUrl . '/' . $table;
        if (!empty($params)) {
            $url .= '?' . http_build_query($params);
        }
        return $this->request('GET', $url);
    }

    public function getOne(string $table, array $params = []): ?array {
        $params['limit'] = 1;
        $h = $this->headers();
        $h[] = 'Accept: application/vnd.pgrst.object+json';
        $url = $this->baseUrl . '/' . $table . '?' . http_build_query($params);
        return $this->request('GET', $url, null, $h);
    }

    public function insert(string $table, array $data): array {
        $url = $this->baseUrl . '/' . $table;
        return $this->request('POST', $url, $data);
    }

    public function update(string $table, array $data, array $params = []): array {
        $url = $this->baseUrl . '/' . $table;
        if (!empty($params)) {
            $url .= '?' . http_build_query($params);
        }
        return $this->request('PATCH', $url, $data);
    }

    public function delete(string $table, array $params = []): array {
        $url = $this->baseUrl . '/' . $table;
        if (!empty($params)) {
            $url .= '?' . http_build_query($params);
        }
        return $this->request('DELETE', $url);
    }

    public function upsert(string $table, array $data, string $onConflict = ''): array {
        $url = $this->baseUrl . '/' . $table;
        $headers = $this->headers();
        $headers = array_map(function($h) use ($onConflict) {
            if (str_starts_with($h, 'Prefer:')) {
                return 'Prefer: return=representation,resolution=merge-duplicates';
            }
            return $h;
        }, $headers);
        if ($onConflict) {
            $url .= '?on_conflict=' . $onConflict;
        }
        return $this->request('POST', $url, $data, $headers);
    }

    public function rpc(string $fn, array $data = []): array {
        $url = $this->baseUrl . '/rpc/' . $fn;
        return $this->request('POST', $url, $data);
    }

    private function request(string $method, string $url, ?array $body = null, ?array $customHeaders = null): array {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $customHeaders ?? $this->headers());
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);

        switch ($method) {
            case 'POST':
                curl_setopt($ch, CURLOPT_POST, true);
                if ($body) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
                break;
            case 'PATCH':
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
                if ($body) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
                break;
            case 'DELETE':
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
                break;
        }

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        $decoded = json_decode($response, true);
        if ($httpCode >= 400) {
            return ['error' => $decoded['message'] ?? $decoded['error'] ?? 'Request failed', 'code' => $httpCode];
        }
        return $decoded ?? [];
    }
}
