<?php
namespace boru\ai\Tests\Provider\Gemini;

use boru\ai\AI;
use boru\ai\Config\Config;
use boru\ai\Conversation\Conversation;
use boru\ai\Conversation\ConversationReference;
use boru\ai\Conversation\Turn;
use boru\ai\Provider\Gemini\GeminiMapper;
use boru\ai\Provider\Gemini\GeminiProvider;
use boru\ai\Schema\Request;
use boru\ai\Schema\Response;
use boru\ai\Schema\ToolCall;
use boru\ai\Schema\ToolResult;
use boru\ai\Tests\Fakes\FakeGeminiClient;
use PHPUnit\Framework\TestCase;

class GeminiConversationReplayTest extends TestCase
{
    /** @var mixed */
    protected $previousConfig;

    protected function setUp(): void
    {
        $this->previousConfig = AI::config();
        AI::config(new Config());
    }

    protected function tearDown(): void
    {
        AI::config($this->previousConfig instanceof Config ? $this->previousConfig : null);

        putenv('AI_REPLAY_STRATEGY');
        putenv('AI_REPLAY_MAX_TURNS');
        putenv('AI_REPLAY_SUMMARY_KEEP_TURNS');
        putenv('AI_REPLAY_SUMMARY_MAX_CHARS');
        putenv('AI_REPLAY_INCLUDE_TOOL_DATA');
        putenv('AI_REPLAY_TOOL_DATA_MAX_CHARS');
        putenv('AI_REPLAY_TOOL_ARGS_MAX_CHARS');
        putenv('AI_REPLAY_TOOL_OUTPUT_MAX_CHARS');
        putenv('GEMINI_REPLAY_STRATEGY');
        putenv('GEMINI_REPLAY_MAX_TURNS');
        putenv('GEMINI_REPLAY_SUMMARY_KEEP_TURNS');
        putenv('GEMINI_REPLAY_SUMMARY_MAX_CHARS');
        putenv('GEMINI_REPLAY_INCLUDE_TOOL_DATA');
        putenv('GEMINI_REPLAY_TOOL_DATA_MAX_CHARS');
        putenv('GEMINI_REPLAY_TOOL_ARGS_MAX_CHARS');
        putenv('GEMINI_REPLAY_TOOL_OUTPUT_MAX_CHARS');
    }

    public function testGeminiProviderAppliesRuntimeOwnedReplayWhenConversationMetadataIsPresent()
    {
        $client = new FakeGeminiClient(array(
            array(
                'responseId' => 'gem_resp_replay_1',
                'modelVersion' => 'gemini-2.5-pro',
                'candidates' => array(
                    array(
                        'content' => array(
                            'parts' => array(
                                array('text' => 'Replay ok'),
                            ),
                        ),
                    ),
                ),
            ),
        ));

        $provider = new GeminiProvider($client, new GeminiMapper());

        $reference = new ConversationReference();
        $reference->externalReference('gemini_replay_test');

        $conversation = new Conversation($reference);

        $previousRequest = new Request();
        $previousRequest->addMessage('Hello there', 'user');
        $previousResponse = new Response();
        $previousResponse->text('Hi! How can I help?');

        $previousTurn = new Turn();
        $previousTurn->number(1)->request($previousRequest)->response($previousResponse);
        $conversation->addTurn($previousTurn);

        $request = new Request();
        $request->provider('gemini')->model('gemini-2.5-pro')->addMessage('Tell me more', 'user');
        $request->metadata('conversation', $conversation);

        $response = $provider->execute($request);

        $this->assertSame('Replay ok', $response->text());
        $this->assertCount(3, $client->requests[0]['payload']['contents']);
        $this->assertSame('Hello there', $client->requests[0]['payload']['contents'][0]['parts'][0]['text']);
        $this->assertSame('Hi! How can I help?', $client->requests[0]['payload']['contents'][1]['parts'][0]['text']);
        $this->assertSame('Tell me more', $client->requests[0]['payload']['contents'][2]['parts'][0]['text']);
    }

    public function testGeminiProviderAppliesSummarizedHistoryReplayStrategyWhenConfigured()
    {
        $config = new Config();
        $config->provider('gemini', array(
            'replay' => array(
                'enabled' => true,
                'strategy' => 'summarize_history',
                'summary' => array(
                    'keep_turns' => 1,
                    'max_chars' => 1000,
                ),
            ),
        ));
        AI::config($config);

        $client = new FakeGeminiClient(array(
            array(
                'responseId' => 'gem_resp_replay_summary_1',
                'modelVersion' => 'gemini-2.5-pro',
                'candidates' => array(
                    array(
                        'content' => array(
                            'parts' => array(
                                array('text' => 'Summary replay ok'),
                            ),
                        ),
                    ),
                ),
            ),
        ));

        $provider = new GeminiProvider($client, new GeminiMapper());
        $conversation = $this->buildConversationWithThreeTurns();

        $request = new Request();
        $request->provider('gemini')->model('gemini-2.5-pro')->addMessage('Continue please', 'user');
        $request->metadata('conversation', $conversation);

        $response = $provider->execute($request);

        $this->assertSame('Summary replay ok', $response->text());
        $this->assertCount(4, $client->requests[0]['payload']['contents']);
        $this->assertStringContainsString('Conversation summary of earlier context:', $client->requests[0]['payload']['contents'][0]['parts'][0]['text']);
        $this->assertStringContainsString('Turn 1', $client->requests[0]['payload']['contents'][0]['parts'][0]['text']);
        $this->assertStringContainsString('Turn 2', $client->requests[0]['payload']['contents'][0]['parts'][0]['text']);
        $this->assertSame('User 3', $client->requests[0]['payload']['contents'][1]['parts'][0]['text']);
        $this->assertSame('Assistant 3', $client->requests[0]['payload']['contents'][2]['parts'][0]['text']);
        $this->assertSame('Continue please', $client->requests[0]['payload']['contents'][3]['parts'][0]['text']);
    }

    public function testGeminiProviderIncludesToolReplayDataWhenConfigured()
    {
        $config = new Config();
        $config->provider('gemini', array(
            'replay' => array(
                'enabled' => true,
                'strategy' => 'full',
                'tool_data' => array(
                    'enabled' => true,
                ),
            ),
        ));
        AI::config($config);

        $client = new FakeGeminiClient(array(
            array(
                'responseId' => 'gem_resp_replay_tool_data_1',
                'modelVersion' => 'gemini-2.5-pro',
                'candidates' => array(
                    array(
                        'content' => array(
                            'parts' => array(
                                array('text' => 'Tool replay ok'),
                            ),
                        ),
                    ),
                ),
            ),
        ));

        $provider = new GeminiProvider($client, new GeminiMapper());
        $conversation = $this->buildConversationWithToolActivity();

        $request = new Request();
        $request->provider('gemini')->model('gemini-2.5-pro')->addMessage('Please continue', 'user');
        $request->metadata('conversation', $conversation);

        $response = $provider->execute($request);

        $this->assertSame('Tool replay ok', $response->text());
        $this->assertCount(4, $client->requests[0]['payload']['contents']);
        $this->assertSame('What is the weather in Atlanta?', $client->requests[0]['payload']['contents'][0]['parts'][0]['text']);
        $this->assertSame('I checked the weather for Atlanta.', $client->requests[0]['payload']['contents'][1]['parts'][0]['text']);
        $this->assertStringContainsString('Turn 1 tool activity:', $client->requests[0]['payload']['contents'][2]['parts'][0]['text']);
        $this->assertStringContainsString('Tool call get_weather', $client->requests[0]['payload']['contents'][2]['parts'][0]['text']);
        $this->assertStringContainsString('Tool result get_weather', $client->requests[0]['payload']['contents'][2]['parts'][0]['text']);
        $this->assertStringContainsString('sunny', $client->requests[0]['payload']['contents'][2]['parts'][0]['text']);
        $this->assertSame('Please continue', $client->requests[0]['payload']['contents'][3]['parts'][0]['text']);
    }

    public function testGeminiProviderTruncatesToolReplayDataToConfiguredBudget()
    {
        $config = new Config();
        $config->provider('gemini', array(
            'replay' => array(
                'enabled' => true,
                'strategy' => 'full',
                'tool_data' => array(
                    'enabled' => true,
                    'max_chars' => 120,
                ),
            ),
        ));
        AI::config($config);

        $client = new FakeGeminiClient(array(
            array(
                'responseId' => 'gem_resp_replay_tool_data_truncated_1',
                'modelVersion' => 'gemini-2.5-pro',
                'candidates' => array(
                    array(
                        'content' => array(
                            'parts' => array(
                                array('text' => 'Tool replay truncated ok'),
                            ),
                        ),
                    ),
                ),
            ),
        ));

        $provider = new GeminiProvider($client, new GeminiMapper());
        $conversation = $this->buildConversationWithLongToolActivity();

        $request = new Request();
        $request->provider('gemini')->model('gemini-2.5-pro')->addMessage('Please continue', 'user');
        $request->metadata('conversation', $conversation);

        $response = $provider->execute($request);

        $this->assertSame('Tool replay truncated ok', $response->text());
        $this->assertCount(4, $client->requests[0]['payload']['contents']);
        $toolReplayText = $client->requests[0]['payload']['contents'][2]['parts'][0]['text'];
        $this->assertLessThanOrEqual(120, strlen($toolReplayText));
        $this->assertStringContainsString('Turn 1 tool activity:', $toolReplayText);
        $this->assertStringEndsWith('...', $toolReplayText);
    }

    public function testGeminiProviderTruncatesToolArgsAndOutputPerFieldBudgets()
    {
        $config = new Config();
        $config->provider('gemini', array(
            'replay' => array(
                'enabled' => true,
                'strategy' => 'full',
                'tool_data' => array(
                    'enabled' => true,
                    'max_chars' => 2000,
                    'args_max_chars' => 40,
                    'output_max_chars' => 50,
                ),
            ),
        ));
        AI::config($config);

        $client = new FakeGeminiClient(array(
            array(
                'responseId' => 'gem_resp_replay_tool_field_truncation_1',
                'modelVersion' => 'gemini-2.5-pro',
                'candidates' => array(
                    array(
                        'content' => array(
                            'parts' => array(
                                array('text' => 'Tool field truncation ok'),
                            ),
                        ),
                    ),
                ),
            ),
        ));

        $provider = new GeminiProvider($client, new GeminiMapper());
        $conversation = $this->buildConversationWithLongToolActivity();

        $request = new Request();
        $request->provider('gemini')->model('gemini-2.5-pro')->addMessage('Please continue', 'user');
        $request->metadata('conversation', $conversation);

        $response = $provider->execute($request);

        $this->assertSame('Tool field truncation ok', $response->text());
        $this->assertCount(4, $client->requests[0]['payload']['contents']);
        $toolReplayText = $client->requests[0]['payload']['contents'][2]['parts'][0]['text'];
        $this->assertStringContainsString('args=', $toolReplayText);
        $this->assertStringContainsString('output=', $toolReplayText);
        $this->assertMatchesRegularExpression('/args=.*\.\.\./', $toolReplayText);
        $this->assertMatchesRegularExpression('/output=.*\.\.\./', $toolReplayText);
    }

    protected function buildConversationWithThreeTurns()
    {
        $reference = new ConversationReference();
        $reference->externalReference('gemini_replay_summary_test');
        $conversation = new Conversation($reference);

        for ($i = 1; $i <= 3; $i++) {
            $request = new Request();
            $request->addMessage('User ' . $i, 'user');

            $response = new Response();
            $response->text('Assistant ' . $i);

            $turn = new Turn();
            $turn->number($i)->request($request)->response($response);
            $conversation->addTurn($turn);
        }

        return $conversation;
    }

    protected function buildConversationWithToolActivity()
    {
        $reference = new ConversationReference();
        $reference->externalReference('gemini_replay_tool_data_test');
        $conversation = new Conversation($reference);

        $request = new Request();
        $request->addMessage('What is the weather in Atlanta?', 'user');

        $response = new Response();
        $response->text('I checked the weather for Atlanta.');

        $toolCall = new ToolCall();
        $toolCall->id('call_weather_1');
        $toolCall->name('get_weather');
        $toolCall->arguments(array('city' => 'Atlanta'));
        $toolCall->status('completed');

        $toolResult = new ToolResult();
        $toolResult->toolCallId('call_weather_1');
        $toolResult->name('get_weather');
        $toolResult->output(array('city' => 'Atlanta', 'forecast' => 'sunny'));
        $toolResult->success(true);

        $turn = new Turn();
        $turn->number(1)
            ->request($request)
            ->response($response)
            ->addToolCall($toolCall)
            ->addToolResult($toolResult);

        $conversation->addTurn($turn);

        return $conversation;
    }

    protected function buildConversationWithLongToolActivity()
    {
        $reference = new ConversationReference();
        $reference->externalReference('gemini_replay_tool_data_long_test');
        $conversation = new Conversation($reference);

        $request = new Request();
        $request->addMessage('Run the large tool payload example.', 'user');

        $response = new Response();
        $response->text('I ran the large tool payload example.');

        $toolCall = new ToolCall();
        $toolCall->id('call_large_1');
        $toolCall->name('large_tool');
        $toolCall->arguments(array('payload' => str_repeat('alpha', 40)));
        $toolCall->status('completed');

        $toolResult = new ToolResult();
        $toolResult->toolCallId('call_large_1');
        $toolResult->name('large_tool');
        $toolResult->output(array('blob' => str_repeat('bravo', 60)));
        $toolResult->success(true);

        $turn = new Turn();
        $turn->number(1)
            ->request($request)
            ->response($response)
            ->addToolCall($toolCall)
            ->addToolResult($toolResult);

        $conversation->addTurn($turn);

        return $conversation;
    }
}
