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

use boru\ai\Runtime\ToolDefinition;
use boru\ai\Runtime\ToolExecutor;
use boru\ai\Runtime\ToolRegistry;
use boru\ai\Schema\Request;
use boru\ai\Schema\Response;
use boru\ai\Schema\ToolCall;
use PHPUnit\Framework\TestCase;

class ToolExecutorTest extends TestCase
{
    public function testExecutesKnownToolAndReturnsToolResult()
    {
        $registry = new ToolRegistry();
        $definition = new ToolDefinition();
        $definition->name('get_current_temperature')
            ->description('Get the current temperature')
            ->schema(array('type' => 'object', 'properties' => array()))
            ->callback(function (array $arguments) {
                return json_encode(array(
                    'location' => $arguments['location'],
                    'temperature' => 69,
                ));
            });
        $registry->register($definition);

        $executor = new ToolExecutor($registry);
        $request = new Request();
        $response = new Response();
        $toolCall = new ToolCall();
        $toolCall->id('call-1')->name('get_current_temperature')->arguments(array('location' => 'Atlanta, GA'));
        $response->addToolCall($toolCall);

        $results = $executor->executeToolCalls($request, $response);

        $this->assertCount(1, $results);
        $this->assertTrue($results[0]->success());
        $this->assertSame('get_current_temperature', $results[0]->name());
        $this->assertStringContainsString('Atlanta, GA', $results[0]->output());
    }

    public function testReturnsFailedToolResultForUnknownTool()
    {
        $executor = new ToolExecutor(new ToolRegistry());
        $request = new Request();
        $response = new Response();
        $toolCall = new ToolCall();
        $toolCall->id('call-2')->name('missing_tool')->arguments(array());
        $response->addToolCall($toolCall);

        $results = $executor->executeToolCalls($request, $response);

        $this->assertCount(1, $results);
        $this->assertFalse($results[0]->success());
        $this->assertSame('Unknown tool: missing_tool', $results[0]->output());
    }

    public function testCatchesToolExceptionsAndReturnsFailedResult()
    {
        $registry = new ToolRegistry();
        $definition = new ToolDefinition();
        $definition->name('explode_tool')
            ->description('Throws an exception')
            ->schema(array('type' => 'object', 'properties' => array()))
            ->callback(function () {
                throw new \RuntimeException('Boom');
            });
        $registry->register($definition);

        $executor = new ToolExecutor($registry);
        $request = new Request();
        $response = new Response();
        $toolCall = new ToolCall();
        $toolCall->id('call-3')->name('explode_tool')->arguments(array());
        $response->addToolCall($toolCall);

        $results = $executor->executeToolCalls($request, $response);

        $this->assertCount(1, $results);
        $this->assertFalse($results[0]->success());
        $this->assertSame('Boom', $results[0]->output());
        $this->assertSame('RuntimeException', $results[0]->metadata('exception_class'));
    }
}
