<?php

namespace Tests\Feature;

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

/**
 * The campaign in hand stays in hand.
 *
 * Every turn re-focused latestCampaign(), which is the newest campaign
 * artifact rather than the one being worked on, so a brief with three
 * platforms snapped back to the last one created after every single message.
 *
 * Driving arb-dev on 25 Sep it moved to Meta, answered one question there, and
 * was on LinkedIn again by the next turn. The product said so in its own reply:
 * "The declaration failed because focus had reset to LinkedIn. Meta is focused
 * again." Two turns went on re-answering the same question, and a request to
 * write ad copy for Meta landed on LinkedIn instead.
 *
 * The put-back exists for file attachments, which take focus for themselves.
 * That case is unaffected, because it is the one where no campaign is focused.
 */
class FocusStaysWhereItWasPutTest extends TestCase
{
    use RefreshDatabase;

    private User $user;

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

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

        MarketingAgent::fake(['Noted.']);
    }

    /** @return array{0: Campaign, 1: Campaign} the first created, then the newest */
    private function twoPlatforms(string $conversationId): array
    {
        $workspace = app(Workspace::class);
        $workspace->bindTo($conversationId);

        Brief::create(['conversation_id' => $conversationId, 'user_id' => $this->user->id]);

        $meta = Campaign::create([
            'user_id' => $this->user->id, 'name' => 'Meta campaign',
            'platform' => 'meta', 'status' => 'draft',
        ]);
        $linkedin = Campaign::create([
            'user_id' => $this->user->id, 'name' => 'LinkedIn campaign',
            'platform' => 'linkedin', 'status' => 'draft',
        ]);

        // Produced in creation order, so LinkedIn is the newest artifact and is
        // what latestCampaign() returns.
        $workspace->produced($meta, focus: false);
        $workspace->produced($linkedin, focus: false);

        return [$meta, $linkedin];
    }

    /** The defect: working on Meta, back on LinkedIn by the next message. */
    public function test_a_campaign_in_focus_survives_the_next_message(): void
    {
        $conversation = $this->ownedConversation();
        [$meta] = $this->twoPlatforms($conversation);

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

        $this->postJson(route('chat.send'), [
            'conversation' => $conversation,
            'message' => 'Declare no special ad category.',
        ])->assertOk();

        $this->assertSame(
            $meta->id,
            app(Workspace::class)->focus()?->id,
            'focus snapped back to the newest campaign instead of staying on the one in hand',
        );
    }

    /** With nothing focused, the newest campaign is still picked up. */
    public function test_a_conversation_with_no_focus_still_gets_one(): void
    {
        $conversation = $this->ownedConversation();
        [, $linkedin] = $this->twoPlatforms($conversation);

        app(Workspace::class)->clearFocus();

        $this->postJson(route('chat.send'), [
            'conversation' => $conversation,
            'message' => 'Carry on.',
        ])->assertOk();

        $this->assertSame($linkedin->id, app(Workspace::class)->focus()?->id);
    }

    /**
     * An attached file hands focus back to the campaign.
     *
     * This is what the put-back was written for, and it is exactly the case
     * where no campaign is focused, so narrowing the rule leaves it working.
     */
    public function test_an_attachment_hands_focus_back_to_the_campaign(): void
    {
        Storage::fake('local');

        $conversation = $this->ownedConversation();
        [, $linkedin] = $this->twoPlatforms($conversation);

        app(Workspace::class)->clearFocus();

        $this->post(route('chat.send'), [
            'conversation' => $conversation,
            'message' => 'Use this image.',
            'files' => [UploadedFile::fake()->image('hero.jpg', 1200, 628)],
        ]);

        $this->assertInstanceOf(Campaign::class, app(Workspace::class)->focus());
        $this->assertSame($linkedin->id, app(Workspace::class)->focus()?->id);
    }

    private function ownedConversation(): string
    {
        $id = (string) Str::uuid7();

        DB::table('agent_conversations')->insert([
            'id' => $id,
            'participant_type' => $this->user->getMorphClass(),
            'participant_id' => $this->user->getKey(),
            'title' => 'Focus test',
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        return $id;
    }
}
