<?php

namespace Tests\Feature;

use App\Agent\MarketingAgent;
use App\Agent\Workspace;
use App\Models\Asset;
use App\Models\Campaign;
use App\Models\ConversationArtifact;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

/**
 * The chat surface.
 *
 * These exercise the HTTP layer, not the model: what matters here is that a
 * message reaches the agent, an attachment becomes an asset linked to the
 * conversation, and publishing is refused unless the gate allows it.
 */
class ChatControllerTest extends TestCase
{
    use RefreshDatabase;

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

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

        // The agent must never reach a real provider from a test. Without this
        // every send() would spend tokens and take seconds, and the suite would
        // fail whenever the provider was busy rather than when the code broke.
        MarketingAgent::fake(['Noted.']);
    }

    public function test_the_chat_page_loads_with_an_empty_panel(): void
    {
        $this->get(route('chat.show'))
            ->assertOk()
            ->assertSee('Campaign chat')
            ->assertSee('No campaign yet', escape: false);
    }

    public function test_the_chat_requires_authentication(): void
    {
        auth()->logout();

        $this->get(route('chat.show'))->assertRedirect(route('login'));
        $this->post(route('chat.send'), ['message' => 'hello'])->assertRedirect(route('login'));
    }

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

        $workspace = app(Workspace::class);
        $workspace->bindTo('conv-chat-0001');

        $this->post(route('chat.send'), [
            'conversation' => 'conv-chat-0001',
            'message' => '',
            'files' => [UploadedFile::fake()->image('creative.png', 600, 600)],
        ]);

        $asset = Asset::first();

        $this->assertNotNull($asset, 'The upload should have become an asset.');
        $this->assertSame('upload', $asset->source);
        $this->assertSame('creative.png', $asset->original_name);
        $this->assertNotNull($asset->checksum);
        Storage::disk('local')->assertExists($asset->path);

        $this->assertSame(1, ConversationArtifact::where('conversation_id', 'conv-chat-0001')->count());
    }

    public function test_publishing_is_refused_while_the_campaign_is_incomplete(): void
    {
        $workspace = app(Workspace::class);
        $workspace->bindTo('conv-chat-0002');

        $campaign = Campaign::create([
            'user_id' => auth()->id(),
            'name' => 'Half built',
            'platform' => 'meta',
            'status' => 'draft',
        ]);
        $workspace->produced($campaign);

        $this->postJson(route('chat.publish'), ['conversation' => 'conv-chat-0002'])
            ->assertStatus(422)
            ->assertJsonPath('ok', false)
            ->assertJsonFragment(['error' => 'The campaign is not ready to publish.']);

        $this->assertSame('draft', $campaign->fresh()->status);
    }

    public function test_publishing_without_a_campaign_is_refused(): void
    {
        app(Workspace::class)->bindTo('conv-chat-0003');

        $this->postJson(route('chat.publish'), ['conversation' => 'conv-chat-0003'])
            ->assertStatus(422)
            ->assertJsonPath('ok', false);
    }

    public function test_oversized_and_unsupported_uploads_are_rejected(): void
    {
        $this->postJson(route('chat.send'), [
            'message' => 'here you go',
            'files' => [UploadedFile::fake()->create('payload.php', 10, 'application/x-php')],
        ])->assertStatus(422)->assertJsonValidationErrors('files.0');
    }
}
