<?php
namespace boru\ai\Tests\Conversation;

use boru\ai\Config\Config;
use boru\ai\Config\ConfigResolver;
use boru\ai\Conversation\Conversation;
use boru\ai\Conversation\ConversationReference;
use boru\ai\Conversation\ConversationReplayService;
use boru\ai\Conversation\Turn;
use boru\ai\Schema\Request;
use boru\ai\Schema\Response;
use boru\ai\Schema\ToolCall;
use boru\ai\Schema\ToolResult;
use PHPUnit\Framework\TestCase;

class ConversationReplayServiceTest extends TestCase
{
    protected function tearDown(): void
    {
        putenv('AI_REPLAY_STRATEGY');
        putenv('AI_REPLAY_MAX_TURNS');
        putenv('AI_REPLAY_SUMMARY_KEEP_TURNS');
        putenv('AI_REPLAY_SUMMARY_MAX_CHARS');
        putenv('AI_REPLAY_TOOL_DATA_ENABLED');
        putenv('AI_REPLAY_TOOL_DATA_MAX_CHARS');
        putenv('AI_REPLAY_TOOL_DATA_ARGS_MAX_CHARS');
        putenv('AI_REPLAY_TOOL_DATA_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_TOOL_DATA_ENABLED');
        putenv('GEMINI_REPLAY_TOOL_DATA_MAX_CHARS');
        putenv('GEMINI_REPLAY_TOOL_DATA_ARGS_MAX_CHARS');
        putenv('GEMINI_REPLAY_TOOL_DATA_OUTPUT_MAX_CHARS');
    }

    public function testFullReplayStrategyIncludesAllPriorTurns()
    {
        $service = new ConversationReplayService();
        $conversation = $this->buildConversationWithThreeTurns();

        $messages = $service->buildReplayMessages($conversation, null, array(
            'strategy' => 'full',
        ));

        $this->assertCount(6, $messages);
        $this->assertSame('User 1', $messages[0]->parts()[0]->value());
        $this->assertSame('Assistant 3', $messages[5]->parts()[0]->value());
    }

    public function testFullReplayStrategyCanIncludeToolCallAndResultData()
    {
        $service = new ConversationReplayService();
        $conversation = $this->buildConversationWithToolActivity();

        $messages = $service->buildReplayMessages($conversation, null, array(
            'strategy' => 'full',
            'tool_data' => array(
                'enabled' => true,
            ),
        ));

        $this->assertCount(3, $messages);
        $this->assertSame('What is the weather in Atlanta?', $messages[0]->parts()[0]->value());
        $this->assertSame('I checked the weather for Atlanta.', $messages[1]->parts()[0]->value());
        $this->assertSame('tool_activity', $messages[2]->metadata('replay_kind'));
        $this->assertStringContainsString('Tool call get_weather', $messages[2]->parts()[0]->value());
        $this->assertStringContainsString('status=completed', $messages[2]->parts()[0]->value());
        $this->assertStringContainsString('Tool result get_weather', $messages[2]->parts()[0]->value());
        $this->assertStringContainsString('sunny', $messages[2]->parts()[0]->value());
    }

    public function testFullReplayStrategyTruncatesToolDataToConfiguredBudget()
    {
        $service = new ConversationReplayService();
        $conversation = $this->buildConversationWithLongToolActivity();

        $messages = $service->buildReplayMessages($conversation, null, array(
            'strategy' => 'full',
            'tool_data' => array(
                'enabled' => true,
                'max_chars' => 120,
            ),
        ));

        $this->assertCount(3, $messages);
        $toolReplayText = $messages[2]->parts()[0]->value();
        $this->assertLessThanOrEqual(120, strlen($toolReplayText));
        $this->assertStringContainsString('Turn 1 tool activity:', $toolReplayText);
        $this->assertStringEndsWith('...', $toolReplayText);
    }

    public function testFullReplayStrategyTruncatesToolArgsAndOutputPerFieldBudgets()
    {
        $service = new ConversationReplayService();
        $conversation = $this->buildConversationWithLongToolActivity();

        $messages = $service->buildReplayMessages($conversation, null, array(
            'strategy' => 'full',
            'tool_data' => array(
                'enabled' => true,
                'max_chars' => 2000,
                'args_max_chars' => 40,
                'output_max_chars' => 50,
            ),
        ));

        $this->assertCount(3, $messages);
        $toolReplayText = $messages[2]->parts()[0]->value();
        $this->assertStringContainsString('args=', $toolReplayText);
        $this->assertStringContainsString('output=', $toolReplayText);
        $this->assertMatchesRegularExpression('/args=.*\.\.\./', $toolReplayText);
        $this->assertMatchesRegularExpression('/output=.*\.\.\./', $toolReplayText);
    }

    public function testLastNTurnsStrategyLimitsReplayWindow()
    {
        $service = new ConversationReplayService();
        $conversation = $this->buildConversationWithThreeTurns();

        $messages = $service->buildReplayMessages($conversation, null, array(
            'strategy' => 'last_n_turns',
            'max_turns' => 2,
        ));

        $this->assertCount(4, $messages);
        $this->assertSame('User 2', $messages[0]->parts()[0]->value());
        $this->assertSame('Assistant 3', $messages[3]->parts()[0]->value());
    }

    public function testSummarizeHistoryStrategyCompactsOlderTurnsIntoPrimerMessage()
    {
        $service = new ConversationReplayService();
        $conversation = $this->buildConversationWithThreeTurns();

        $messages = $service->buildReplayMessages($conversation, null, array(
            'strategy' => 'summarize_history',
            'summary' => array(
                'keep_turns' => 1,
                'max_chars' => 1000,
            ),
        ));

        $this->assertCount(3, $messages);
        $this->assertSame('system', $messages[0]->role());
        $this->assertSame('summarize_history', $messages[0]->metadata('replay_strategy'));
        $this->assertStringContainsString('Turn 1', $messages[0]->parts()[0]->value());
        $this->assertStringContainsString('Turn 2', $messages[0]->parts()[0]->value());
        $this->assertSame('User 3', $messages[1]->parts()[0]->value());
        $this->assertSame('Assistant 3', $messages[2]->parts()[0]->value());
    }

    public function testResolvedOptionsUseConfigResolverPrecedence()
    {
        $config = new Config();
        $config->runtime(array(
            'replay' => array(
                'enabled' => false,
                'strategy' => 'full',
                'max_turns' => 10,
                'summary' => array(
                    'keep_turns' => 2,
                    'max_chars' => 2000,
                ),
                'tool_data' => array(
                    'enabled' => false,
                    'max_chars' => 1800,
                    'args_max_chars' => 500,
                    'output_max_chars' => 800,
                ),
            ),
        ));
        $config->provider('gemini', array(
            'replay' => array(
                'enabled' => true,
                'strategy' => 'last_n_turns',
                'max_turns' => 3,
                'summary' => array(
                    'keep_turns' => 4,
                    'max_chars' => 3000,
                ),
                'tool_data' => array(
                    'enabled' => false,
                    'max_chars' => 900,
                    'args_max_chars' => 300,
                    'output_max_chars' => 700,
                ),
            ),
        ));

        putenv('AI_REPLAY_MAX_TURNS=5');
        putenv('GEMINI_REPLAY_STRATEGY=summarize_history');
        putenv('GEMINI_REPLAY_SUMMARY_KEEP_TURNS=1');
        putenv('GEMINI_REPLAY_SUMMARY_MAX_CHARS=1500');
        putenv('GEMINI_REPLAY_TOOL_DATA_ENABLED=1');
        putenv('GEMINI_REPLAY_TOOL_DATA_MAX_CHARS=300');
        putenv('GEMINI_REPLAY_TOOL_DATA_ARGS_MAX_CHARS=90');
        putenv('GEMINI_REPLAY_TOOL_DATA_OUTPUT_MAX_CHARS=140');

        $service = new ConversationReplayService();
        $resolver = new ConfigResolver($config);
        $options = $service->resolvedOptions('gemini', $resolver, array(
            'enabled' => true,
            'strategy' => 'full',
            'max_turns' => 10,
            'summary' => array(
                'keep_turns' => 3,
                'max_chars' => 4000,
            ),
            'tool_data' => array(
                'enabled' => false,
                'max_chars' => 2000,
                'args_max_chars' => 600,
                'output_max_chars' => 1000,
            ),
        ));

        $this->assertTrue($options['enabled']);
        $this->assertSame('summarize_history', $options['strategy']);
        $this->assertSame(5, (int)$options['max_turns']);
        $this->assertSame(1, (int)$options['summary']['keep_turns']);
        $this->assertSame(1500, (int)$options['summary']['max_chars']);
        $this->assertTrue((bool)$options['tool_data']['enabled']);
        $this->assertSame(300, (int)$options['tool_data']['max_chars']);
        $this->assertSame(90, (int)$options['tool_data']['args_max_chars']);
        $this->assertSame(140, (int)$options['tool_data']['output_max_chars']);
    }

    protected function buildConversationWithThreeTurns()
    {
        $reference = new ConversationReference();
        $reference->externalReference('replay_strategy_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('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('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;
    }
}
