<?php

namespace Tests\Feature;

use App\Jobs\GeneratePromptLandingPageJob;
use App\Models\PromptLandingPage;
use App\Models\PromptLandingPageClickout;
use App\Models\PromptLandingPageLead;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Storage;
use Laravel\Ai\StructuredAnonymousAgent;
use Tests\TestCase;

class PromptLandingPageControllerTest extends TestCase
{
    use RefreshDatabase;

    public function test_guests_are_redirected_from_the_prompt_pages_index(): void
    {
        $this->get(route('prompt-landing-pages.index'))
            ->assertRedirect(route('login'));
    }

    public function test_the_index_groups_pages_into_purpose_tabs(): void
    {
        $user = User::factory()->create();
        PromptLandingPage::factory()->approved()->for($user)->create([
            'title' => 'WebCrawlers leads',
            'purpose' => 'Leads Page',
            'website_url' => 'https://webcrawlers.com',
        ]);
        PromptLandingPage::factory()->approved()->for($user)->create([
            'title' => 'Yoga signup',
            'purpose' => 'Signup page',
            'website_url' => 'https://yoga.example.com',
        ]);
        PromptLandingPage::factory()->for($user)->create([
            'title' => 'Untitled draft',
            'purpose' => null,
            'website_url' => null,
            'status' => 'draft',
        ]);
        PromptLandingPage::factory()->approved()->create([
            'title' => 'Someone else subscription',
            'purpose' => 'Subscription page',
            'website_url' => 'https://other.example.com',
        ]);

        $this->actingAs($user);

        $this->get($this->promptListing())
            ->assertOk()
            ->assertSee('Leads Page')
            ->assertSee('Signup page')
            ->assertSee('Unset')
            ->assertDontSee('Subscription page')
            ->assertSee($this->listedPageTitle('WebCrawlers leads'), false)
            ->assertSee('webcrawlers.com')
            ->assertDontSee($this->listedPageTitle('Yoga signup'), false)
            ->assertDontSee($this->listedPageTitle('Untitled draft'), false)
            ->assertDontSee($this->listedPageTitle('Someone else subscription'), false);

        $this->get($this->promptListing(['purpose' => 'Signup page']))
            ->assertOk()
            ->assertSee($this->listedPageTitle('Yoga signup'), false)
            ->assertSee('yoga.example.com')
            ->assertDontSee($this->listedPageTitle('WebCrawlers leads'), false)
            ->assertDontSee($this->listedPageTitle('Untitled draft'), false);

        $this->get($this->promptListing(['purpose' => PromptLandingPage::UNSET_PURPOSE]))
            ->assertOk()
            ->assertSee($this->listedPageTitle('Untitled draft'), false)
            ->assertDontSee($this->listedPageTitle('WebCrawlers leads'), false)
            ->assertDontSee($this->listedPageTitle('Yoga signup'), false);
    }

    public function test_the_index_filters_the_active_tab_by_website_domain(): void
    {
        $user = User::factory()->create();
        PromptLandingPage::factory()->approved()->for($user)->create([
            'title' => 'WebCrawlers leads',
            'purpose' => 'Leads Page',
            'website_url' => 'https://webcrawlers.com',
        ]);
        PromptLandingPage::factory()->approved()->for($user)->create([
            'title' => 'Lucos leads',
            'purpose' => 'Leads Page',
            'website_url' => 'https://lucos.com',
        ]);
        PromptLandingPage::factory()->approved()->for($user)->create([
            'title' => 'Yoga signup',
            'purpose' => 'Signup page',
            'website_url' => 'https://yoga.example.com',
        ]);

        $this->actingAs($user);

        $this->get($this->promptListing(['purpose' => 'Leads Page']))
            ->assertOk()
            ->assertSee('All websites')
            ->assertSee('value="https://webcrawlers.com"', false)
            ->assertSee('value="https://lucos.com"', false)
            ->assertDontSee('value="https://yoga.example.com"', false)
            ->assertSee($this->listedPageTitle('WebCrawlers leads'), false)
            ->assertSee($this->listedPageTitle('Lucos leads'), false);

        $this->get($this->promptListing([
            'purpose' => 'Leads Page',
            'website' => 'https://webcrawlers.com',
        ]))
            ->assertOk()
            ->assertSee($this->listedPageTitle('WebCrawlers leads'), false)
            ->assertDontSee($this->listedPageTitle('Lucos leads'), false)
            ->assertDontSee($this->listedPageTitle('Yoga signup'), false);

        $this->get($this->promptListing([
            'purpose' => 'Leads Page',
            'website' => 'https://missing.example.com',
        ]))
            ->assertOk()
            ->assertSee($this->listedPageTitle('WebCrawlers leads'), false)
            ->assertSee($this->listedPageTitle('Lucos leads'), false);
    }

    public function test_authenticated_users_can_open_the_create_chat(): void
    {
        $this->actingAs(User::factory()->create());

        $this->get(route('prompt-landing-pages.create'))
            ->assertOk()
            ->assertSee('Website domain')
            ->assertSee('Purpose of this landing page')
            ->assertSee('id="pageSetupForm"', false)
            ->assertSee('id="setupWebsiteUrl"', false)
            ->assertSee('autocomplete="url" required', false)
            ->assertSee('id="setupPurpose"', false)
            ->assertSee('<select name="purpose" id="setupPurpose" required>', false)
            ->assertDontSee('<textarea name="purpose"', false)
            ->assertSee('Select purpose')
            ->assertSee('Leads Page')
            ->assertSee('App download page')
            ->assertSee('Signup page')
            ->assertDontSee('Subscription page')
            ->assertSee('Save and continue')
            ->assertDontSee('mb-msg--setup', false)
            ->assertSee('id="composer" disabled', false)
            ->assertSee('Back to listing')
            ->assertSee('New landing page')
            ->assertSee(route('landing-pages.index', ['tab' => 'prompt']), false)
            ->assertSee(route('prompt-landing-pages.create'), false)
            ->assertSee('Describe the landing page')
            ->assertDontSee('Create a new ad campaign')
            ->assertDontSee('Product URLs')
            ->assertSee('Landing Pages')
            ->assertSee('No landing pages yet')
            ->assertSeeInOrder(['AdStudio', 'Landing Pages']);
    }

    public function test_create_chat_does_not_offer_subscription_page_as_a_purpose(): void
    {
        $this->actingAs(User::factory()->create());

        $this->get(route('prompt-landing-pages.create'))
            ->assertOk()
            ->assertSee('<option value="Leads Page"', false)
            ->assertSee('<option value="App download page"', false)
            ->assertSee('<option value="Signup page"', false)
            ->assertDontSee('<option value="Subscription page"', false);
    }

    public function test_sidebar_lists_the_users_prompt_pages_separately_from_chats(): void
    {
        $user = User::factory()->create();
        $ownPage = PromptLandingPage::factory()->completed()->for($user)->create([
            'title' => 'Yoga studio landing page',
        ]);
        PromptLandingPage::factory()->completed()->create([
            'title' => 'Someone else private page',
        ]);

        $this->actingAs($user);

        $this->get(route('prompt-landing-pages.create'))
            ->assertOk()
            ->assertSee('Landing Pages')
            ->assertSee('Recent landing pages')
            ->assertSee('Yoga studio landing page')
            ->assertSee(route('prompt-landing-pages.show', $ownPage), false)
            ->assertDontSee('Someone else private page');
    }

    public function test_sidebar_marks_the_open_prompt_page_as_active(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->completed()->for($user)->create([
            'title' => 'Austin Yoga',
        ]);

        $this->actingAs($user);

        $this->get(route('prompt-landing-pages.show', $page))
            ->assertOk()
            ->assertSee('Austin Yoga')
            ->assertSee('mb-hist active', false);
    }

    public function test_send_requires_a_prompt_or_files(): void
    {
        $this->actingAs(User::factory()->create());

        $this->postJson(route('prompt-landing-pages.send'), [])
            ->assertStatus(422)
            ->assertJsonValidationErrors('message');
    }

    public function test_send_rejects_campaign_video_attachments(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->configured()->for($user)->create();

        $this->actingAs($user)
            ->postJson(route('prompt-landing-pages.send'), [
                'page' => $page->id,
                'message' => 'Make a page',
                'files' => [UploadedFile::fake()->create('clip.mp4', 100, 'video/mp4')],
            ])
            ->assertStatus(422)
            ->assertJsonValidationErrors('files.0');
    }

    public function test_setup_creates_a_draft_page_without_dispatching_generation(): void
    {
        Bus::fake();

        $this->actingAs(User::factory()->create());

        $this->postJson(route('prompt-landing-pages.setup'), [
            'website_url' => 'lucos.com',
            'purpose' => 'Leads Page',
        ])
            ->assertOk()
            ->assertJsonPath('setup_complete', true)
            ->assertJsonPath('website_url', 'https://lucos.com')
            ->assertJsonPath('purpose', 'Leads Page')
            ->assertJsonPath('status', 'draft');

        $page = PromptLandingPage::query()->first();

        $this->assertNotNull($page);
        $this->assertSame('draft', $page->status);
        $this->assertSame('https://lucos.com', $page->website_url);
        $this->assertSame('Leads Page', $page->purpose);
        $this->assertSame(0, $page->messages()->count());

        Bus::assertNotDispatched(GeneratePromptLandingPageJob::class);
    }

    public function test_setup_returns_422_for_an_invalid_website_domain(): void
    {
        $this->actingAs(User::factory()->create());

        $this->postJson(route('prompt-landing-pages.setup'), [
            'website_url' => 'not a domain',
            'purpose' => 'Leads Page',
        ])
            ->assertStatus(422)
            ->assertJsonValidationErrors('website_url')
            ->assertJsonPath(
                'errors.website_url.0',
                'Enter a website domain such as lucos.com or https://lucos.com.',
            );

        $this->assertSame(0, PromptLandingPage::query()->count());
    }

    public function test_setup_returns_422_when_fields_are_missing(): void
    {
        $this->actingAs(User::factory()->create());

        $this->postJson(route('prompt-landing-pages.setup'), [])
            ->assertStatus(422)
            ->assertJsonValidationErrors(['website_url', 'purpose'])
            ->assertJsonPath('errors.website_url.0', 'Enter a website domain such as lucos.com or https://lucos.com.')
            ->assertJsonPath('errors.purpose.0', 'Select the purpose of this landing page.');
    }

    public function test_setup_returns_422_when_purpose_is_missing(): void
    {
        $this->actingAs(User::factory()->create());

        $this->postJson(route('prompt-landing-pages.setup'), [
            'website_url' => 'lucos.com',
            'purpose' => '',
        ])
            ->assertStatus(422)
            ->assertJsonValidationErrors('purpose')
            ->assertJsonMissingValidationErrors('website_url')
            ->assertJsonPath('errors.purpose.0', 'Select the purpose of this landing page.');

        $this->assertSame(0, PromptLandingPage::query()->count());
    }

    public function test_setup_returns_422_when_purpose_is_not_an_allowed_option(): void
    {
        $this->actingAs(User::factory()->create());

        $this->postJson(route('prompt-landing-pages.setup'), [
            'website_url' => 'lucos.com',
            'purpose' => 'Subscription page',
        ])
            ->assertStatus(422)
            ->assertJsonValidationErrors('purpose')
            ->assertJsonPath('errors.purpose.0', 'Select the purpose of this landing page.');

        $this->assertSame(0, PromptLandingPage::query()->count());
    }

    public function test_returns_401_when_a_guest_saves_setup(): void
    {
        $this->postJson(route('prompt-landing-pages.setup'), [
            'website_url' => 'lucos.com',
            'purpose' => 'Leads Page',
        ])
            ->assertUnauthorized();
    }

    public function test_users_cannot_setup_another_users_page(): void
    {
        $owner = User::factory()->create();
        $other = User::factory()->create();
        $page = PromptLandingPage::factory()->configured()->for($owner)->create();

        $this->actingAs($other)
            ->postJson(route('prompt-landing-pages.setup'), [
                'page' => $page->id,
                'website_url' => 'https://other.example.com',
                'purpose' => 'Leads Page',
            ])
            ->assertNotFound();

        $page->refresh();

        $this->assertSame('https://yoga.example.com', $page->website_url);
        $this->assertSame('Leads Page', $page->purpose);
    }

    public function test_send_returns_422_when_setup_is_incomplete(): void
    {
        $this->actingAs(User::factory()->create());

        $this->postJson(route('prompt-landing-pages.send'), [
            'message' => 'Landing page for a yoga studio in Austin.',
        ])
            ->assertStatus(422)
            ->assertJsonValidationErrors('website_url')
            ->assertJsonPath(
                'errors.website_url.0',
                'Save a website domain and the landing page purpose before generating.',
            );

        $this->assertSame(0, PromptLandingPage::query()->count());
    }

    public function test_pending_approval_pages_keep_the_composer_enabled(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->pendingApproval()->for($user)->create();

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.show', $page))
            ->assertOk()
            ->assertSee('mb-msg--setup', false)
            ->assertSee("Website: https://yoga.example.com\nPurpose: Leads Page", false)
            ->assertDontSee('id="pageSetupForm"', false)
            ->assertDontSee('Save and continue')
            ->assertDontSee('id="composer" disabled', false)
            ->assertSee('id="pageApprove"', false)
            ->assertSee('id="pageReject"', false)
            ->assertSeeInOrder(['Open preview', 'Approve', 'Reject']);
    }

    public function test_approved_pages_lock_the_composer(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create();

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.show', $page))
            ->assertOk()
            ->assertSee('id="composer" disabled', false)
            ->assertSee('id="pageApprovalStatus"', false)
            ->assertSee('Approved')
            ->assertDontSee('Public page')
            ->assertDontSee('pageLeadsToggle', false)
            ->assertSee(route('prompt-landing-pages.download', $page), false);
    }

    public function test_send_dispatches_generation_after_setup(): void
    {
        Bus::fake();

        $this->actingAs(User::factory()->create());

        $setup = $this->postJson(route('prompt-landing-pages.setup'), [
            'website_url' => 'https://yoga.example.com',
            'purpose' => 'Leads Page',
        ])->assertOk();

        $pageId = $setup->json('page_id');

        $this->postJson(route('prompt-landing-pages.send'), [
            'page' => $pageId,
            'message' => 'Landing page for a yoga studio in Austin.',
        ])
            ->assertOk()
            ->assertJsonPath('status', 'queued')
            ->assertJsonPath('done', false);

        $page = PromptLandingPage::query()->find($pageId);

        $this->assertNotNull($page);
        $this->assertSame('queued', $page->status);
        $this->assertSame('Landing page for a yoga studio in Austin.', $page->messages()->first()->content);
        $this->assertCount(1, $page->messages);

        Bus::assertDispatched(
            GeneratePromptLandingPageJob::class,
            fn (GeneratePromptLandingPageJob $job) => $job->promptLandingPageId === $page->id,
        );
    }

    public function test_send_generates_one_html_page_from_a_prompt(): void
    {
        StructuredAnonymousAgent::fake([
            $this->briefPayload(),
            $this->htmlPayload(),
        ]);

        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->configured()->for($user)->create();

        $this->actingAs($user)
            ->postJson(route('prompt-landing-pages.send'), [
                'page' => $page->id,
                'message' => 'Landing page for a yoga studio in Austin.',
            ])
            ->assertOk()
            ->assertJsonPath('status', 'pending_approval')
            ->assertJsonPath('done', true)
            ->assertJsonPath('can_approve', true)
            ->assertJsonPath('download_url', null);

        $page->refresh();

        $this->assertTrue($page->isReady());
        $this->assertStringContainsString('Yoga with us', $page->html);
        $this->assertNotEmpty($page->css);
        $this->assertSame(1, PromptLandingPage::query()->count());
        $this->assertCount(2, $page->messages);
    }

    public function test_a_follow_up_message_revises_the_same_page(): void
    {
        StructuredAnonymousAgent::fake([
            $this->htmlPayload('<section><h1>Green CTA</h1></section>'),
        ]);

        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->pendingApproval()->create([
            'user_id' => $user->id,
            'brief' => $this->briefPayload(),
        ]);
        $page->messages()->create([
            'role' => 'user',
            'content' => 'Landing page for a yoga studio.',
        ]);
        $page->messages()->create([
            'role' => 'assistant',
            'content' => 'Your landing page is ready.',
        ]);

        $this->actingAs($user)
            ->postJson(route('prompt-landing-pages.send'), [
                'page' => $page->id,
                'message' => 'Make the CTA green.',
            ])
            ->assertOk()
            ->assertJsonPath('status', 'pending_approval');

        $page->refresh();

        $this->assertSame(1, PromptLandingPage::query()->count());
        $this->assertStringContainsString('Green CTA', $page->html);
        $this->assertSame('pending_approval', $page->status);
    }

    public function test_generated_html_does_not_include_attachment_storage_paths(): void
    {
        Storage::fake('local');

        StructuredAnonymousAgent::fake([
            $this->briefPayload(),
            [
                'title' => 'Yoga',
                'html' => '<section><h1>Yoga</h1><img src="prompt-landing-pages/1/client-brief-pack.txt" alt="brief"></section>',
                'css' => 'body { margin: 0; }',
                'js' => '',
            ],
        ]);

        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->configured()->for($user)->create();

        $this->actingAs($user)
            ->postJson(route('prompt-landing-pages.send'), [
                'page' => $page->id,
                'message' => 'Use this brief.',
                'files' => [UploadedFile::fake()->createWithContent('client-brief-pack.txt', 'Offer: 10 classes for $99.')],
            ])
            ->assertOk()
            ->assertJsonPath('status', 'pending_approval');

        $page = PromptLandingPage::query()->first();

        $this->assertNotNull($page);
        $this->assertStringNotContainsString('prompt-landing-pages/', $page->html);
        $this->assertStringNotContainsString('client-brief-pack.txt', $page->html);
        $this->assertSame(1, $page->attachments()->count());
        $this->assertTrue($page->attachments()->first()->included_in_brief);
        $this->assertSame(
            $page->messages()->where('role', 'user')->first()->id,
            $page->attachments()->first()->prompt_landing_page_message_id,
        );
    }

    public function test_show_renders_uploaded_filenames_with_the_user_prompt(): void
    {
        Storage::fake('local');

        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->completed()->create([
            'user_id' => $user->id,
        ]);
        $message = $page->messages()->create([
            'role' => 'user',
            'content' => 'Use this brief.',
        ]);
        $page->messages()->create([
            'role' => 'assistant',
            'content' => 'Your landing page is ready.',
        ]);
        $page->attachments()->create([
            'prompt_landing_page_message_id' => $message->id,
            'path' => 'prompt-landing-pages/'.$page->id.'/brief.txt',
            'original_name' => 'client-brief-pack.txt',
            'mime' => 'text/plain',
            'kind' => 'document',
            'included_in_brief' => true,
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.show', $page))
            ->assertSee('Use this brief.')
            ->assertSee('client-brief-pack.txt')
            ->assertSee('mb-file-card', false)
            ->assertSee('TXT')
            ->assertDontSee('prompt-landing-pages/'.$page->id.'/brief.txt', false);
    }

    public function test_show_embeds_a_pdf_preview_from_the_attachment_route(): void
    {
        Storage::fake('local');

        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->completed()->create([
            'user_id' => $user->id,
        ]);
        $message = $page->messages()->create([
            'role' => 'user',
            'content' => 'Use this brief.',
        ]);
        $attachment = $page->attachments()->create([
            'prompt_landing_page_message_id' => $message->id,
            'path' => 'prompt-landing-pages/'.$page->id.'/brief.pdf',
            'original_name' => 'client-brief-pack.pdf',
            'mime' => 'application/pdf',
            'kind' => 'document',
            'included_in_brief' => true,
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.show', $page))
            ->assertSee('mb-file-card', false)
            ->assertSee('PDF')
            ->assertSee(route('prompt-landing-pages.attachments.show', [$page, $attachment]), false)
            ->assertSee('download=1', false)
            ->assertSee('download="client-brief-pack.pdf"', false)
            ->assertDontSee('prompt-landing-pages/'.$page->id.'/brief.pdf', false);
    }

    public function test_show_keeps_line_breaks_in_the_user_prompt(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->completed()->create([
            'user_id' => $user->id,
        ]);
        $page->messages()->create([
            'role' => 'user',
            'content' => "Headline:\nCustomer Proof\nFAQ",
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.show', $page))
            ->assertSee("<div class=\"mb-msg-text\">Headline:\nCustomer Proof\nFAQ</div>", false);
    }

    public function test_show_escapes_attachment_filenames(): void
    {
        Storage::fake('local');

        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->completed()->create([
            'user_id' => $user->id,
        ]);
        $message = $page->messages()->create([
            'role' => 'user',
            'content' => 'Use this brief.',
        ]);
        $page->attachments()->create([
            'prompt_landing_page_message_id' => $message->id,
            'path' => 'prompt-landing-pages/'.$page->id.'/brief.pdf',
            'original_name' => '<script>alert(1)</script>.pdf',
            'mime' => 'application/pdf',
            'kind' => 'document',
            'included_in_brief' => true,
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.show', $page))
            ->assertDontSee('<script>alert(1)</script>', false)
            ->assertSee('&lt;script&gt;alert(1)&lt;/script&gt;.pdf', false);
    }

    public function test_owner_can_stream_an_uploaded_attachment(): void
    {
        Storage::fake('local');

        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->create([
            'user_id' => $user->id,
        ]);
        $path = 'prompt-landing-pages/'.$page->id.'/brief.pdf';
        Storage::disk('local')->put($path, '%PDF-1.4 test');
        $attachment = $page->attachments()->create([
            'path' => $path,
            'original_name' => 'brief.pdf',
            'mime' => 'application/pdf',
            'kind' => 'document',
        ]);

        $response = $this->actingAs($user)
            ->get(route('prompt-landing-pages.attachments.show', [$page, $attachment]));

        $response->assertOk();
        $this->assertSame('%PDF-1.4 test', $response->streamedContent());
        $response->assertHeader('content-type', 'application/pdf');
        $this->assertStringContainsString('inline', (string) $response->headers->get('content-disposition'));
    }

    public function test_owner_can_download_an_uploaded_attachment(): void
    {
        Storage::fake('local');

        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->create([
            'user_id' => $user->id,
        ]);
        $path = 'prompt-landing-pages/'.$page->id.'/brief.pdf';
        Storage::disk('local')->put($path, '%PDF-1.4 test');
        $attachment = $page->attachments()->create([
            'path' => $path,
            'original_name' => 'brief.pdf',
            'mime' => 'application/pdf',
            'kind' => 'document',
        ]);

        $response = $this->actingAs($user)
            ->get(route('prompt-landing-pages.attachments.show', [
                'page' => $page,
                'attachment' => $attachment,
                'download' => 1,
            ]));

        $response->assertOk();
        $this->assertSame('%PDF-1.4 test', $response->streamedContent());
        $this->assertStringContainsString('attachment', (string) $response->headers->get('content-disposition'));
        $this->assertStringContainsString('brief.pdf', (string) $response->headers->get('content-disposition'));
    }

    public function test_guests_are_redirected_from_an_attachment(): void
    {
        Storage::fake('local');

        $page = PromptLandingPage::factory()->create();
        $path = 'prompt-landing-pages/'.$page->id.'/brief.pdf';
        Storage::disk('local')->put($path, '%PDF-1.4 test');
        $attachment = $page->attachments()->create([
            'path' => $path,
            'original_name' => 'brief.pdf',
            'mime' => 'application/pdf',
            'kind' => 'document',
        ]);

        $this->get(route('prompt-landing-pages.attachments.show', [$page, $attachment]))
            ->assertRedirect(route('login'));
    }

    public function test_users_cannot_stream_another_users_attachment(): void
    {
        Storage::fake('local');

        $owner = User::factory()->create();
        $other = User::factory()->create();
        $page = PromptLandingPage::factory()->create([
            'user_id' => $owner->id,
        ]);
        $path = 'prompt-landing-pages/'.$page->id.'/brief.pdf';
        Storage::disk('local')->put($path, '%PDF-1.4 test');
        $attachment = $page->attachments()->create([
            'path' => $path,
            'original_name' => 'brief.pdf',
            'mime' => 'application/pdf',
            'kind' => 'document',
        ]);

        $this->actingAs($other)
            ->get(route('prompt-landing-pages.attachments.show', [$page, $attachment]))
            ->assertForbidden();
    }

    public function test_attachment_from_another_page_returns_404(): void
    {
        Storage::fake('local');

        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->create([
            'user_id' => $user->id,
        ]);
        $otherPage = PromptLandingPage::factory()->create([
            'user_id' => $user->id,
        ]);
        $path = 'prompt-landing-pages/'.$otherPage->id.'/brief.pdf';
        Storage::disk('local')->put($path, '%PDF-1.4 test');
        $attachment = $otherPage->attachments()->create([
            'path' => $path,
            'original_name' => 'brief.pdf',
            'mime' => 'application/pdf',
            'kind' => 'document',
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.attachments.show', [$page, $attachment]))
            ->assertNotFound();
    }

    public function test_users_cannot_view_another_users_page(): void
    {
        $owner = User::factory()->create();
        $other = User::factory()->create();
        $page = PromptLandingPage::factory()->completed()->create([
            'user_id' => $owner->id,
        ]);

        $this->actingAs($other)
            ->get(route('prompt-landing-pages.show', $page))
            ->assertForbidden();
    }

    public function test_the_show_page_toolbar_includes_new_download_and_preview(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->completed()->create([
            'user_id' => $user->id,
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.show', $page))
            ->assertOk()
            ->assertSee('Back to listing')
            ->assertSee('New landing page')
            ->assertSee('Download HTML')
            ->assertSee('Open preview')
            ->assertDontSee(route('prompt-landing-pages.editor', $page), false)
            ->assertSee(route('landing-pages.index', ['tab' => 'prompt']), false)
            ->assertSee(route('prompt-landing-pages.create'), false)
            ->assertSee(route('prompt-landing-pages.download', $page), false)
            ->assertSee(route('prompt-landing-pages.preview', $page), false);
    }

    public function test_the_show_page_links_to_the_partner_preview_when_the_page_is_approved(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create([
            'website_url' => 'https://webcrawlers.com',
            'purpose' => 'Leads Page',
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.show', $page))
            ->assertOk()
            ->assertSee('Partner link')
            ->assertSee('https://webcrawlers.com/lp/leads-page/'.$page->id, false);
    }

    public function test_the_show_page_hides_the_partner_preview_when_the_page_is_pending_approval(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->pendingApproval()->for($user)->create([
            'website_url' => 'https://webcrawlers.com',
            'purpose' => 'Leads Page',
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.show', $page))
            ->assertOk()
            ->assertDontSee('https://webcrawlers.com/lp/leads-page/'.$page->id, false);
    }

    public function test_the_show_page_links_to_the_editor_when_the_page_is_pending_approval(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->pendingApproval()->for($user)->create();

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.show', $page))
            ->assertOk()
            ->assertSee('Edit page')
            ->assertSee(route('prompt-landing-pages.editor', $page), false);
    }

    public function test_users_cannot_delete_another_users_page(): void
    {
        $owner = User::factory()->create();
        $other = User::factory()->create();
        $page = PromptLandingPage::factory()->completed()->create([
            'user_id' => $owner->id,
        ]);

        $this->actingAs($other)
            ->delete(route('prompt-landing-pages.destroy', $page))
            ->assertForbidden();

        $this->assertDatabaseHas('prompt_landing_pages', ['id' => $page->id]);
    }

    public function test_preview_and_download_are_forbidden_for_another_user(): void
    {
        $owner = User::factory()->create();
        $other = User::factory()->create();
        $page = PromptLandingPage::factory()->completed()->create([
            'user_id' => $owner->id,
        ]);

        $this->actingAs($other)
            ->get(route('prompt-landing-pages.preview', $page))
            ->assertForbidden();

        $this->actingAs($other)
            ->get(route('prompt-landing-pages.download', $page))
            ->assertForbidden();
    }

    public function test_download_returns_an_html_attachment(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->completed()->create([
            'user_id' => $user->id,
            'title' => 'Yoga studio',
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.download', $page))
            ->assertOk()
            ->assertHeader('content-disposition', 'attachment; filename="yoga-studio.html"')
            ->assertSee('<h1>Yoga studio</h1>', false)
            ->assertSee('body { margin: 0; }', false)
            ->assertSee('width=device-width, initial-scale=1', false)
            ->assertSee('max-width: 100%', false)
            ->assertSee('window.scrollTo', false);
    }

    public function test_preview_keeps_the_window_scrollable_after_in_page_cta_hashes(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->completed()->for($user)->create([
            'html' => '<a href="#consultation">Book</a><form id="consultation"><input name="email"></form>',
            'css' => 'html, body { height: 100%; overflow: hidden; }',
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.preview', $page))
            ->assertOk()
            ->assertSee('href="#consultation"', false)
            ->assertSee('id="consultation"', false)
            ->assertSee('window.scrollTo', false)
            ->assertSee('overflow-y: visible !important', false);
    }

    public function test_send_is_rejected_while_a_page_is_generating(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->configured()->create([
            'user_id' => $user->id,
            'status' => 'generating',
        ]);
        $page->messages()->create([
            'role' => 'user',
            'content' => 'Make a page',
        ]);

        $this->actingAs($user)
            ->postJson(route('prompt-landing-pages.send'), [
                'page' => $page->id,
                'message' => 'Change the headline.',
            ])
            ->assertStatus(422);
    }

    public function test_guests_cannot_approve_a_landing_page(): void
    {
        $page = PromptLandingPage::factory()->pendingApproval()->create();

        $this->postJson(route('prompt-landing-pages.approve', $page))
            ->assertUnauthorized();
    }

    public function test_owner_can_approve_a_pending_landing_page(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->pendingApproval()->for($user)->create();

        $this->actingAs($user)
            ->postJson(route('prompt-landing-pages.approve', $page))
            ->assertOk()
            ->assertJsonPath('status', 'approved')
            ->assertJsonPath('can_approve', false)
            ->assertJsonPath('approved', true)
            ->assertJsonPath('composer_locked', true)
            ->assertJsonPath('download_url', route('prompt-landing-pages.download', $page))
            ->assertJsonPath('editor_url', null);

        $page->refresh();

        $this->assertSame('approved', $page->status);
        $this->assertSame($user->id, $page->approved_by);
        $this->assertNotNull($page->approved_at);
        $this->assertNull($page->rejected_at);
    }

    public function test_owner_can_reject_a_pending_landing_page(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->pendingApproval()->for($user)->create();

        $this->actingAs($user)
            ->postJson(route('prompt-landing-pages.reject', $page), [
                'reason' => 'Headline is too vague.',
            ])
            ->assertOk()
            ->assertJsonPath('status', 'rejected')
            ->assertJsonPath('can_approve', false)
            ->assertJsonPath('rejected', true)
            ->assertJsonPath('composer_locked', false)
            ->assertJsonPath('download_url', null);

        $page->refresh();

        $this->assertSame('rejected', $page->status);
        $this->assertSame('Headline is too vague.', $page->rejection_reason);
        $this->assertNotNull($page->rejected_at);
        $this->assertNull($page->approved_at);
    }

    public function test_another_user_cannot_approve_a_landing_page(): void
    {
        $owner = User::factory()->create();
        $other = User::factory()->create();
        $page = PromptLandingPage::factory()->pendingApproval()->for($owner)->create();

        $this->actingAs($other)
            ->postJson(route('prompt-landing-pages.approve', $page))
            ->assertForbidden();

        $this->assertSame('pending_approval', $page->fresh()->status);
    }

    public function test_approve_returns_422_when_the_page_is_not_pending(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create();

        $this->actingAs($user)
            ->postJson(route('prompt-landing-pages.approve', $page))
            ->assertStatus(422)
            ->assertJsonValidationErrors('status')
            ->assertJsonPath('errors.status.0', 'This landing page is not waiting for approval.');
    }

    public function test_send_is_rejected_after_the_page_is_approved(): void
    {
        Bus::fake();

        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create();

        $this->actingAs($user)
            ->postJson(route('prompt-landing-pages.send'), [
                'page' => $page->id,
                'message' => 'Make the CTA green.',
            ])
            ->assertStatus(422)
            ->assertJsonValidationErrors('message')
            ->assertJsonPath(
                'errors.message.0',
                'This landing page is approved. Start a new landing page to generate another one.',
            );

        Bus::assertNothingDispatched();
        $this->assertSame('approved', $page->fresh()->status);
    }

    public function test_download_is_not_found_until_the_page_is_approved(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->pendingApproval()->for($user)->create();

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.preview', $page))
            ->assertOk();

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.download', $page))
            ->assertNotFound();
    }

    public function test_status_promotes_legacy_completed_pages_and_returns_the_preview(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->for($user)->create([
            'title' => 'Yoga studio',
            'website_url' => 'https://yoga.example.com',
            'purpose' => 'Leads Page',
            'status' => 'completed',
            'html' => '<section><h1>Yoga studio</h1></section>',
            'css' => 'body { margin: 0; }',
            'js' => '',
        ]);
        $page->messages()->create([
            'role' => 'assistant',
            'content' => 'Your landing page is ready.',
        ]);

        $this->actingAs($user)
            ->getJson(route('prompt-landing-pages.status', $page))
            ->assertOk()
            ->assertJsonPath('status', 'pending_approval')
            ->assertJsonPath('done', true)
            ->assertJsonPath('can_approve', true)
            ->assertJsonPath('preview_url', route('prompt-landing-pages.preview', $page))
            ->assertJsonPath('download_url', null);

        $this->assertSame('pending_approval', $page->fresh()->status);
    }

    public function test_rejected_pages_can_be_revised(): void
    {
        StructuredAnonymousAgent::fake([
            $this->htmlPayload('<section><h1>Revised</h1></section>'),
        ]);

        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->rejected()->for($user)->create([
            'brief' => $this->briefPayload(),
        ]);

        $this->actingAs($user)
            ->postJson(route('prompt-landing-pages.send'), [
                'page' => $page->id,
                'message' => 'Try a stronger headline.',
            ])
            ->assertOk()
            ->assertJsonPath('status', 'pending_approval')
            ->assertJsonPath('can_approve', true);

        $page->refresh();

        $this->assertSame('pending_approval', $page->status);
        $this->assertNull($page->rejected_at);
        $this->assertNull($page->rejection_reason);
        $this->assertStringContainsString('Revised', $page->html);
    }

    public function test_dashboard_links_to_prompt_pages(): void
    {
        $this->actingAs(User::factory()->create());

        $this->get(route('home'))
            ->assertOk()
            ->assertSee('Landing Pages')
            ->assertSee(route('landing-pages.index'), false)
            ->assertSee('Open Landing Pages')
            ->assertDontSee(route('prompt-landing-pages.index'), false)
            ->assertDontSee(route('catalog-landing-pages.index'), false);
    }

    public function test_guests_are_redirected_from_the_owner_leads_list(): void
    {
        $page = PromptLandingPage::factory()->approved()->create();

        $this->get(route('prompt-landing-pages.leads.index', $page))
            ->assertRedirect(route('login'));
    }

    public function test_the_owner_can_list_leads_for_an_approved_page(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create();
        PromptLandingPageLead::factory()->for($page, 'page')->create([
            'payload' => ['email' => 'ada@example.com', 'company' => 'Analytical Engines'],
            'query_params' => ['utm_source' => 'facebook'],
        ]);

        $this->actingAs($user)
            ->getJson(route('prompt-landing-pages.leads.index', $page))
            ->assertOk()
            ->assertJsonPath('leads.0.payload.email', 'ada@example.com')
            ->assertJsonPath('leads.0.payload.company', 'Analytical Engines')
            ->assertJsonPath('leads.0.query_params.utm_source', 'facebook')
            ->assertJsonPath('public_url', $page->publicUrl());
    }

    public function test_the_owner_can_view_the_leads_listing(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create([
            'title' => 'WebCrawlers leads',
            'website_url' => 'https://webcrawlers.com',
        ]);
        PromptLandingPageLead::factory()->for($page, 'page')->create([
            'payload' => ['email' => 'ada@example.com', 'company' => 'Analytical Engines'],
            'query_params' => ['utm_source' => 'facebook'],
        ]);
        PromptLandingPageLead::factory()->for($page, 'page')->create([
            'payload' => ['email' => 'charles@example.com', 'phone' => '555-0100'],
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.leads.index', $page))
            ->assertOk()
            ->assertSee('Leads')
            ->assertSee('WebCrawlers leads')
            ->assertSee('webcrawlers.com')
            ->assertSee('ada@example.com')
            ->assertSee('Analytical Engines')
            ->assertSee('charles@example.com')
            ->assertSee('555-0100')
            ->assertSee('Tracking')
            ->assertSee('utm_source=facebook')
            ->assertSee(route('landing-pages.index', ['tab' => 'prompt']), false);
    }

    public function test_the_leads_listing_shows_an_empty_state(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create();

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.leads.index', $page))
            ->assertOk()
            ->assertSee('No leads captured yet.');
    }

    public function test_the_index_links_to_the_leads_listing(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create([
            'title' => 'WebCrawlers leads',
            'purpose' => 'Leads Page',
            'website_url' => 'https://webcrawlers.com',
        ]);
        PromptLandingPageLead::factory()->for($page, 'page')->create([
            'payload' => ['email' => 'ada@example.com'],
        ]);

        $this->actingAs($user)
            ->get($this->promptListing())
            ->assertOk()
            ->assertSee(route('prompt-landing-pages.leads.index', $page), false)
            ->assertSee('Leads (1)', false)
            ->assertSee('aria-label="Open"', false)
            ->assertSee('aria-label="Delete"', false)
            ->assertSee('aria-label="Tracking pixels"', false)
            ->assertSee('New prompt page')
            ->assertSee('All statuses')
            ->assertSee('Search prompt pages');
    }

    public function test_the_index_shows_download_clicks_for_app_download_pages(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->appDownload()->for($user)->create();
        $visit = PromptLandingPageLead::factory()->bounce()->for($page, 'page')->create();
        PromptLandingPageClickout::factory()->forLead($visit)->count(2)->create();

        $this->actingAs($user)
            ->get(route('landing-pages.index', [
                'tab' => 'prompt',
                'purpose' => PromptLandingPage::PURPOSE_APP_DOWNLOAD,
            ]))
            ->assertOk()
            ->assertSee('Downloads (2)', false)
            ->assertSee(route('prompt-landing-pages.leads.index', ['page' => $page, 'tab' => 'downloads']), false)
            ->assertDontSee('aria-label="Leads"', false)
            ->assertDontSee('title="Leads"', false);
    }

    public function test_the_owner_can_view_download_clicks_for_an_app_download_page(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->appDownload()->for($user)->create();
        $visit = PromptLandingPageLead::factory()->bounce()->for($page, 'page')->create([
            'query_params' => ['utm_source' => 'facebook'],
        ]);
        PromptLandingPageClickout::factory()->forLead($visit)->create([
            'destination_url' => 'https://apps.apple.com/app/yoga',
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.leads.index', ['page' => $page, 'tab' => 'downloads']))
            ->assertOk()
            ->assertSee('Downloads')
            ->assertSee('https://apps.apple.com/app/yoga')
            ->assertSee('utm_source=facebook')
            ->assertSee('Clicked');

        $this->actingAs($user)
            ->getJson(route('prompt-landing-pages.leads.index', ['page' => $page, 'tab' => 'downloads']))
            ->assertOk()
            ->assertJsonPath('tab', 'downloads')
            ->assertJsonPath('clickouts.0.destination_url', 'https://apps.apple.com/app/yoga')
            ->assertJsonPath('clickouts.0.query_params.utm_source', 'facebook');
    }

    public function test_download_clicks_are_hidden_on_a_leads_page(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create();

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.leads.index', $page))
            ->assertOk()
            ->assertDontSee('tab=downloads', false);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.leads.index', ['page' => $page, 'tab' => 'downloads']))
            ->assertOk()
            ->assertSee('Leads')
            ->assertDontSee('Clicked');
    }

    public function test_the_index_links_to_the_partner_preview_for_the_entered_website(): void
    {
        $user = User::factory()->create();
        $webcrawlers = PromptLandingPage::factory()->approved()->for($user)->create([
            'title' => 'WebCrawlers leads',
            'purpose' => 'Leads Page',
            'website_url' => 'https://webcrawlers.com',
        ]);
        $attestpath = PromptLandingPage::factory()->approved()->for($user)->create([
            'title' => 'AttestPath leads',
            'purpose' => 'Leads Page',
            'website_url' => 'https://attestpath.ai',
        ]);
        $other = PromptLandingPage::factory()->approved()->for($user)->create([
            'title' => 'Newsletter capture',
            'purpose' => 'Leads Page',
            'website_url' => 'https://news.example.com',
        ]);

        $this->actingAs($user)
            ->get($this->promptListing())
            ->assertOk()
            ->assertSee('https://webcrawlers.com/lp/leads-page/'.$webcrawlers->id, false)
            ->assertSee('https://attestpath.ai/lp/leads-page/'.$attestpath->id, false)
            ->assertSee('https://news.example.com/lp/leads-page/'.$other->id, false)
            ->assertSee('aria-label="Partner link"', false);
    }

    public function test_the_index_hides_pixel_settings_when_the_page_is_not_approved(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->pendingApproval()->for($user)->create([
            'title' => 'WebCrawlers leads',
            'purpose' => 'Leads Page',
            'website_url' => 'https://webcrawlers.com',
        ]);

        $this->actingAs($user)
            ->get($this->promptListing())
            ->assertOk()
            ->assertDontSee('aria-label="Tracking pixels"', false)
            ->assertDontSee(route('prompt-landing-pages.pixels.update', $page), false);
    }

    public function test_the_owner_can_save_pixel_ids_for_an_approved_page(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create();

        $this->actingAs($user)
            ->from(route('prompt-landing-pages.index'))
            ->patch(route('prompt-landing-pages.pixels.update', $page), [
                'google_conversion_send_to' => 'AW-17926447003/fOwtCJz32vocEJu__-NC',
                'meta_pixel_id' => '2862655720757421',
            ])
            ->assertRedirect(route('prompt-landing-pages.index'))
            ->assertSessionHas('status', 'Tracking pixels saved.');

        $page->refresh();

        $this->assertSame('AW-17926447003/fOwtCJz32vocEJu__-NC', $page->google_conversion_send_to);
        $this->assertSame('2862655720757421', $page->meta_pixel_id);
    }

    public function test_pixel_ids_are_rejected_when_they_are_not_safe_to_embed(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create([
            'google_conversion_send_to' => 'AW-17926447003/fOwtCJz32vocEJu__-NC',
        ]);

        $this->actingAs($user)
            ->from(route('prompt-landing-pages.index'))
            ->patch(route('prompt-landing-pages.pixels.update', $page), [
                'google_conversion_send_to' => "AW-1';alert(1)//",
                'meta_pixel_id' => 'not-digits',
            ])
            ->assertRedirect(route('prompt-landing-pages.index'))
            ->assertSessionHasErrors(['google_conversion_send_to', 'meta_pixel_id']);

        $this->assertSame('AW-17926447003/fOwtCJz32vocEJu__-NC', $page->refresh()->google_conversion_send_to);
    }

    public function test_guests_are_redirected_from_pixel_settings(): void
    {
        $page = PromptLandingPage::factory()->approved()->create();

        $this->patch(route('prompt-landing-pages.pixels.update', $page), [
            'meta_pixel_id' => '2862655720757421',
        ])->assertRedirect(route('login'));
    }

    public function test_another_user_cannot_save_pixel_ids(): void
    {
        $page = PromptLandingPage::factory()->approved()->create();

        $this->actingAs(User::factory()->create())
            ->patch(route('prompt-landing-pages.pixels.update', $page), [
                'meta_pixel_id' => '2862655720757421',
            ])
            ->assertForbidden();
    }

    public function test_returns_404_when_saving_pixels_for_a_page_that_is_not_approved(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->pendingApproval()->for($user)->create();

        $this->actingAs($user)
            ->patch(route('prompt-landing-pages.pixels.update', $page), [
                'meta_pixel_id' => '2862655720757421',
            ])
            ->assertNotFound();
    }

    public function test_the_index_searches_pages_by_title(): void
    {
        $user = User::factory()->create();
        PromptLandingPage::factory()->approved()->for($user)->create([
            'title' => 'WebCrawlers leads',
            'purpose' => 'Leads Page',
            'website_url' => 'https://webcrawlers.com',
        ]);
        PromptLandingPage::factory()->approved()->for($user)->create([
            'title' => 'Newsletter capture',
            'purpose' => 'Leads Page',
            'website_url' => 'https://news.example.com',
        ]);

        $this->actingAs($user)
            ->get($this->promptListing(['q' => 'Newsletter']))
            ->assertOk()
            ->assertSee($this->listedPageTitle('Newsletter capture'), false)
            ->assertDontSee($this->listedPageTitle('WebCrawlers leads'), false);
    }

    public function test_the_leads_listing_hides_bounce_visits_and_shows_tracking(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create();
        PromptLandingPageLead::factory()->bounce()->for($page, 'page')->create();
        PromptLandingPageLead::factory()->for($page, 'page')->create([
            'payload' => ['email' => 'ada@example.com'],
            'query_params' => ['utm_source' => 'facebook'],
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.leads.index', $page))
            ->assertOk()
            ->assertSee('ada@example.com')
            ->assertSee('utm_source=facebook')
            ->assertDontSee('No leads captured yet.');

        $this->actingAs($user)
            ->getJson(route('prompt-landing-pages.leads.index', $page))
            ->assertOk()
            ->assertJsonCount(1, 'leads')
            ->assertJsonPath('leads.0.payload.email', 'ada@example.com');

        $this->actingAs($user)
            ->get($this->promptListing())
            ->assertOk()
            ->assertSee('Leads (1)', false);
    }

    public function test_the_page_views_tab_lists_bounce_visits_with_tracking(): void
    {
        $user = User::factory()->create();
        $page = PromptLandingPage::factory()->approved()->for($user)->create();
        PromptLandingPageLead::factory()->bounce()->for($page, 'page')->create([
            'query_params' => ['utm_source' => 'google', 'utm_campaign' => 'test'],
        ]);
        PromptLandingPageLead::factory()->for($page, 'page')->create([
            'payload' => ['email' => 'ada@example.com'],
        ]);

        $this->actingAs($user)
            ->get(route('prompt-landing-pages.leads.index', ['page' => $page, 'tab' => 'views']))
            ->assertOk()
            ->assertSee('Page views')
            ->assertSee('utm_source=google')
            ->assertSee('utm_campaign=test')
            ->assertDontSee('ada@example.com')
            ->assertDontSee('No page views yet.');

        $this->actingAs($user)
            ->getJson(route('prompt-landing-pages.leads.index', ['page' => $page, 'tab' => 'views']))
            ->assertOk()
            ->assertJsonPath('tab', 'views')
            ->assertJsonCount(1, 'leads')
            ->assertJsonPath('leads.0.query_params.utm_source', 'google');
    }

    public function test_another_user_cannot_list_leads(): void
    {
        $page = PromptLandingPage::factory()->approved()->create();
        PromptLandingPageLead::factory()->for($page, 'page')->create();

        $this->actingAs(User::factory()->create())
            ->getJson(route('prompt-landing-pages.leads.index', $page))
            ->assertForbidden();
    }

    /**
     * @param  array<string, mixed>  $query
     */
    private function promptListing(array $query = []): string
    {
        return route('landing-pages.index', array_merge(['tab' => 'prompt'], $query));
    }

    private function listedPageTitle(string $title): string
    {
        return 'mb-cell-title">'.$title;
    }

    /**
     * @return array<string, mixed>
     */
    private function briefPayload(): array
    {
        return [
            'brand_name' => 'Austin Yoga',
            'offering' => 'Studio membership',
            'headline' => 'Find your calm in Austin',
            'features' => ['Small classes'],
            'benefits' => ['Less stress'],
            'pricing' => '$99 / month',
            'audience' => 'Busy professionals',
            'usps' => ['Downtown location'],
            'pain_points' => ['Crowded gyms'],
            'tone' => 'Calm and warm',
            'cta' => 'Book a class',
            'primary_color' => '#0f766e',
            'secondary_color' => '#14b8a6',
            'accent_color' => '#0d9488',
            'background_color' => '#f7fffc',
            'text_color' => '#0f172a',
            'logo_present' => false,
            'logo_type' => 'unknown',
            'logo_wordmark_text' => '',
            'logo_description' => '',
            'notes' => 'Earth tones',
        ];
    }

    /**
     * @return array{title: string, html: string, css: string, js: string}
     */
    private function htmlPayload(string $html = '<section><h1>Yoga with us</h1></section>'): array
    {
        return [
            'title' => 'Austin Yoga',
            'html' => $html,
            'css' => 'body { margin: 0; }',
            'js' => '',
        ];
    }
}
