<?php

namespace Tests\Feature;

use App\Agent\Tools\Campaign\AnalyzeLandingPage;
use App\Models\User;
use App\Services\LandingPageAnalyzer;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Ai\Tools\Request;
use Mockery;
use RuntimeException;
use Tests\TestCase;

/**
 * A page we cannot read does not become a question asked over and over.
 *
 * From dev on 23 Sep, the whole of a chat before the user gave up:
 *
 *   I want to promote https://iblockads.com/
 *   > The landing page could not be reached. Please check the URL...
 *   home page only
 *   > The landing page could not be reached. Please check the URL...
 *   iblockads.com
 *   > The landing page could not be reached. Please check the URL...
 *   are you dumb, i have already answered 3 times now
 *
 * The domain really was unreachable, so refusing to invent a summary of it was
 * right. What was wrong is that the tool handed back a reason and nothing else,
 * leaving the agent with no move except to ask for the URL again, which it did
 * verbatim each time while the user answered in three different ways.
 *
 * Being unable to read a page is not evidence the user got it wrong. Geo-blocks,
 * WAFs and user-agent filters all produce this, and in each case the URL is
 * correct and the campaign can proceed without our suggestions. So the result
 * now carries the way out as well as the reason.
 */
class UnreadablePageDoesNotLoopTest extends TestCase
{
    use RefreshDatabase;

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

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

    private function refusing(string $because): void
    {
        $analyzer = Mockery::mock(LandingPageAnalyzer::class);
        $analyzer->shouldReceive('analyze')->andThrow(new RuntimeException($because));

        $this->app->instance(LandingPageAnalyzer::class, $analyzer);
    }

    public function test_the_failure_tells_the_agent_not_to_ask_for_the_url_again(): void
    {
        $this->refusing('The landing page could not be reached.');

        $result = json_decode((string) app(AnalyzeLandingPage::class)
            ->handle(new Request(['url' => 'https://iblockads.com/'])), true);

        $this->assertFalse($result['ok']);

        $this->assertStringContainsString(
            'Do not ask for the URL again',
            $result['next'],
            'Nothing stops the agent re-asking a question the user has already answered.',
        );

        $this->assertStringContainsString(
            'carry on with it as it is',
            $result['next'],
            'The user is left with no way forward but to keep retyping the URL.',
        );
    }

    /**
     * The reason survives alongside the instruction.
     *
     * A private address and a 404 need different things from the user, and
     * flattening both into "could not be read" was never the intent here.
     */
    public function test_the_reason_is_still_reported(): void
    {
        $this->refusing('That address is on a private network.');

        $result = json_decode((string) app(AnalyzeLandingPage::class)
            ->handle(new Request(['url' => 'http://127.0.0.1/'])), true);

        $this->assertSame('That address is on a private network.', $result['error']);
    }

    /**
     * The URL comes back so the agent can offer it verbatim.
     *
     * Without it the agent has to reconstruct what the user typed from the
     * transcript, which is exactly where "iblockads.com" turns into
     * "https://iblockads.com/" and the user is asked to confirm a third spelling
     * of the thing they have already said twice.
     */
    public function test_the_url_is_returned_as_the_user_typed_it(): void
    {
        $this->refusing('The landing page could not be reached.');

        $result = json_decode((string) app(AnalyzeLandingPage::class)
            ->handle(new Request(['url' => '  iblockads.com  '])), true);

        $this->assertSame('iblockads.com', $result['url_as_given']);
    }
}
