<?php

namespace App\Services\Licensing;

use Illuminate\Support\Facades\Http;

/**
 * Talks to the license server — the single endpoint we control
 * (docs/features/installer.md §6.2). This is the only place that knows the
 * server's URL and request/response shape. Used both at install time (Step 3)
 * and by the background 30-day re-check.
 *
 * The license check must never be allowed to hard-block: an unreachable server
 * resolves to a structured `{ok: false, reason: 'unreachable'}` so the caller
 * can offer the offline-activation path (install) or simply keep running and
 * surface a banner (re-check). The URL passes through the `license.check_url`
 * filter so a fork can point it elsewhere.
 */
class LicenseClient
{
    /**
     * GET the validator endpoint with the purchase code + domain bound to
     * this install. Matches the Infinitie validator contract:
     *
     *   GET <check_url>?purchase_code=…&domain_url=…&item_id=<WEB_CODE>
     *
     * The server replies with JSON:
     *   - On valid:   { error: 0, purchase_code, token, username, item_id, … }
     *   - On invalid: { error: 1, message: '…' }
     *
     * Returns a structured result the caller can branch on without knowing
     * the transport details:
     *   - ok=true               → server responded; `body` holds the JSON.
     *   - ok=false, reason=...  → 'unreachable' | 'http_error' | 'bad_response'.
     *
     * @param  array<string,mixed>  $payload
     * @return array{ok: bool, body?: array<string,mixed>, status?: int, reason?: string, error?: string}
     */
    public function check(array $payload): array
    {
        $url = (string) apply_filters('license.check_url', (string) config('pos.license.check_url'));
        if ($url === '') {
            return ['ok' => false, 'reason' => 'unreachable', 'error' => __('installer.license.errors.no_server')];
        }

        // Validator-style query params — only the three the server
        // recognises. Extra payload fields the caller passed (fingerprint
        // / ip / version) ride along as additional query args so a
        // future server version can read them, but legacy validators
        // ignore unknown keys.
        $query = array_filter([
            'purchase_code' => $payload['license_key'] ?? $payload['purchase_code'] ?? null,
            'domain_url'    => $payload['install_url'] ?? $payload['domain_url']    ?? null,
            'item_id'       => $payload['item_id']     ?? (string) config('pos.license.item_id', ''),
        ], fn ($v) => $v !== null && $v !== '');

        // Anything else the caller wanted to send (fingerprint, ip,
        // version) tacks onto the query — harmless to the legacy
        // validator, useful to a future endpoint.
        $extra = array_diff_key($payload, array_flip(['license_key', 'purchase_code', 'install_url', 'domain_url', 'item_id']));
        $query += $extra;

        try {
            $response = Http::timeout((int) config('pos.license.timeout', 15))
                ->acceptJson()
                ->get($url, $query);
        } catch (\Throwable $e) {
            report($e);

            return ['ok' => false, 'reason' => 'unreachable', 'error' => __('installer.license.errors.unreachable')];
        }

        if ($response->failed()) {
            return [
                'ok'     => false,
                'reason' => 'http_error',
                'status' => $response->status(),
                'error'  => __('installer.license.errors.http', ['status' => $response->status()]),
            ];
        }

        $body = $response->json();
        if (! is_array($body)) {
            return ['ok' => false, 'reason' => 'bad_response', 'error' => __('installer.license.errors.bad_response')];
        }

        return ['ok' => true, 'body' => $body, 'status' => $response->status()];
    }
}
