<?php
/**
 * کلاینت وب‌سرویس — کنار پروژه‌تان بگذارید و استفاده کنید.
 *
 *   $api = new WebserviceClient('https://api.neshanpro.com/api/v1.php', 'YOUR_KEY');
 *   $acc = $api->account();
 */

final class WebserviceException extends RuntimeException
{
    public string $errorCode;
    public int $httpStatus;

    public function __construct(string $errorCode, string $message, int $httpStatus = 0)
    {
        parent::__construct($message);
        $this->errorCode = $errorCode;
        $this->httpStatus = $httpStatus;
    }
}

final class WebserviceClient
{
    public function __construct(private string $url, private string $key, private int $timeout = 40)
    {
    }

    /** یک درخواست خام. در صورت خطا WebserviceException می‌دهد. */
    public function call(string $action, array $params = []): array
    {
        $ch = curl_init($this->url);
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => $this->timeout,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_HTTPHEADER     => ['Content-Type: application/json', 'X-API-Key: ' . $this->key],
            CURLOPT_POSTFIELDS     => json_encode(['action' => $action] + $params, JSON_UNESCAPED_UNICODE),
        ]);
        $raw = curl_exec($ch);
        $status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $err = curl_error($ch);
        curl_close($ch);

        if ($raw === false) {
            // شبکه قطع شد: اگر order_id فرستاده بودید، دقیقاً همان درخواست را تکرار کنید
            throw new WebserviceException('network', 'ارتباط برقرار نشد: ' . $err, 0);
        }
        $json = json_decode((string) $raw, true);
        if (!is_array($json)) {
            throw new WebserviceException('bad_response', 'پاسخ نامعتبر از سرور', $status);
        }
        if (empty($json['success'])) {
            throw new WebserviceException((string) ($json['code'] ?? 'error'), (string) ($json['message'] ?? 'خطا'), $status);
        }
        return (array) ($json['data'] ?? []);
    }

    public function account(): array                        { return $this->call('account.info'); }
    public function plans(): array                          { return $this->call('plans.list'); }
    public function quote(array $p): array                 { return $this->call('price.quote', $p); }
    public function services(int $page = 1, int $per = 25): array
                                                            { return $this->call('services.list', ['page' => $page, 'per' => $per]); }
    public function service(int|string $id): array         { return $this->call('services.get', $this->ref($id)); }
    public function configs(int|string $id): array         { return $this->call('services.configs', $this->ref($id)); }
    public function transactions(int $limit = 50): array   { return $this->call('reseller.transactions', ['limit' => $limit]); }

    public function create(int $planId, ?string $orderId = null, ?string $name = null): array
    {
        return $this->call('services.create', array_filter(['plan_id' => $planId, 'order_id' => $orderId, 'name' => $name ?? ('svc-' . bin2hex(random_bytes(4)))]));
    }

    public function createCustom(float $gb, int $days, int $devices = 1, ?string $orderId = null, ?string $name = null): array
    {
        return $this->call('services.create', array_filter([
            'quota_gb' => $gb, 'duration_days' => $days, 'max_devices' => $devices,
            'order_id' => $orderId, 'name' => $name ?? ('svc-' . bin2hex(random_bytes(4))),
        ]));
    }

    public function renew(int|string $id, int $planId, ?string $orderId = null): array
    {
        return $this->call('services.renew', $this->ref($id) + array_filter(['plan_id' => $planId, 'order_id' => $orderId]));
    }

    public function delete(int|string $id): array          { return $this->call('services.delete', $this->ref($id)); }

    private function ref(int|string $id): array
    {
        return is_int($id) || ctype_digit((string) $id) ? ['id' => (int) $id] : ['name' => (string) $id];
    }
}