<?php

declare(strict_types=1);

namespace OnoxWhmcs;

/**
 * Thin HTTP client for the Onoxsoft Panel REST API.
 *
 * - Base URL resolved from WHMCS server params
 * - Bearer token auth (Onoxsoft API token, stored encrypted in WHMCS server password field)
 * - PHP 7.4+ compatible (no match expressions, no named args, no enums)
 */
class ApiClient
{
    private string $baseUrl;

    private string $apiToken;

    private int $timeout = 30;

    /**
     * @param  array  $params  WHMCS module params (serverhostname, serversecure, serverpassword, …)
     */
    public function __construct(array $params)
    {
        $host = rtrim($params['serverhostname'] ?? '', '/');

        if (! preg_match('#^https?://#', $host)) {
            $scheme = ! empty($params['serversecure']) ? 'https' : 'http';
            // Respect non-standard port from WHMCS "Server Port" field
            $port = ! empty($params['serverport']) ? ':'.$params['serverport'] : '';
            $host = $scheme.'://'.$host.$port;
        }

        $this->baseUrl = $host.'/api/v1';
        $this->apiToken = function_exists('decrypt') ? decrypt($params['serverpassword'] ?? '') : ($params['serverpassword'] ?? '');
    }

    // ─── Public HTTP verbs ────────────────────────────────────────────────

    /** @param array<string,mixed>  $params  Query-string params */
    public function get(string $path, array $params = [], array $headers = []): array
    {
        return $this->request('GET', $path, $params, $headers);
    }

    /** @param array<string,mixed>  $body    JSON body */
    public function post(string $path, array $body = [], array $headers = []): array
    {
        return $this->request('POST', $path, $body, $headers);
    }

    /** @param array<string,mixed>  $body    JSON body */
    public function patch(string $path, array $body = [], array $headers = []): array
    {
        return $this->request('PATCH', $path, $body, $headers);
    }

    /** @param array<string,mixed>  $body    JSON body */
    public function delete(string $path, array $body = [], array $headers = []): array
    {
        return $this->request('DELETE', $path, $body, $headers);
    }

    // ─── SSO ─────────────────────────────────────────────────────────────

    /**
     * Generate a short-lived SSO URL for the customer or admin.
     *
     * Calls: POST /api/v1/accounts/{id}/sso
     *
     * @param  string  $accountId  Onoxsoft account ID (numeric string)
     * @param  string  $scope  'customer' | 'admin'
     * @return string Full redirect URL including SSO token
     */
    public function generateSsoUrl(string $accountId, string $scope = 'customer'): string
    {
        $response = $this->request('POST', "/accounts/{$accountId}/sso", ['scope' => $scope]);

        $token = $response['data']['sso_token'] ?? '';
        $expiresAt = $response['data']['expires_at'] ?? '';

        if ($token === '') {
            throw new \RuntimeException(Helper::lang('sso_token_empty'));
        }

        // Strip /api/v1 suffix to get the panel root
        $panelRoot = str_replace('/api/v1', '', $this->baseUrl);

        return $panelRoot.'/auth/sso?token='.urlencode($token)
             .'&account_id='.urlencode($accountId);
    }

    // ─── Internal ─────────────────────────────────────────────────────────

    /**
     * @param  array<string,mixed>  $data  Body (POST/PATCH/DELETE) or query-string (GET)
     * @param  array<string,string>  $extraHeaders
     * @return array<string,mixed>
     *
     * @throws \RuntimeException on cURL failure or HTTP 4xx/5xx
     */
    private function request(string $method, string $path, array $data = [], array $extraHeaders = []): array
    {
        $url = $this->baseUrl.$path;

        $ch = curl_init();

        if ($method === 'GET' && ! empty($data)) {
            $url .= '?'.http_build_query($data);
        }

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_MAXREDIRS, 3);

        $headers = [
            'Authorization: Bearer '.$this->apiToken,
            'Accept: application/json',
            'User-Agent: ONOX-WHMCS-Plugin/1.0',
            'X-Plugin-Version: 1.0.0',
        ];

        if (in_array($method, ['POST', 'PATCH', 'PUT', 'DELETE'], true) && ! empty($data)) {
            $encoded = json_encode($data);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded);
            $headers[] = 'Content-Type: application/json';
            $headers[] = 'Content-Length: '.strlen($encoded);
        }

        foreach ($extraHeaders as $k => $v) {
            $headers[] = $k.': '.$v;
        }

        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

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

        if ($response === false) {
            throw new \RuntimeException(Helper::lang('connection_error').': '.$curlErr);
        }

        $parsed = json_decode((string) $response, true);

        if ($httpCode >= 400) {
            $errMsg = $parsed['error']['message']
                   ?? $parsed['message']
                   ?? "HTTP {$httpCode}";
            throw new \RuntimeException("Onoxsoft API [{$httpCode}]: {$errMsg}");
        }

        return is_array($parsed) ? $parsed : [];
    }
}
