<?php

namespace Tests\Feature;

use App\Agent\Tools\Campaign\SplitBudget;
use App\Agent\Workspace;
use App\Campaigns\Platforms\Platforms;
use App\Campaigns\Platforms\UnsupportedPlatformRules;
use App\Models\Brief;
use App\Models\Campaign;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Ai\Tools\Request;
use Tests\TestCase;

/**
 * A minimum spend must be the platform's own rule, not one we invented.
 *
 * Google was given Meta's 5.00 floor because Meta was once the only platform,
 * and then it was stated as Google's: "Google Ads needs at least 5.00 a day".
 * Google makes no such rule. A buyer cannot argue with a platform requirement,
 * which is what made inventing one worse than having none at all.
 */
class PlatformFloorsAreRealTest extends TestCase
{
    use RefreshDatabase;

    private User $user;

    private Brief $brief;

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

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

        $this->brief = Brief::create([
            'user_id' => $this->user->id,
            'conversation_id' => 'conv-floors',
            'budget' => 6,
            'budget_mode' => 'daily',
            'currency' => 'USD',
        ]);
    }

    private function campaign(string $platform): Campaign
    {
        return Campaign::create([
            'user_id' => $this->user->id,
            'brief_id' => $this->brief->id,
            'name' => ucfirst($platform),
            'platform' => $platform,
            'status' => 'draft',
        ]);
    }

    private function split(array $shares): array
    {
        $meta = $this->campaign('meta');
        $this->campaign('google');

        app(Workspace::class)->focusOn($meta);

        return json_decode((string) app(SplitBudget::class)->handle(new Request(['shares' => $shares])), true);
    }

    /** Google's real rule is that there is no daily minimum. */
    public function test_google_declares_no_daily_minimum(): void
    {
        $this->assertSame(0.0, app(Platforms::class)->for('google')->minimumDailyBudget());
    }

    /** Meta's floor is Meta's own, and stays. */
    public function test_meta_still_declares_its_own_floor(): void
    {
        $this->assertSame(5.0, app(Platforms::class)->for('meta')->minimumDailyBudget());
    }

    /**
     * Every platform states its own, and none of them share a number by accident.
     *
     * They all returned 5.00 before, inherited from UnsupportedPlatformRules,
     * which is Meta's floor wearing four other platforms' names. TikTok and
     * LinkedIn genuinely refuse below theirs, so the figures matter as soon as
     * either gets a publisher.
     */
    public function test_each_platform_declares_its_own_floor(): void
    {
        $platforms = app(Platforms::class);

        $this->assertSame(0.0, $platforms->for('google')->minimumDailyBudget());
        $this->assertSame(5.0, $platforms->for('meta')->minimumDailyBudget());
        $this->assertSame(20.0, $platforms->for('tiktok')->minimumDailyBudget());
        $this->assertSame(10.0, $platforms->for('taboola')->minimumDailyBudget());
        $this->assertSame(10.0, $platforms->for('linkedin')->minimumDailyBudget());
    }

    /**
     * No platform may inherit a floor it did not state.
     *
     * The base class no longer defines one, so this is enforced by PHP rather
     * than by remembering. If a new platform class resolves at all, it declared
     * its own figure.
     */
    public function test_the_shared_default_floor_no_longer_exists(): void
    {
        $this->assertFalse(
            method_exists(UnsupportedPlatformRules::class, 'minimumDailyBudget')
                && (new \ReflectionMethod(UnsupportedPlatformRules::class, 'minimumDailyBudget'))
                    ->getDeclaringClass()->getName() === UnsupportedPlatformRules::class,
            'UnsupportedPlatformRules defines a shared minimum again, so a new platform can inherit a floor it never stated.',
        );
    }

    /**
     * A share below 5.00 is fine on Google and not on Meta.
     *
     * The whole point: the refusal now names the platform that actually refuses,
     * and says nothing about the one that does not.
     */
    public function test_only_the_platform_that_really_refuses_is_named(): void
    {
        $result = $this->split([
            ['platform' => 'meta', 'amount' => 2.50],
            ['platform' => 'google', 'amount' => 3.50],
        ]);

        $this->assertFalse($result['ok']);
        $this->assertStringContainsString('Meta', $result['error']);
        $this->assertStringNotContainsString('Google Ads needs at least', $result['error']);
    }

    /** And a small Google share alongside a funded Meta one is allowed. */
    public function test_a_small_google_share_is_accepted(): void
    {
        $result = $this->split([
            ['platform' => 'meta', 'amount' => 5.00],
            ['platform' => 'google', 'amount' => 1.00],
        ]);

        $this->assertTrue($result['ok'], $result['error'] ?? '');
    }

    /**
     * Zero is still refused, which the invented floor used to do by accident.
     *
     * Not running on a platform is an answer, but it means removing the
     * campaign rather than funding it with nothing.
     */
    public function test_a_share_of_zero_is_refused(): void
    {
        $result = $this->split([
            ['platform' => 'meta', 'amount' => 6.00],
            ['platform' => 'google', 'amount' => 0],
        ]);

        $this->assertFalse($result['ok']);
        $this->assertStringContainsString('drop the platform', $result['error']);
    }

    /** As is a negative one. */
    public function test_a_negative_share_is_refused(): void
    {
        $result = $this->split([
            ['platform' => 'meta', 'amount' => 8.00],
            ['platform' => 'google', 'amount' => -2.00],
        ]);

        $this->assertFalse($result['ok']);
        $this->assertStringContainsString('cannot have a share', $result['error']);
    }
}
