<?php

declare(strict_types=1);

namespace BotGuard\Services\License;

/**
 * License activation client.
 *
 * Talks to the (separately-shipped) License Server when `license.server_url` is
 * configured. When no server is configured, or the server is unreachable and
 * offline activation is permitted, it returns an offline/trial status so the
 * installer can proceed. This keeps the product installable and testable without
 * standing up the license portal.
 */
final class LicenseClient
{
    /**
     * Activate a license key for a domain.
     */
    public function activate(string $key, string $domain, string $email, string $company = ''): LicenseStatus
    {
        $serverUrl = rtrim((string) config('license.server_url', ''), '/');
        $offlineAllowed = (bool) config('license.offline_allowed', true);

        // No license server configured → offline/trial (still requires basic details).
        if ($serverUrl === '') {
            if ($key === '' && ! $offlineAllowed) {
                return LicenseStatus::invalid('A license key is required.');
            }

            return LicenseStatus::offline(
                $key === '' ? 'No license key supplied — installing in trial mode.' : 'License recorded (offline mode; server not configured).'
            );
        }

        // Contact the license server.
        $endpoint = $serverUrl . (string) config('license.endpoints.activate', '/api/v1/activate');
        $response = $this->post($endpoint, [
            'product' => (string) config('license.product', 'botguard'),
            'key'     => $key,
            'domain'  => $domain,
            'email'   => $email,
            'company' => $company,
        ]);

        if ($response === null) {
            // Server unreachable.
            return $offlineAllowed
                ? LicenseStatus::offline('License server unreachable — installed in offline mode. It will re-verify later.')
                : LicenseStatus::invalid('Could not reach the license server. Please try again.');
        }

        if (! empty($response['valid'])) {
            return LicenseStatus::licensed(
                (string) ($response['token'] ?? ''),
                isset($response['expires_at']) ? (string) $response['expires_at'] : null,
                (string) ($response['message'] ?? 'License activated.'),
            );
        }

        return LicenseStatus::invalid((string) ($response['message'] ?? 'The license key was rejected.'));
    }

    /**
     * POST JSON and decode the response. Returns null on transport failure.
     *
     * @param array<string, string> $data
     * @return array<string, mixed>|null
     */
    private function post(string $url, array $data): ?array
    {
        $body = json_encode($data);

        if (function_exists('curl_init')) {
            $ch = curl_init($url);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_POST           => true,
                CURLOPT_POSTFIELDS     => $body,
                CURLOPT_HTTPHEADER     => ['Content-Type: application/json', 'Accept: application/json'],
                CURLOPT_TIMEOUT        => 6,
                CURLOPT_CONNECTTIMEOUT => 4,
            ]);
            $raw = curl_exec($ch);
            $ok = $raw !== false && curl_errno($ch) === 0;
            curl_close($ch);
            if (! $ok) {
                return null;
            }
        } else {
            $context = stream_context_create([
                'http' => [
                    'method'        => 'POST',
                    'header'        => "Content-Type: application/json\r\nAccept: application/json",
                    'content'       => $body,
                    'timeout'       => 6,
                    'ignore_errors' => true,
                ],
            ]);
            $raw = @file_get_contents($url, false, $context);
            if ($raw === false) {
                return null;
            }
        }

        $decoded = json_decode((string) $raw, true);

        return is_array($decoded) ? $decoded : null;
    }
}
