<?php

namespace Tests\Feature;

use App\Services\LandingPageAnalyzer;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

/**
 * Keywords read from a page are bid targets, not a suggestion.
 *
 * The extractor counted any word of five characters or more and took the top
 * ten, and its own docblock called that "a prompt for the user, not a keyword
 * strategy". That stopped being true when GooglePublisher started sending them
 * to Google as PHRASE match keywords: nobody sees them in between, and they buy
 * traffic.
 *
 * iblockads.org produced "about", "works", "experience", "online" and
 * "smoother". A Search campaign phrase-matching those would have spent its
 * budget on almost anything, and it passed the gate because the gate only asks
 * whether there are any keywords at all.
 */
class KeywordsAreWorthBiddingOnTest extends TestCase
{
    private function page(string $body, string $title = 'iBlockAds AdBlocker'): void
    {
        Http::fake(['*' => Http::response(
            '<html><head><title>'.$title.'</title></head><body>'.$body.'</body></html>',
        )]);
    }

    /** @return list<string> */
    private function keywords(): array
    {
        return app(LandingPageAnalyzer::class)->analyze('https://iblockads.org/')->keywords;
    }

    public function test_function_words_are_not_bid_on(): void
    {
        $this->page(str_repeat(
            '<p>About what works through their experience, because these things should '
            .'always matter without any adblocker.</p>',
            4,
        ));

        foreach (['about', 'works', 'through', 'their', 'because', 'these', 'things', 'always', 'without'] as $word) {
            $this->assertNotContains($word, $this->keywords(), "[{$word}] is not worth bidding on.");
        }
    }

    /** The furniture every site has says nothing about what it sells. */
    public function test_page_furniture_is_not_bid_on(): void
    {
        $this->page(str_repeat(
            '<p>Privacy policy, terms, cookie settings, contact us, welcome to our website. '
            .'Adblocker extension for browsers.</p>',
            4,
        ));

        foreach (['privacy', 'policy', 'terms', 'cookie', 'contact', 'welcome', 'website'] as $word) {
            $this->assertNotContains($word, $this->keywords(), "[{$word}] is on every page ever made.");
        }
    }

    /**
     * The title leads, because that is where a page says what it sells.
     *
     * "adblocker" appears once on iblockads.org, in the title, and is the most
     * valuable thing on the page to bid on. Ranked on frequency it came out
     * below "smoother".
     */
    public function test_the_title_leads_even_when_the_body_repeats_other_words(): void
    {
        $this->page(str_repeat('<p>Smoother browsing experience online every day.</p>', 8));

        $keywords = $this->keywords();

        $this->assertContains('adblocker', $keywords);
        $this->assertContains('iblockads', $keywords);
        $this->assertLessThan(
            array_search('smoother', $keywords, true) === false ? PHP_INT_MAX : array_search('smoother', $keywords, true),
            array_search('adblocker', $keywords, true),
            'A word repeated in the body outranked the product name in the title.',
        );
    }

    /**
     * Inline markup does not fuse two words into one.
     *
     * <h1>Block<span>Annoying Ads</span></h1> read back as "BlockAnnoying Ads"
     * because textContent joins descendants with nothing between them, and
     * "blockannoying" was sent to Google as a phrase match. Nobody searches for
     * that.
     */
    public function test_words_split_by_markup_are_not_fused(): void
    {
        $this->page('<h1>Block<span>Annoying Ads</span></h1>'.str_repeat('<p>Adblocker extension.</p>', 3));

        $keywords = $this->keywords();

        $this->assertNotContains('blockannoying', $keywords);
        $this->assertContains('block', $keywords);
        $this->assertContains('annoying', $keywords);
    }

    /**
     * A page with little to say still yields something.
     *
     * Requiring repetition is right until it leaves nothing: an empty list
     * blocks the publish gate, so a short page falls back to what it has.
     */
    public function test_a_sparse_page_still_produces_keywords(): void
    {
        $this->page('<p>Adblocker extension blocks trackers.</p>');

        $this->assertNotEmpty($this->keywords(), 'A sparse page cannot be advertised at all.');
    }

    /** Numbers are not keywords. */
    public function test_numbers_are_dropped(): void
    {
        $this->page(str_repeat('<p>12345 67890 adblocker extension browsers.</p>', 4));

        foreach ($this->keywords() as $keyword) {
            $this->assertFalse(ctype_digit($keyword), "[{$keyword}] is a number.");
        }
    }
}
