<?php

declare(strict_types=1);

namespace App\Domain\License\Services;

use App\Domain\License\Exceptions\LicenseClientException;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;

/**
 * License Server (onox.com.tr) ile konuşan HTTP istemci.
 *
 * 5 endpoint sarmalayıcısı:
 * - activate    → ilk lisans bağlama (intent token ile)
 * - heartbeat   → periyodik canlılık + lisans yenileme
 * - transferIp  → IP değişikliği (operatör onayı sonrası)
 * - checkIntent → satın alma sonrası polling
 * - crackReport → anti-tamper sinyal raporu (fire-and-forget)
 *
 * Security:
 * - Response Ed25519 JWT verify ZORUNLU (sahte License Server forge edemez)
 * - Request HMAC-SHA256 imzalama (X-License-UUID + X-License-Signature)
 */
class OnoxsoftLicenseClient
{
    public function __construct(
        private string $baseUrl,
        private JwtVerifier $verifier,
        private int $timeoutSeconds = 10,
    ) {}

    public function activate(array $payload): array
    {
        $url = rtrim($this->baseUrl, '/').'/api/v1/activate';
        $response = $this->postJson($url, $payload);

        return $this->verifyResponse($response);
    }

    public function heartbeat(string $licenseUuid, array $payload): array
    {
        $url = rtrim($this->baseUrl, '/').'/api/v1/heartbeat';
        $headers = $this->signRequest('POST', 'api/v1/heartbeat', $payload, $licenseUuid);
        $response = $this->postJson($url, $payload, $headers);

        return $this->verifyResponse($response);
    }

    public function transferIp(string $licenseUuid, array $payload): array
    {
        $url = rtrim($this->baseUrl, '/').'/api/v1/transfer-ip';
        $headers = $this->signRequest('POST', 'api/v1/transfer-ip', $payload, $licenseUuid);
        $response = $this->postJson($url, $payload, $headers);

        return $this->verifyResponse($response);
    }

    /**
     * Polling endpoint — panel waits for license to be ready after purchase.
     * Doesn't throw on 404 (status: not_found) — that's normal status response.
     */
    public function checkIntent(string $token): array
    {
        $url = rtrim($this->baseUrl, '/').'/api/v1/license/check?intent='.urlencode($token);

        try {
            $response = Http::timeout($this->timeoutSeconds)->acceptJson()->get($url);
        } catch (ConnectionException $e) {
            throw LicenseClientException::networkError($e->getMessage());
        }

        return $response->json() ?? ['status' => 'unknown'];
    }

    /**
     * Crack signal report — fire-and-forget. Never throws, never blocks panel.
     */
    public function crackReport(string $licenseUuid, string $signal, array $payload = []): void
    {
        $url = rtrim($this->baseUrl, '/').'/api/v1/crack-report';
        $body = ['signal' => $signal, 'payload' => $payload];
        $headers = $this->signRequest('POST', 'api/v1/crack-report', $body, $licenseUuid);

        try {
            Http::timeout($this->timeoutSeconds)
                ->withHeaders($headers)
                ->acceptJson()
                ->asJson()
                ->post($url, $body);
        } catch (\Throwable) {
            // Sessizce yut — panel'i bozma
        }
    }

    public function signRequest(string $method, string $path, array $body, string $licenseUuid): array
    {
        $bodyJson = empty($body) ? '' : json_encode($body);
        $payload = strtoupper($method).'|'.ltrim($path, '/').'|'.$bodyJson;

        return [
            'X-License-UUID' => $licenseUuid,
            'X-License-Signature' => hash_hmac('sha256', $payload, $licenseUuid),
            'Accept' => 'application/json',
            'Content-Type' => 'application/json',
        ];
    }

    private function postJson(string $url, array $body, array $headers = []): Response
    {
        try {
            return Http::timeout($this->timeoutSeconds)
                ->withHeaders($headers)
                ->acceptJson()
                ->asJson()
                ->post($url, $body);
        } catch (ConnectionException $e) {
            throw LicenseClientException::networkError($e->getMessage());
        }
    }

    private function verifyResponse(Response $response): array
    {
        $data = $response->json();
        if (! is_array($data)) {
            throw LicenseClientException::invalidResponse('JSON parse fail');
        }

        if ($response->failed()) {
            $error = $data['error'] ?? "HTTP {$response->status()}";
            throw new LicenseClientException($error);
        }

        $jwt = $data['license_jwt'] ?? null;
        if (! $jwt) {
            throw LicenseClientException::invalidResponse('license_jwt eksik');
        }

        $claims = $this->verifier->verify($jwt);
        if ($claims === null) {
            throw LicenseClientException::jwtVerifyFailed();
        }

        return [
            'jwt' => $jwt,
            'claims' => $claims,
            'expires_at' => $data['expires_at'] ?? null,
        ];
    }
}
