<?php
namespace EasyChat\Library;

use EasyChat\Library\Http\ExternalHttpClient;

class CentralGatewayClient {
    public static function normalizeBaseUrl($url) {
        $url = trim((string)$url);
        if ($url === '') {
            return '';
        }

        $parts = parse_url($url);
        if (!$parts || strtolower($parts['scheme'] ?? '') !== 'https' || empty($parts['host'])) {
            return '';
        }

        $base_url = rtrim($url, '/');
        $path = rtrim($parts['path'] ?? '', '/');

         
         
        if ($path === '' || $path === '/') {
            return $base_url . '/api/v1';
        }
        if ($path !== '/api/v1' && substr($path, -7) !== '/api/v1') {
            return $base_url . '/api/v1';
        }

        return $base_url;
    }

    public static function ensureInstallId($model_setting) {
        $install_id = trim((string)$model_setting->get('chat_install_id', ''));
        if ($install_id !== '') {
            return $install_id;
        }

        $install_id = 'inst_' . bin2hex(random_bytes(16));
        $model_setting->set('chat_install_id', $install_id);
        return $install_id;
    }

    public static function ensureInstallSecret($model_setting) {
        $secret = trim((string)$model_setting->get('chat_install_secret', ''));
        if ($secret !== '') {
            return $secret;
        }

         
         
        $secret = 'instsec_' . bin2hex(random_bytes(32));
        $model_setting->set('chat_install_secret', $secret);
        return $secret;
    }

    private static function signedEnvelope($secret, $install_id, array $payload) {
        $timestamp = (string)time();
        $nonce = bin2hex(random_bytes(16));
        $payload_json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        $canonical = $install_id . '|' . $timestamp . '|' . $nonce . '|' . ($payload_json ?: '{}');

        return [
            'install_id' => $install_id,
            'timestamp' => $timestamp,
            'nonce' => $nonce,
            'payload' => $payload,
            'signature' => base64_encode(hash_hmac('sha256', $canonical, $secret, true)),
        ];
    }

    public static function signedBody($license_key, $install_id, array $payload) {
        $body = self::signedEnvelope($license_key, $install_id, $payload);
        $body['license_key'] = $license_key;
        return $body;
    }

    public static function signedUnlicensedBody($install_secret, $install_id, array $payload) {
        $body = self::signedEnvelope($install_secret, $install_id, $payload);
        $body['mode'] = 'unlicensed';
        $body['install_secret'] = $install_secret;
        return $body;
    }

    public static function withBaseDomain($model_setting, array $payload) {
        $domain = trim((string)($payload['domain'] ?? ''));
        if ($domain === '' || !class_exists(__NAMESPACE__ . '\\WidgetDomainPolicy')) {
            return $payload;
        }

        $policy = WidgetDomainPolicy::fromSettings($model_setting);
        $base_domain = $policy->baseDomain();
        if ($base_domain !== '') {
            $payload['base_domain'] = $base_domain;
        }

        return $payload;
    }

    public static function postJson($base_url, $path, array $body, $timeout = 5, $registry = null) {
        $base_url = rtrim($base_url, '/');
        $path = '/' . ltrim($path, '/');
        if (substr($base_url, -7) === '/api/v1' && strpos($path, '/api/v1/') === 0) {
            $path = substr($path, 7);
        }

        $url = $base_url . $path;
        $client = new ExternalHttpClient($registry);
        $result = $client->request('POST', $url, [
            'headers' => ['Content-Type: application/json'],
            'body' => json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            'timeout' => (int)$timeout,
            'connect_timeout' => 4,
            'trust_profile' => 'easychat_gateway',
            'proxy_profile' => 'easychat_gateway',
        ]);

        $raw = is_string($result['raw'] ?? null) ? $result['raw'] : '';
        $decoded = json_decode($raw, true);
        if (!is_array($decoded)) {
            $decoded = null;
        }
        $transport_error = !empty($result['transport_ok']) ? '' : (string)($result['curl_error'] ?? 'curl_failed');
        $error_code = self::extractErrorCode($decoded, $transport_error);

        return [
            'status' => (int)($result['http_code'] ?? 0),
            'response' => $raw,
            'json' => $decoded,
            'error_code' => $error_code,
            'error' => $transport_error,
            'tls_fallback_used' => !empty($result['tls_fallback_used']),
        ];
    }

    private static function extractErrorCode($decoded, $transport_error) {
        if (is_array($decoded)) {
            if (isset($decoded['error']) && is_scalar($decoded['error'])) {
                return (string)$decoded['error'];
            }
            if (isset($decoded['code']) && is_scalar($decoded['code'])) {
                return (string)$decoded['code'];
            }
            if (isset($decoded['payload']) && is_array($decoded['payload'])) {
                if (isset($decoded['payload']['error']) && is_scalar($decoded['payload']['error'])) {
                    return (string)$decoded['payload']['error'];
                }
                if (isset($decoded['payload']['code']) && is_scalar($decoded['payload']['code'])) {
                    return (string)$decoded['payload']['code'];
                }
            }
            if (isset($decoded['error']) && is_array($decoded['error']) && isset($decoded['error']['status']) && is_scalar($decoded['error']['status'])) {
                return (string)$decoded['error']['status'];
            }
        }

        return (string)$transport_error;
    }

    public static function buildSignedInstallBody($model_setting, $domain) {
        $license = trim((string)$model_setting->get('chat_license'));
        $install_id = self::ensureInstallId($model_setting);
        $payload = self::withBaseDomain($model_setting, ['domain' => $domain]);

        if ($license !== '') {
            return self::signedBody($license, $install_id, $payload);
        }

        return self::signedUnlicensedBody(
            self::ensureInstallSecret($model_setting),
            $install_id,
            $payload
        );
    }
}
