<?php

use Illuminate\Support\Facades\Http;
use Modules\Shared\Bnfexpress\BnfexpressAdminClient;
use Modules\Shared\Bnfexpress\Exceptions\BnfexpressApiException;

$config = [
    'ai_api_url' => 'https://bnfexpress.test',
    'client_id' => 'ev_admin',
    'client_secret' => 'shared-secret',
];

function assertSignedCorrectly($request, string $method, string $path, string $rawBody, string $secret): bool
{
    $timestamp = $request->header('X-Timestamp')[0] ?? null;
    $expected = hash_hmac('sha256', strtoupper($method)."\n".$path."\n".$timestamp."\n".$rawBody, $secret);

    return $request->hasHeader('X-Client-Id', 'ev_admin')
        && $timestamp !== null
        && abs(time() - (int) $timestamp) < 5
        && $request->header('X-Signature')[0] === $expected;
}

test('listFaqs signs a GET request and excludes the query string from the signed path', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['faqs' => []])]);

    (new BnfexpressAdminClient($config))->listFaqs(q: 'range', search: 'semantic', limit: 10, offset: 0);

    Http::assertSent(function ($request) use ($config) {
        return $request->url() === 'https://bnfexpress.test/admin/faqs?agent=ev&q=range&search=semantic&limit=10&offset=0'
            && assertSignedCorrectly($request, 'GET', '/admin/faqs', '', $config['client_secret']);
    });
});

test('listFaqs omits search when q is empty', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['faqs' => []])]);

    (new BnfexpressAdminClient($config))->listFaqs();

    Http::assertSent(fn ($request) => $request->url() === 'https://bnfexpress.test/admin/faqs?agent=ev');
});

test('createFaq signs the exact raw JSON body being sent', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['id' => 1], 201)]);

    (new BnfexpressAdminClient($config))->createFaq('How do I charge?', ['source' => 'admin']);

    $rawBody = json_encode([
        'content' => 'How do I charge?',
        'agent' => 'ev',
        'metadata' => ['source' => 'admin'],
    ]);

    Http::assertSent(function ($request) use ($config, $rawBody) {
        return $request->url() === 'https://bnfexpress.test/admin/faqs'
            && $request->method() === 'POST'
            && $request->body() === $rawBody
            && assertSignedCorrectly($request, 'POST', '/admin/faqs', $rawBody, $config['client_secret']);
    });
});

test('getActiveInstruction requests the active instruction for the ev agent', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['content' => 'You are the EV assistant.'])]);

    $result = (new BnfexpressAdminClient($config))->getActiveInstruction();

    expect($result)->toBe(['content' => 'You are the EV assistant.']);
    Http::assertSent(fn ($request) => $request->url() === 'https://bnfexpress.test/admin/agent-instructions/active?agent=ev');
});

test('a non-2xx response surfaces the gateway detail message', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['detail' => 'FAQ not found.'], 404)]);

    (new BnfexpressAdminClient($config))->getFaq(999);
})->throws(BnfexpressApiException::class, 'FAQ not found.');

test('a FastAPI validation error (detail as a list of objects) is flattened into a readable message', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response([
        'detail' => [
            ['type' => 'int_parsing', 'loc' => ['path', 'suggestion_id'], 'msg' => 'Input should be a valid integer, unable to parse string as an integer', 'input' => 'batch'],
        ],
    ], 422)]);

    (new BnfexpressAdminClient($config))->getSuggestion('batch');
})->throws(BnfexpressApiException::class, 'Input should be a valid integer, unable to parse string as an integer');

test('deleteFaq signs a bodyless DELETE request', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['deleted' => true])]);

    (new BnfexpressAdminClient($config))->deleteFaq(5);

    Http::assertSent(function ($request) use ($config) {
        return $request->url() === 'https://bnfexpress.test/admin/faqs/5'
            && $request->method() === 'DELETE'
            && assertSignedCorrectly($request, 'DELETE', '/admin/faqs/5', '', $config['client_secret']);
    });
});

test('createSuggestion sends text_display/lang/intent/weight/source', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['id' => 1], 201)]);

    (new BnfexpressAdminClient($config))->createSuggestion('How do I charge?', 'en', 'charging', 5, 'manual');

    $rawBody = json_encode([
        'text_display' => 'How do I charge?',
        'lang' => 'en',
        'intent' => 'charging',
        'weight' => 5,
        'source' => 'manual',
    ]);

    Http::assertSent(fn ($request) => $request->url() === 'https://bnfexpress.test/admin/suggestions'
        && $request->method() === 'POST'
        && $request->body() === $rawBody);
});

test('updateSuggestion only sends the given fields', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['id' => 1])]);

    (new BnfexpressAdminClient($config))->updateSuggestion(1, weight: 10);

    Http::assertSent(fn ($request) => $request->body() === json_encode(['weight' => 10]));
});

test('batchCreateSuggestions posts items and returns the created/skipped/trie_rebuilt result', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['created' => 2, 'skipped' => 1, 'trie_rebuilt' => true])]);

    $result = (new BnfexpressAdminClient($config))->batchCreateSuggestions([
        ['text' => 'a', 'lang' => 'en'],
        ['text' => 'b', 'lang' => 'en', 'intent' => 'x'],
    ]);

    expect($result)->toBe(['created' => 2, 'skipped' => 1, 'trie_rebuilt' => true]);
    Http::assertSent(fn ($request) => $request->url() === 'https://bnfexpress.test/admin/suggestions/batch'
        && $request->method() === 'POST'
        && $request['items'] === [
            ['text' => 'a', 'lang' => 'en'],
            ['text' => 'b', 'lang' => 'en', 'intent' => 'x'],
        ]);
});

test('batchDeleteSuggestions sends a JSON body on DELETE and signs it', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['deleted' => 2, 'skipped' => 0])]);

    $result = (new BnfexpressAdminClient($config))->batchDeleteSuggestions([1, 2]);

    $rawBody = json_encode(['ids' => [1, 2]]);

    expect($result)->toBe(['deleted' => 2, 'skipped' => 0]);
    Http::assertSent(function ($request) use ($config, $rawBody) {
        return $request->url() === 'https://bnfexpress.test/admin/suggestions/batch'
            && $request->method() === 'DELETE'
            && $request->body() === $rawBody
            && assertSignedCorrectly($request, 'DELETE', '/admin/suggestions/batch', $rawBody, $config['client_secret']);
    });
});

test('listSuggestionMisses filters by was_used', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response([])]);

    (new BnfexpressAdminClient($config))->listSuggestionMisses(wasUsed: false, limit: 25);

    Http::assertSent(fn ($request) => $request->url() === 'https://bnfexpress.test/admin/suggestion-misses?was_used=0&limit=25'
        || $request->url() === 'https://bnfexpress.test/admin/suggestion-misses?was_used=&limit=25');
});

test('promoteSuggestionMisses posts miss_ids with an optional lang/intent override', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['created' => 1, 'skipped' => 0, 'trie_rebuilt' => true])]);

    $result = (new BnfexpressAdminClient($config))->promoteSuggestionMisses([1, 2], lang: 'my');

    expect($result)->toBe(['created' => 1, 'skipped' => 0, 'trie_rebuilt' => true]);
    Http::assertSent(fn ($request) => $request->url() === 'https://bnfexpress.test/admin/suggestion-misses/promote'
        && $request->method() === 'POST'
        && $request->body() === json_encode(['miss_ids' => [1, 2], 'lang' => 'my']));
});

test('syncSuggestions triggers a sync job and returns the job id', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['job_id' => 'abc-123'], 202)]);

    $result = (new BnfexpressAdminClient($config))->syncSuggestions();

    expect($result)->toBe(['job_id' => 'abc-123']);
    Http::assertSent(fn ($request) => $request->url() === 'https://bnfexpress.test/admin/suggestions/sync'
        && $request->method() === 'POST');
});

test('getSuggestionSyncStatus polls the job status endpoint', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response(['status' => 'finished', 'result' => ['synced' => 3]])]);

    $result = (new BnfexpressAdminClient($config))->getSuggestionSyncStatus('abc-123');

    expect($result)->toBe(['status' => 'finished', 'result' => ['synced' => 3]]);
    Http::assertSent(fn ($request) => $request->url() === 'https://bnfexpress.test/admin/suggestions/sync/abc-123'
        && $request->method() === 'GET');
});

test('deleteSuggestionEmbedding signs a bodyless DELETE request', function () use ($config) {
    Http::fake(['bnfexpress.test/*' => Http::response([])]);

    (new BnfexpressAdminClient($config))->deleteSuggestionEmbedding(5);

    Http::assertSent(function ($request) use ($config) {
        return $request->url() === 'https://bnfexpress.test/admin/suggestions/5/embedding'
            && $request->method() === 'DELETE'
            && assertSignedCorrectly($request, 'DELETE', '/admin/suggestions/5/embedding', '', $config['client_secret']);
    });
});
