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

use boru\ai\AI;
use boru\ai\Conversation\ConversationReference;
use boru\ai\Persistence\Db\DbPersistenceProvider;
use boru\ai\Tests\Fakes\FakeHttpClient;

class DbBootstrapToolLoopIntegrationTest extends DbIntegrationTestCase
{
    public function testBootstrapToolLoopPersistsToolCallLifecycleRows()
    {
        $httpClient = new FakeHttpClient();
        $httpClient->queueResponse($this->jsonResponse(array(
            'id' => 'resp_tool_1',
            'model' => 'gpt-5.1',
            'status' => 'tool_calling',
            'output' => array(
                array(
                    'type' => 'function_call',
                    'call_id' => 'call_weather_1',
                    'name' => 'get_current_temperature',
                    'arguments' => '{"location":"Atlanta, GA","unit":"Fahrenheit"}',
                ),
            ),
        )));
        $httpClient->queueResponse($this->jsonResponse(array(
            'id' => 'resp_tool_2',
            'model' => 'gpt-5.1',
            'status' => 'completed',
            'output' => array(
                array(
                    'type' => 'message',
                    'content' => array(
                        array(
                            'type' => 'output_text',
                            'text' => 'The current temperature is 69°F.',
                        ),
                    ),
                ),
            ),
        )));

        $this->bootstrapWithDbPersistence($httpClient, null);

        $response = AI::provider('openai')
            ->reference('db-tool-ref', 'user-2')
            ->model('gpt-5.1')
            ->defineTool(
                'get_current_temperature',
                'Get the current temperature',
                array(
                    'type' => 'object',
                    'properties' => array(
                        'location' => array('type' => 'string'),
                        'unit' => array('type' => 'string'),
                    ),
                ),
                function (array $arguments) {
                    return json_encode(array(
                        'location' => $arguments['location'],
                        'unit' => $arguments['unit'],
                        'temperature' => 69,
                    ));
                }
            )
            ->addMessage('What is the weather in Atlanta?')
            ->run();

        $this->assertSame('The current temperature is 69°F.', $response->text());
        $this->assertCount(2, $httpClient->requests);
        $this->assertSame('call_weather_1', $httpClient->requests[1]->json()['input'][1]['call_id']);
        $this->assertStringContainsString('"temperature":69', $httpClient->requests[1]->json()['input'][1]['output']);

        $this->assertSame(1, $this->countRows($this->tableNames['conversations']));
        $this->assertSame(1, $this->countRows($this->tableNames['turns']));
        $this->assertSame(1, $this->countRows($this->tableNames['tool_calls']));

        $toolCallRow = $this->fetchOne('select * from `' . $this->tableNames['tool_calls'] . '` order by `id` asc limit 1');
        $this->assertSame('call_weather_1', $toolCallRow->get('tool_call_id'));
        $this->assertSame('get_current_temperature', $toolCallRow->get('name'));
        $this->assertSame(1, (int)$toolCallRow->get('success'));
        $this->assertSame('completed', $toolCallRow->get('status'));
        $this->assertStringContainsString('Atlanta, GA', $toolCallRow->get('arguments_json'));
        $toolCallResult = json_decode($toolCallRow->get('result_json'), true);
        $this->assertIsArray($toolCallResult);
        $this->assertSame('call_weather_1', $toolCallResult['tool_call_id']);
        $this->assertTrue($toolCallResult['success']);
        $decodedToolOutput = json_decode($toolCallResult['output'], true);
        $this->assertIsArray($decodedToolOutput);
        $this->assertSame(69, $decodedToolOutput['temperature']);
        $this->assertSame('Atlanta, GA', $decodedToolOutput['location']);

        $turnRow = $this->fetchOne('select * from `' . $this->tableNames['turns'] . '` order by `id` asc limit 1');
        $this->assertSame('completed', $turnRow->get('status'));
        $this->assertStringContainsString('The current temperature is 69', $turnRow->get('response_json'));
        $this->assertStringContainsString('"success":true', $turnRow->get('tool_results_json'));

        $persistence = AI::persistence();
        $this->assertInstanceOf(DbPersistenceProvider::class, $persistence);

        $reference = new ConversationReference();
        $reference->externalReference('db-tool-ref');
        $reference->userId('user-2');
        $conversation = $persistence->loadConversation($reference);

        $this->assertCount(1, $conversation->turns());
        $this->assertCount(0, $conversation->turns()[0]->response()->toolCalls());
        $this->assertCount(1, $conversation->turns()[0]->toolResults());
        $this->assertSame('The current temperature is 69°F.', $conversation->turns()[0]->response()->text());
    }

    public function testBootstrapToolLoopPersistsFailedToolResultState()
    {
        $httpClient = new FakeHttpClient();
        $httpClient->queueResponse($this->jsonResponse(array(
            'id' => 'resp_tool_fail_1',
            'model' => 'gpt-5.1',
            'status' => 'tool_calling',
            'output' => array(
                array(
                    'type' => 'function_call',
                    'call_id' => 'call_weather_fail_1',
                    'name' => 'get_current_temperature',
                    'arguments' => '{"location":"Atlanta, GA"}',
                ),
            ),
        )));
        $httpClient->queueResponse($this->jsonResponse(array(
            'id' => 'resp_tool_fail_2',
            'model' => 'gpt-5.1',
            'status' => 'completed',
            'output' => array(
                array(
                    'type' => 'message',
                    'content' => array(
                        array(
                            'type' => 'output_text',
                            'text' => 'Tool failure was handled.',
                        ),
                    ),
                ),
            ),
        )));

        $this->bootstrapWithDbPersistence($httpClient, null);

        $response = AI::provider('openai')
            ->reference('db-tool-fail-ref', 'user-3')
            ->model('gpt-5.1')
            ->defineTool(
                'get_current_temperature',
                'Get the current temperature',
                array(
                    'type' => 'object',
                    'properties' => array(
                        'location' => array('type' => 'string'),
                    ),
                ),
                function () {
                    throw new \RuntimeException('Tool execution blew up');
                }
            )
            ->addMessage('Try the weather tool')
            ->run();

        $this->assertSame('Tool failure was handled.', $response->text());
        $this->assertCount(2, $httpClient->requests);
        $this->assertSame('call_weather_fail_1', $httpClient->requests[1]->json()['input'][1]['call_id']);
        $this->assertSame('Tool execution blew up', $httpClient->requests[1]->json()['input'][1]['output']);

        $toolCallRow = $this->fetchOne('select * from `' . $this->tableNames['tool_calls'] . '` order by `id` asc limit 1');
        $this->assertSame('call_weather_fail_1', $toolCallRow->get('tool_call_id'));
        $this->assertSame('get_current_temperature', $toolCallRow->get('name'));
        $this->assertSame(0, (int)$toolCallRow->get('success'));
        $this->assertStringContainsString('Tool execution blew up', $toolCallRow->get('result_json'));

        $turnRow = $this->fetchOne('select * from `' . $this->tableNames['turns'] . '` order by `id` asc limit 1');
        $this->assertSame('completed', $turnRow->get('status'));
        $this->assertStringContainsString('"success":false', $turnRow->get('tool_results_json'));
        $this->assertStringContainsString('Tool execution blew up', $turnRow->get('tool_results_json'));
    }
}
