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

use boru\ai\AI;
use boru\ai\Runtime\RuntimeContext;
use boru\ai\Schema\Response;
use boru\ai\Schema\ToolCall;
use boru\ai\Tests\Fakes\FakeGeminiClient;
use PHPUnit\Framework\TestCase;

class GeminiRuntimeToolLoopTest extends TestCase
{
    protected function setUp(): void
    {
        AI::runtimeContext(new RuntimeContext());
    }

    public function testAiFacadeExecutesGeminiToolLoopAndReturnsFinalText()
    {
        $client = new FakeGeminiClient(array(
            array(
                'responseId' => 'gem_resp_tool_1',
                'modelVersion' => 'gemini-2.5-pro',
                'candidates' => array(
                    array(
                        'content' => array(
                            'parts' => array(
                                array(
                                    'functionCall' => array(
                                        'name' => 'get_current_temperature',
                                        'args' => array(
                                            'location' => 'Atlanta, GA',
                                            'unit' => 'Fahrenheit',
                                        ),
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
            ),
            array(
                'responseId' => 'gem_resp_tool_2',
                'modelVersion' => 'gemini-2.5-pro',
                'candidates' => array(
                    array(
                        'content' => array(
                            'parts' => array(
                                array(
                                    'text' => 'The current temperature in Atlanta, GA is 69°F.',
                                ),
                            ),
                        ),
                    ),
                ),
            ),
        ));

        AI::registry()->register(new \boru\ai\Provider\Gemini\GeminiProvider($client, new \boru\ai\Provider\Gemini\GeminiMapper()));

        $response = AI::provider('gemini')
            ->model('gemini-2.5-pro')
            ->setToolContextValue('temp', 69)
            ->defineTool(
                'get_current_temperature',
                'Get the current temperature for a specific location',
                array(
                    'type' => 'object',
                    'properties' => array(
                        'location' => array('type' => 'string'),
                        'unit' => array('type' => 'string'),
                    ),
                    'required' => array('location', 'unit'),
                ),
                function (array $arguments, $context) {
                    $temp = $context ? $context->get('temp', 69) : 69;
                    return json_encode(array(
                        'location' => isset($arguments['location']) ? $arguments['location'] : null,
                        'unit' => isset($arguments['unit']) ? $arguments['unit'] : null,
                        'temperature' => $temp,
                    ));
                }
            )
            ->addMessage('What is the weather in Atlanta right now in Fahrenheit?')
            ->run();

        $this->assertInstanceOf(Response::class, $response);
        $this->assertSame('The current temperature in Atlanta, GA is 69°F.', $response->text());
        $this->assertCount(2, $client->requests);
        $this->assertSame('generateContent', $client->requests[0]['method']);
        $this->assertSame('generateContent', $client->requests[1]['method']);
        $this->assertSame('model', $client->requests[1]['payload']['contents'][1]['role']);
        $this->assertSame('get_current_temperature', $client->requests[1]['payload']['contents'][1]['parts'][0]['functionCall']['name']);
        $this->assertSame('user', $client->requests[1]['payload']['contents'][2]['role']);
        $this->assertSame('get_current_temperature', $client->requests[1]['payload']['contents'][2]['parts'][0]['functionResponse']['name']);
        $this->assertSame(69, $client->requests[1]['payload']['contents'][2]['parts'][0]['functionResponse']['response']['temperature']);
    }

    public function testGeminiMapperResponseCreatesNormalizedToolCallForRuntime()
    {
        $mapper = new \boru\ai\Provider\Gemini\GeminiMapper();
        $response = $mapper->mapResponse(array(
            'responseId' => 'gem_resp_tool_call',
            'modelVersion' => 'gemini-2.5-pro',
            'candidates' => array(
                array(
                    'content' => array(
                        'parts' => array(
                            array(
                                'functionCall' => array(
                                    'name' => 'get_current_temperature',
                                    'args' => array(
                                        'location' => 'Atlanta, GA',
                                        'unit' => 'Fahrenheit',
                                    ),
                                ),
                            ),
                        ),
                    ),
                ),
            ),
        ));

        $this->assertCount(1, $response->toolCalls());
        $this->assertInstanceOf(ToolCall::class, $response->toolCalls()[0]);
        $this->assertSame('get_current_temperature', $response->toolCalls()[0]->name());
        $this->assertSame('Atlanta, GA', $response->toolCalls()[0]->arguments()['location']);
    }

    public function testGeminiMapperCanExtractTextFromCandidatePartsWhenPresent()
    {
        $mapper = new \boru\ai\Provider\Gemini\GeminiMapper();
        $response = $mapper->mapResponse(array(
            'responseId' => 'gem_resp_text',
            'modelVersion' => 'gemini-2.5-pro',
            'candidates' => array(
                array(
                    'content' => array(
                        'parts' => array(
                            array('text' => 'Hello'),
                            array('text' => 'world'),
                        ),
                    ),
                ),
            ),
        ));

        $this->assertSame("Hello\nworld", $response->text());
    }
}
