<?php
declare(strict_types=1);

/**
 * Korba XChange Client
 *
 * Location:
 *   public_html/includes/korba_client.php
 *
 * Auth:
 *   Authorization: HMAC {client_key}:{hmac_signature}
 *
 * Signature message:
 *   sorted(body keys asc) => key=value&key2=value2...
 *
 * IMPORTANT:
 * - Do NOT leak secret key in responses/logs.
 */

require_once __DIR__ . '/config.php';

function korba_base_url(): string {
    $base = (string)(defined('KORBA_BASE_URL') ? KORBA_BASE_URL : '');
    return rtrim($base, '/');
}

function korba_client_id(): int {
    return (int)(defined('KORBA_CLIENT_ID') ? KORBA_CLIENT_ID : 0);
}

/**
 * Korba signature sometimes expects numeric values normalized.
 * - integers => "15.0"
 * - decimals => trimmed (e.g. "10.5", "10.25")
 */
function korba_num_to_string(float $n): string {
    $s = sprintf('%.8F', $n);
    $s = rtrim($s, '0');
    $s = rtrim($s, '.');
    if (strpos($s, '.') === false) $s .= '.0';
    return $s;
}

function korba_value_to_string(mixed $v): string {
    if (is_bool($v)) return $v ? 'true' : 'false';
    if (is_int($v)) return (string)$v;
    if (is_float($v)) return korba_num_to_string($v);
    if ($v === null) return '';
    if (is_array($v)) return json_encode($v, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    return (string)$v;
}

function korba_hmac_urlencode_values(): bool {
    return (bool)(defined('KORBA_HMAC_URLENCODE_VALUES') ? KORBA_HMAC_URLENCODE_VALUES : false);
}

/** message used for signature */
function korba_hmac_message(array $payload): string {
    ksort($payload, SORT_STRING);
    $pairs = [];
    $encode = korba_hmac_urlencode_values();
    foreach ($payload as $k => $v) {
        $val = korba_value_to_string($v);
        $pairs[] = $encode ? ($k . '=' . rawurlencode($val)) : ($k . '=' . $val);
    }
    return implode('&', $pairs);
}

function korba_auth_type(): string {
    return strtolower((string)(defined('KORBA_AUTH_TYPE') ? KORBA_AUTH_TYPE : 'hmac'));
}

function korba_hmac_algo(): string {
    return strtolower((string)(defined('KORBA_HMAC_ALGO') ? KORBA_HMAC_ALGO : 'sha256'));
}

function korba_hmac_prefix(): string {
    return (string)(defined('KORBA_HMAC_PREFIX') ? KORBA_HMAC_PREFIX : 'HMAC');
}

function korba_client_key(): string {
    if (defined('KORBA_CLIENT_KEY') && (string)KORBA_CLIENT_KEY !== '') return (string)KORBA_CLIENT_KEY;
    if (defined('KORBA_HMAC_CLIENT_KEY') && (string)KORBA_HMAC_CLIENT_KEY !== '') return (string)KORBA_HMAC_CLIENT_KEY;
    return '';
}

function korba_secret_key(): string {
    if (defined('KORBA_SECRET_KEY') && (string)KORBA_SECRET_KEY !== '') return (string)KORBA_SECRET_KEY;
    if (defined('KORBA_HMAC_SECRET_KEY') && (string)KORBA_HMAC_SECRET_KEY !== '') return (string)KORBA_HMAC_SECRET_KEY;
    return '';
}

function korba_headers(array $payload): array {
    $headers = [
        'Content-Type: application/json',
        'Accept: application/json',
    ];

    if (defined('KORBA_MOCK_MODE') && KORBA_MOCK_MODE === true) return $headers;

    if (korba_auth_type() === 'hmac') {
        $clientKey = korba_client_key();
        $secretKey = korba_secret_key();
        if ($clientKey === '' || $secretKey === '') return $headers;

        $msg = korba_hmac_message($payload);
        $sig = hash_hmac(korba_hmac_algo(), $msg, $secretKey);
        $headers[] = 'Authorization: ' . korba_hmac_prefix() . ' ' . $clientKey . ':' . $sig;
    }
    return $headers;
}

/** Safe substring */
function korba_substr(string $s, int $len = 800): string {
    return substr($s, 0, $len);
}

/**
 * POST helper
 */
function korba_post(string $endpointPath, array $payload, int $timeout = 35, bool $wantDebug = false): array {
    $base = korba_base_url();
    if ($base === '') {
        return ['ok' => false, 'http' => 0, 'error' => 'KORBA_BASE_URL is not configured'];
    }

    $endpointPath = ltrim($endpointPath, '/');
    $url = $base . '/' . $endpointPath;

    $jsonFlags = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
    if (defined('JSON_PRESERVE_ZERO_FRACTION')) $jsonFlags |= JSON_PRESERVE_ZERO_FRACTION;

    $bodyJson = json_encode($payload, $jsonFlags);

    $respHeaders = [];

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => korba_headers($payload),
        CURLOPT_POSTFIELDS => $bodyJson,
        CURLOPT_TIMEOUT => $timeout,
        CURLOPT_CONNECTTIMEOUT => 15,
        CURLOPT_USERAGENT => 'BoabanPlus/1.0 (+https://boabanpluslimited.org)',
        CURLOPT_HEADERFUNCTION => function ($curl, $headerLine) use (&$respHeaders) {
            $len = strlen($headerLine);
            $headerLine = trim($headerLine);
            if ($headerLine !== '' && strpos($headerLine, ':') !== false) {
                [$k, $v] = explode(':', $headerLine, 2);
                $respHeaders[strtolower(trim($k))] = trim($v);
            }
            return $len;
        },
    ]);

    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);

    $raw = curl_exec($ch);
    $err = curl_error($ch);
    $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($raw === false) {
        return ['ok' => false, 'http' => $code, 'error' => $err ?: 'Korba request failed'];
    }

    $json = json_decode((string)$raw, true);
    $is2xx = ($code >= 200 && $code < 300);

    return [
        'ok' => ($is2xx && is_array($json)),
        'http' => $code,
        'data' => $json,
        'raw' => $is2xx ? null : korba_substr((string)$raw, 800),
    ];
}

/* --------------------------- AIRTIME --------------------------- */
function korba_airtime_topup(array $payload, bool $debug = false): array {
    if (!isset($payload['client_id'])) $payload['client_id'] = korba_client_id();
    return korba_post('topup/', $payload, 40, $debug);
}

function korba_transaction_status(string $transactionId, bool $debug = false): array {
    return korba_post('transaction_status/', [
        'transaction_id' => $transactionId,
        'client_id' => korba_client_id(),
    ], 30, $debug);
}
