<?php

use App\Models\FileCard;
use App\Models\GenericBackoffice\Entry;
use Illuminate\Support\Facades\DB;

/*
|--------------------------------------------------------------------------
| Search API Tests
|--------------------------------------------------------------------------
|
| Feature tests for the /api/v1/search and /api/v1/search-autocomplete endpoints
|
*/

function createSearchEntry(string $title, string $slug, ?string $password = null): int
{
    $language = getActiveLanguage();

    $entryId = DB::table('entries')->insertGetId([
        'password_protected' => $password,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    DB::table('entries_lang')->insert([
        'entry_id' => $entryId,
        'language_id' => $language,
        'title' => $title,
        'content' => $title,
    ]);

    createSearchWebsiteObject($entryId, Entry::class, 'page', $slug, $title);

    return $entryId;
}

function createSearchFileCard(int $entryId, string $title, string $slug): int
{
    $language = getActiveLanguage();

    $blockId = DB::table('blocks')->insertGetId([
        'type' => 'blockAccordion',
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    DB::table('entry_block')->insert([
        'entry_id' => $entryId,
        'block_id' => $blockId,
        'position' => 1,
    ]);

    $accordionItemId = DB::table('accordion_items')->insertGetId([
        'block_id' => $blockId,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    $fileCardId = DB::table('file_cards')->insertGetId([
        'accordion_item_id' => $accordionItemId,
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    DB::table('file_card_languages')->insert([
        'file_card_id' => $fileCardId,
        'language_id' => $language,
        'title' => $title,
        'subtitle' => '',
        'description' => $title,
    ]);

    createSearchWebsiteObject($fileCardId, FileCard::class, 'fileCards', $slug, $title);

    return $fileCardId;
}

function createSearchWebsiteObject(int $objectId, string $objectType, string $contentType, string $slug, string $title): int
{
    $language = getActiveLanguage();

    $websiteObjectId = DB::table('website_objects')->insertGetId([
        'object_id' => $objectId,
        'object_type' => $objectType,
        'content_type' => $contentType,
        'slug' => $slug,
        'uri' => '/' . $slug,
        'status' => 'published',
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    DB::table('website_objects_lang')->insert([
        'website_object_id' => $websiteObjectId,
        'wo_language_id' => $language,
        'lang_slug' => $slug,
        'lang_uri' => '/' . $slug,
    ]);

    DB::table('website_objects_search')->insert([
        'website_object_id' => $websiteObjectId,
        'language_id' => $language,
        'title' => $title,
        'search_content' => '<||>' . $title . '<||>',
    ]);

    return $websiteObjectId;
}

function withCommittedSearchFixtures(callable $callback): void
{
    DB::commit();

    try {
        $callback();
    } finally {
        $fileCardIds = DB::table('file_card_languages')
            ->whereIn('title', [
                'protectedsharedfilecard',
                'visiblesharedfilecard',
            ])
            ->pluck('file_card_id');
        $accordionItemIds = DB::table('file_cards')
            ->whereIn('id', $fileCardIds)
            ->pluck('accordion_item_id')
            ->filter();
        $blockIds = DB::table('accordion_items')
            ->whereIn('id', $accordionItemIds)
            ->pluck('block_id')
            ->filter();

        DB::table('website_objects')->whereIn('slug', [
            'protected-unique-page',
            'visible-unique-page',
            'protected-file-card-page',
            'visible-file-card-page',
            'protected-unique-file-card',
            'visible-unique-file-card',
        ])->delete();

        DB::table('entries')->whereIn('id', function ($query) {
            $query->select('entry_id')
                ->from('entries_lang')
                ->whereIn('title', [
                    'protectedsharedpage',
                    'visiblesharedpage',
                    'protectedfilecardpage',
                    'visiblefilecardpage',
                ]);
        })->delete();

        DB::table('file_cards')->whereIn('id', $fileCardIds)->delete();
        DB::table('accordion_items')->whereIn('id', $accordionItemIds)->delete();
        DB::table('blocks')->whereIn('id', $blockIds)->delete();

        DB::beginTransaction();
    }
}

it('returns a successful response for search', function () {
    $response = $this->getJson('/api/v1/search?q=test&contentType=document');

    $response->assertStatus(200);
});

it('returns search results with correct structure', function () {
    $response = $this->getJson('/api/v1/search?q=test&contentType=document');

    $response->assertStatus(200)
        ->assertJsonStructure([
            'searchParam',
            'data',
            'pagination',
        ]);
});

it('returns 422 when contentType is missing', function () {
    $response = $this->getJson('/api/v1/search?q=test');

    $response->assertStatus(422)
        ->assertJsonValidationErrors(['contentType']);
});

it('returns 422 when query is missing', function () {
    $response = $this->getJson('/api/v1/search?contentType=document');

    $response->assertStatus(422)
        ->assertJsonValidationErrors(['q']);
});

it('can search with pagination', function () {
    $response = $this->getJson('/api/v1/search?q=energy&contentType=document&perPage=5&page=1');

    $response->assertStatus(200);
});

it('can search by content type document', function () {
    $response = $this->getJson('/api/v1/search?q=energy&contentType=document');

    $response->assertStatus(200);
});

it('can search by content type press', function () {
    $response = $this->getJson('/api/v1/search?q=energy&contentType=press');

    $response->assertStatus(200);
});

it('excludes password protected pages from all search results', function () {
    withCommittedSearchFixtures(function () {
        createSearchEntry('protectedsharedpage', 'protected-unique-page', 'secret');
        createSearchEntry('visiblesharedpage', 'visible-unique-page');

        $protectedResponse = test()->getJson('/api/v1/search?q=protectedsharedpage&contentType=all');
        $visibleResponse = test()->getJson('/api/v1/search?q=visiblesharedpage&contentType=all');

        $protectedResponse->assertStatus(200)
            ->assertJsonMissing(['title' => 'protectedsharedpage']);

        $visibleResponse->assertStatus(200)
            ->assertJsonFragment(['title' => 'visiblesharedpage']);
    });
});

it('excludes file cards nested under password protected pages from document search results', function () {
    withCommittedSearchFixtures(function () {
        $protectedEntryId = createSearchEntry('protectedfilecardpage', 'protected-file-card-page', 'secret');
        $visibleEntryId = createSearchEntry('visiblefilecardpage', 'visible-file-card-page');

        createSearchFileCard($protectedEntryId, 'protectedsharedfilecard', 'protected-unique-file-card');
        createSearchFileCard($visibleEntryId, 'visiblesharedfilecard', 'visible-unique-file-card');

        $protectedResponse = test()->getJson('/api/v1/search?q=protectedsharedfilecard&contentType=document');
        $visibleResponse = test()->getJson('/api/v1/search?q=visiblesharedfilecard&contentType=document');

        $protectedResponse->assertStatus(200)
            ->assertJsonMissing(['title' => 'protectedsharedfilecard']);

        $visibleResponse->assertStatus(200)
            ->assertJsonFragment(['title' => 'visiblesharedfilecard']);
    });
});

/*
|--------------------------------------------------------------------------
| Search Autocomplete Endpoint Tests
|--------------------------------------------------------------------------
*/

it('returns a successful response for search autocomplete', function () {
    $response = $this->getJson('/api/v1/search-autocomplete?q=test');

    $response->assertStatus(200);
});

it('returns autocomplete results with correct structure', function () {
    $response = $this->getJson('/api/v1/search-autocomplete?q=test');

    $response->assertStatus(200)
        ->assertJsonStructure([
            'searchParam',
            'data',
        ]);
});

it('returns 422 when autocomplete query is missing', function () {
    $response = $this->getJson('/api/v1/search-autocomplete?q=');

    $response->assertStatus(422)
        ->assertJsonValidationErrors(['q']);
});
