<?php

namespace Tests\Feature;

use App\Agent\ToolRegistry;
use App\Agent\Workspace;
use App\Models\Campaign;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Ai\Tools\Request;
use Tests\TestCase;

/**
 * A large daily budget is repeated back before it is set.
 *
 * The ceiling on its own is a cliff: everything under it is accepted in silence
 * and the first thing over it is refused outright. That ceiling was also 20,
 * which is a development guard rather than a product limit - Mohsin asked for
 * 50 and was refused, Koushik hit it repeatedly, and neither was doing anything
 * unreasonable.
 *
 * So the ceiling is 500 and there is a band beneath it where the amount is read
 * back and has to be confirmed. A budget is the one field where a mistyped zero
 * spends somebody else's money.
 */
class ALargeBudgetIsReadBackBeforeItIsSetTest extends TestCase
{
    use RefreshDatabase;

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

        config([
            'platforms.guardrails.max_daily_budget' => 500,
            'platforms.guardrails.confirm_daily_budget_above' => 50,
        ]);

        $this->actingAs(User::factory()->create());
        app(Workspace::class)->bindTo('conv-big-budget');
    }

    private function campaign(): Campaign
    {
        $campaign = Campaign::create([
            'user_id' => auth()->id(),
            'name' => 'Meta campaign',
            'platform' => 'meta',
            'status' => 'draft',
            'currency' => 'USD',
            'budget_mode' => 'daily',
        ]);

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

        return $campaign;
    }

    private function setBudget(array $arguments): array
    {
        $result = collect(app(ToolRegistry::class)->resolve())
            ->first(fn ($t) => $t->name() === 'campaign__set_campaign_budget')
            ->handle(new Request($arguments));

        return json_decode((string) $result, true);
    }

    /** Under the threshold, nothing changes: it is set without ceremony. */
    public function test_a_small_amount_is_set_without_confirmation(): void
    {
        $campaign = $this->campaign();

        $result = $this->setBudget(['amount' => 20]);

        $this->assertTrue($result['ok'], $result['error'] ?? '');
        $this->assertSame('20.00', (string) $campaign->refresh()->budget);
    }

    /** Over it, the amount is refused until it has been read back. */
    public function test_a_large_amount_is_held_for_confirmation(): void
    {
        $campaign = $this->campaign();

        $result = $this->setBudget(['amount' => 200]);

        $this->assertFalse($result['ok']);
        $this->assertTrue($result['needs_confirmation']);
        $this->assertStringContainsString('200.00', $result['error']);
        $this->assertNull($campaign->refresh()->budget, 'the budget was set before it was confirmed');
    }

    /** And confirming sets it. */
    public function test_confirming_sets_the_large_amount(): void
    {
        $campaign = $this->campaign();

        $result = $this->setBudget(['amount' => 200, 'confirmed' => true]);

        $this->assertTrue($result['ok'], $result['error'] ?? '');
        $this->assertSame('200.00', (string) $campaign->refresh()->budget);
    }

    /**
     * Above the ceiling is still refused, confirmed or not.
     *
     * The confirmation band sits beneath the ceiling; it does not become a way
     * through it.
     */
    public function test_confirmation_does_not_open_the_ceiling(): void
    {
        $campaign = $this->campaign();

        $result = $this->setBudget(['amount' => 900, 'confirmed' => true]);

        $this->assertFalse($result['ok']);
        $this->assertStringContainsString('ceiling', $result['error']);
        $this->assertNull($campaign->refresh()->budget);
    }

    /** 50 was refused outright before; now it is simply set. */
    public function test_fifty_a_day_is_no_longer_refused(): void
    {
        $campaign = $this->campaign();

        $result = $this->setBudget(['amount' => 50]);

        $this->assertTrue($result['ok'], $result['error'] ?? '');
        $this->assertSame('50.00', (string) $campaign->refresh()->budget);
    }
}
