<?php

namespace Tests\Unit\Services\AI;

use App\Services\AI\MetaAdLibraryProvider;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

class MetaAdLibraryProviderTest extends TestCase
{
    public function test_search_without_a_country_defaults_to_the_united_states(): void
    {
        $this->fakeArchiveResponse();

        (new MetaAdLibraryProvider)->search('Nike');

        Http::assertSent(fn ($request): bool => str_contains($request->url(), 'ads_archive')
            && $request['ad_reached_countries'] === '["US"]'
            && $request['search_terms'] === 'Nike'
            && $request['media_type'] === 'IMAGE');
    }

    public function test_search_sends_video_media_type_when_requested(): void
    {
        $this->fakeArchiveResponse();

        (new MetaAdLibraryProvider)->search('Nike', 'US', null, 'VIDEO');

        Http::assertSent(fn ($request): bool => str_contains($request->url(), 'ads_archive')
            && $request['media_type'] === 'VIDEO');
    }

    public function test_search_sends_image_media_type_when_requested(): void
    {
        $this->fakeArchiveResponse();

        (new MetaAdLibraryProvider)->search('Nike', 'US', null, 'IMAGE');

        Http::assertSent(fn ($request): bool => str_contains($request->url(), 'ads_archive')
            && $request['media_type'] === 'IMAGE');
    }

    public function test_search_by_page_sends_search_page_ids_instead_of_search_terms(): void
    {
        $this->fakeArchiveResponse();

        (new MetaAdLibraryProvider)->searchByPage(['15087023444'], 'US', null, 'VIDEO');

        Http::assertSent(fn ($request): bool => str_contains($request->url(), 'ads_archive')
            && $request['search_page_ids'] === '["15087023444"]'
            && $request['media_type'] === 'VIDEO'
            && $request['ad_reached_countries'] === '["US"]'
            && ! isset($request['search_terms']));
    }

    public function test_search_by_page_caps_page_ids_at_ten(): void
    {
        $this->fakeArchiveResponse();

        $pageIds = array_map(fn (int $id): string => (string) $id, range(1, 12));

        (new MetaAdLibraryProvider)->searchByPage($pageIds);

        Http::assertSent(function ($request): bool {
            $sent = json_decode((string) $request['search_page_ids'], true);

            return str_contains($request->url(), 'ads_archive')
                && is_array($sent)
                && count($sent) === 10;
        });
    }

    public function test_search_by_page_returns_no_ads_when_no_numeric_ids_are_given(): void
    {
        Http::preventStrayRequests();

        $results = (new MetaAdLibraryProvider)->searchByPage(['nike', '']);

        $this->assertSame([], $results->all());
    }

    public function test_search_uses_an_explicit_country_code(): void
    {
        $this->fakeArchiveResponse();

        (new MetaAdLibraryProvider)->search('Nike', 'IN');

        Http::assertSent(fn ($request): bool => str_contains($request->url(), 'ads_archive')
            && $request['ad_reached_countries'] === '["IN"]');
    }

    public function test_search_uses_the_country_from_the_request_when_one_is_supplied(): void
    {
        $this->fakeArchiveResponse();

        request()->merge(['country' => 'GB']);

        (new MetaAdLibraryProvider)->search('Nike');

        Http::assertSent(fn ($request): bool => str_contains($request->url(), 'ads_archive')
            && $request['ad_reached_countries'] === '["GB"]');
    }

    public function test_search_returns_ads_newest_first_by_delivery_start_time(): void
    {
        $this->fakeArchiveResponse([
            $this->archiveAd('old', '2024-01-01T00:00:00+0000'),
            $this->archiveAd('new', '2026-09-01T00:00:00+0000'),
            $this->archiveAd('mid', '2025-06-01T00:00:00+0000'),
        ]);

        $results = (new MetaAdLibraryProvider)->search('Nike');

        $this->assertSame(['new', 'mid', 'old'], $results->pluck('externalAdId')->all());
    }

    public function test_pagination_forwards_the_after_cursor_with_the_selected_country(): void
    {
        $this->fakeArchiveResponse();

        (new MetaAdLibraryProvider)->search('Nike', 'CA', 'CURSOR123');

        Http::assertSent(fn ($request): bool => str_contains($request->url(), 'ads_archive')
            && $request['ad_reached_countries'] === '["CA"]'
            && $request['after'] === 'CURSOR123'
            && $request['search_terms'] === 'Nike');
    }

    public function test_pagination_from_the_request_keeps_country_and_newest_first_ordering(): void
    {
        request()->merge([
            'country' => 'AU',
            'after' => 'PAGE2',
        ]);

        $this->fakeArchiveResponse([
            $this->archiveAd('older', '2025-01-01T00:00:00+0000'),
            $this->archiveAd('newer', '2026-01-01T00:00:00+0000'),
        ]);

        $results = (new MetaAdLibraryProvider)->search('Nike');

        Http::assertSent(fn ($request): bool => str_contains($request->url(), 'ads_archive')
            && $request['ad_reached_countries'] === '["AU"]'
            && $request['after'] === 'PAGE2');

        $this->assertSame(['newer', 'older'], $results->pluck('externalAdId')->all());
    }

    public function test_search_uses_the_page_picture_as_the_thumbnail_url(): void
    {
        $this->fakeArchiveResponse([
            $this->archiveAd('987', '2026-01-01T00:00:00+0000'),
        ]);

        $ad = (new MetaAdLibraryProvider)->search('Nike')->first();

        $this->assertSame('987', $ad->externalAdId);
        $this->assertSame('https://graph.facebook.com/v26.0/123/picture?type=large', $ad->thumbnailUrl);
        $this->assertSame('987', $ad->snapshot['id']);
        $this->assertSame(
            'https://www.facebook.com/ads/archive/render_ad/?id=987',
            $ad->snapshot['ad_snapshot_url'],
        );
    }

    /**
     * @param  list<array<string, mixed>>  $ads
     */
    private function fakeArchiveResponse(array $ads = []): void
    {
        config(['services.meta.access_token' => 'test-token']);

        Http::preventStrayRequests();
        Http::fake([
            'https://graph.facebook.com/v26.0/ads_archive*' => Http::response([
                'data' => $ads !== [] ? $ads : [
                    $this->archiveAd('1', '2026-01-01T00:00:00+0000'),
                ],
            ]),
        ]);
    }

    /**
     * @return array<string, mixed>
     */
    private function archiveAd(string $id, string $startedAt): array
    {
        return [
            'id' => $id,
            'page_id' => '123',
            'page_name' => 'Acme',
            'ad_creative_bodies' => ['Copy'],
            'ad_creative_link_titles' => ['Title'],
            'ad_creative_link_captions' => ['Caption'],
            'ad_snapshot_url' => 'https://www.facebook.com/ads/archive/render_ad/?id='.$id,
            'publisher_platforms' => ['facebook'],
            'ad_delivery_start_time' => $startedAt,
        ];
    }
}
