<?php

namespace Tests\Feature;

use App\Mcp\Tools\GoogleAds\SelectGoogleImageAsset;
use App\Models\Asset;
use App\Models\Creative;
use App\Models\GoogleAdsConnection;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
use Laravel\Mcp\Request;
use Laravel\Mcp\Response;
use Tests\TestCase;

/**
 * An image that is no longer on disk is explained, not thrown.
 *
 * Storage::get() returns null for a file that is not there and
 * createImageAsset() takes a non-nullable string, so a missing file became a
 * TypeError. It reached a buyer verbatim on 22 Sep as "createImageAsset():
 * Argument #2 ($contents) must be of type string, null given, called in
 * /var/www/php85/manoj/arb.admedia.com/app/Mcp/Tools/GoogleAds/...", which says
 * nothing they can act on and prints the server's filesystem layout into the
 * chat.
 *
 * Found by reading every failure recorded in the arb-dev chats rather than by
 * hitting it. It is the third instance of one shape: GraphApi::multipart() and
 * LinkedInRestApi::uploadImage() had the same hole, and GooglePublisher and
 * AssetLibrary already guarded against it on this very asset.
 */
class AMissingImageIsNotATypeErrorTest extends TestCase
{
    use RefreshDatabase;

    private function creativeWithNoFileOnDisk(User $user): Creative
    {
        $asset = Asset::create([
            'owner_type' => $user->getMorphClass(),
            'owner_id' => $user->getKey(),
            'kind' => 'image',
            'source' => 'upload',
            'disk' => 'local',
            'path' => 'creatives/images/deleted-since.jpg',
            'original_name' => 'hero.jpg',
            'mime' => 'image/jpeg',
            'bytes' => 1024,
            'checksum' => str_repeat('a', 64),
        ]);

        return Creative::create([
            'user_id' => $user->getKey(),
            'type' => 'image',
            'status' => 'completed',
            'source' => 'upload',
            // Matched on the stored ratio, so nothing has to read the file to
            // decide the creative is eligible. That is what lets the run reach
            // the upload with the bytes still missing.
            'ratio' => '1:1',
            'title' => 'Square hero',
            'asset_id' => $asset->id,
        ]);
    }

    private function select(int $creativeId): Response
    {
        return app(SelectGoogleImageAsset::class)->handle(new Request([
            'creative_id' => $creativeId,
            'image_field' => 'square_marketing_image_assets',
        ]));
    }

    public function test_a_missing_image_is_refused_with_something_the_user_can_act_on(): void
    {
        Storage::fake('local');

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

        GoogleAdsConnection::create([
            'user_id' => $user->getKey(),
            'customer_id' => '3460855874',
            'refresh_token' => 'a-refresh-token',
        ]);

        $creative = $this->creativeWithNoFileOnDisk($user);

        $response = $this->select($creative->id);

        $this->assertTrue($response->isError(), 'a missing image was not reported as a failure at all');

        $text = (string) $response->content();

        $this->assertStringContainsString('no longer on disk', $text);

        // The two things the old failure did that this must not: name a server
        // path, and mention the function that blew up.
        $this->assertStringNotContainsString('/var/www', $text);
        $this->assertStringNotContainsString('createImageAsset', $text);
    }

    /** And an image that is present is not refused by the new guard. */
    public function test_an_image_that_is_on_disk_gets_past_the_guard(): void
    {
        Storage::fake('local');

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

        GoogleAdsConnection::create([
            'user_id' => $user->getKey(),
            'customer_id' => '3460855874',
            'refresh_token' => 'a-refresh-token',
        ]);

        $creative = $this->creativeWithNoFileOnDisk($user);
        Storage::disk('local')->put($creative->asset->path, 'not-really-a-jpeg');

        // It still fails, because nothing is faking Google here, but it must
        // fail past the guard rather than at it.
        try {
            $text = (string) $this->select($creative->id)->content();
        } catch (\Throwable $e) {
            $text = $e->getMessage();
        }

        $this->assertStringNotContainsString('no longer on disk', $text);
    }
}
