<?php

namespace Tests\Feature;

use App\Agent\SummarisingConversationStore;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Collection;
use Laravel\Ai\Agents\SummarizeAgent;
use Laravel\Ai\Contracts\ConversationStore;
use Laravel\Ai\Messages\UserMessage;
use Laravel\Ai\Prompts\AgentPrompt;
use Laravel\Ai\Responses\AgentResponse;
use Tests\TestCase;

/**
 * Long conversations must not silently lose their beginning.
 *
 * The package keeps the newest N turns and drops the rest with no signal.
 * Campaign values survive that, because they live in the database and are
 * re-injected each turn, so what is lost is the reasoning: what the user asked
 * for and what they already rejected. Losing that is how a chat starts
 * re-suggesting the thing it was told no to.
 */
class ConversationSummaryTest extends TestCase
{
    use RefreshDatabase;

    public function test_a_short_conversation_is_passed_through_untouched(): void
    {
        $store = new SummarisingConversationStore($this->storeReturning(3));

        $messages = $store->getLatestConversationMessages('conv-1', 10);

        // Under the window nothing was dropped, so there is nothing to summarise
        // and no reason to spend a model call finding that out.
        $this->assertCount(3, $messages);
    }

    public function test_older_turns_are_summarised_rather_than_dropped(): void
    {
        SummarizeAgent::fake(['User rejected Instagram and set a 20 dollar cap.']);

        $store = new SummarisingConversationStore($this->storeReturning(12));

        $messages = $store->getLatestConversationMessages('conv-2', 4);

        // Four recent turns verbatim, plus one message carrying the rest.
        $this->assertCount(5, $messages);
        $this->assertStringContainsString('Earlier in this conversation', $messages->first()->content);
        $this->assertStringContainsString('rejected Instagram', $messages->first()->content);
    }

    public function test_a_failed_summary_does_not_break_the_turn(): void
    {
        SummarizeAgent::fake(fn () => throw new \RuntimeException('provider down'));

        $store = new SummarisingConversationStore($this->storeReturning(12));

        $messages = $store->getLatestConversationMessages('conv-3', 4);

        // Degrades to the package's own behaviour rather than failing the user.
        $this->assertCount(4, $messages);
    }

    /** A stand-in store that returns however many messages the test asks for. */
    private function storeReturning(int $available): ConversationStore
    {
        return new class($available) implements ConversationStore
        {
            public function __construct(private int $available) {}

            public function getLatestConversationMessages(string $conversationId, int $limit): Collection
            {
                return Collection::times(
                    min($limit, $this->available),
                    fn (int $i): UserMessage => new UserMessage("turn {$i}"),
                );
            }

            public function latestConversationId(string $t, string|int $i): ?string
            {
                return null;
            }

            public function storeConversation(?string $t, string|int|null $i, string $title): string
            {
                return 'conv';
            }

            public function storeUserMessage(string $c, ?string $t, string|int|null $i, AgentPrompt $p): string
            {
                return 'msg';
            }

            public function storeAssistantMessage(string $c, ?string $t, string|int|null $i, AgentPrompt $p, AgentResponse $r): ?string
            {
                return 'msg';
            }

            public function storeApprovalResults(string $c, ?string $t, string|int|null $i, array $results): void {}
        };
    }
}
