<?php

namespace Tests\Feature;

use App\Campaigns\LaunchFlow;
use App\Campaigns\Platforms\Platforms;
use App\Campaigns\Platforms\UnsupportedPlatformRules;
use App\Campaigns\PublishGate;
use App\Models\Campaign;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

/**
 * A platform that needs a Page is asked for one.
 *
 * The flow decided this with $campaign->platform === 'meta' while PublishGate
 * decided it with requiresPage(). Both give the same answer today, which is
 * what made the hardcode survive: nothing disagrees until a second platform
 * needs a Page.
 *
 * When one does, the gate blocks on a missing Page and the flow never asks for
 * one, so the campaign cannot publish and there is no step to clear. LinkedIn
 * is the live candidate, since every sponsored post is authored by an
 * organization. This asserts the two now read the same source.
 */
class PageStepFollowsThePlatformRuleTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();

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

        config(['platforms.rules.pagehungry' => PageHungryRules::class]);
    }

    private function campaignOn(string $platform): Campaign
    {
        return Campaign::create([
            'user_id' => auth()->id(),
            'name' => 'Winter Sale',
            'platform' => $platform,
            'status' => 'draft',
            'objective' => 'OUTCOME_TRAFFIC',
            'ad_account_id' => 'act_777',
        ]);
    }

    /** @return list<string> */
    private function stepKeys(Campaign $campaign): array
    {
        return array_column(app(LaunchFlow::class)->progress($campaign), 'key');
    }

    /**
     * The test that the hardcode fails: a platform that is not Meta, that says
     * it needs a Page, is asked for one.
     */
    public function test_a_non_meta_platform_that_needs_a_page_is_asked_for_one(): void
    {
        $campaign = $this->campaignOn('pagehungry');

        $this->assertTrue(
            app(Platforms::class)->for($campaign)->requiresPage(),
            'Precondition: the test platform should require a Page.',
        );

        $this->assertContains(
            'page',
            $this->stepKeys($campaign),
            'The platform requires a Page and the flow never asks for one.',
        );
    }

    /** And the gate that blocks on it agrees, which is the whole point. */
    public function test_the_gate_and_the_flow_agree_about_the_page(): void
    {
        $campaign = $this->campaignOn('pagehungry');

        $blocking = implode(' ', app(PublishGate::class)->check($campaign));

        $this->assertStringContainsString('Page not selected', $blocking);
        $this->assertContains('page', $this->stepKeys($campaign), 'The gate blocks on a step nothing offers.');
    }

    /** Meta still asks, because it still requires one. */
    public function test_meta_is_still_asked_for_a_page(): void
    {
        $this->assertContains('page', $this->stepKeys($this->campaignOn('meta')));
    }

    /** Google still is not, because it does not need one. */
    public function test_google_is_not_asked_for_a_page(): void
    {
        $this->assertNotContains('page', $this->stepKeys($this->campaignOn('google')));
    }
}

/**
 * A platform that needs a Page and is not Meta.
 *
 * Declared rather than mocked because Platforms::for() resolves a class name
 * out of config, which is also how a real fourth platform would arrive.
 */
class PageHungryRules extends UnsupportedPlatformRules
{
    public function key(): string
    {
        return 'pagehungry';
    }

    public function name(): string
    {
        return 'Page Hungry';
    }

    public function requiresPage(): bool
    {
        return true;
    }

    /** Abstract on the base, and irrelevant to what is being asserted. */
    public function minimumDailyBudget(): float
    {
        return 1.0;
    }

    /** @return list<array{key: string, label: string, tool: string, offers: bool, ask?: string}> */
    public function steps(): array
    {
        return [
            ['key' => 'page', 'label' => 'Page', 'tool' => 'choose_page', 'offers' => true,
                'ask' => 'Which Page should these ads come from?'],
        ];
    }
}
