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

use boru\ai\Provider\OpenAI\OpenAIMapper;
use boru\ai\Schema\Request;
use boru\ai\Schema\ToolResult;
use PHPUnit\Framework\TestCase;

class OpenAIMapperTest extends TestCase
{
    public function testMapsNormalizedRequestToOpenAIResponsesPayload()
    {
        $mapper = new OpenAIMapper();
        $request = new Request();
        $request->model('gpt-5.1')
            ->instructions('You are helpful')
            ->addMessage('What is the weather in Atlanta?', 'user')
            ->addTool(array(
                'name' => 'get_current_temperature',
                'description' => 'Get current temperature',
                'schema' => array(
                    'type' => 'object',
                    'properties' => array(
                        'location' => array('type' => 'string'),
                    ),
                ),
            ))
            ->option('temperature', 0.2);

        $payload = $mapper->mapRequest($request);

        $this->assertSame('gpt-5.1', $payload['model']);
        $this->assertSame('You are helpful', $payload['instructions']);
        $this->assertSame(0.2, $payload['temperature']);
        $this->assertCount(1, $payload['input']);
        $this->assertSame('message', $payload['input'][0]['type']);
        $this->assertSame('user', $payload['input'][0]['role']);
        $this->assertSame('input_text', $payload['input'][0]['content'][0]['type']);
        $this->assertSame('What is the weather in Atlanta?', $payload['input'][0]['content'][0]['text']);
        $this->assertCount(1, $payload['tools']);
        $this->assertSame('function', $payload['tools'][0]['type']);
        $this->assertSame('get_current_temperature', $payload['tools'][0]['name']);
    }

    public function testMapsToolResultsIntoOpenAIFunctionCallOutputInput()
    {
        $mapper = new OpenAIMapper();
        $request = new Request();
        $request->model('gpt-5.1');

        $toolResult = new ToolResult();
        $toolResult->toolCallId('call_123');
        $toolResult->name('get_current_temperature');
        $toolResult->output('{"temperature":69}');
        $request->addToolResult($toolResult);

        $payload = $mapper->mapRequest($request);

        $this->assertCount(1, $payload['input']);
        $this->assertSame('function_call_output', $payload['input'][0]['type']);
        $this->assertSame('call_123', $payload['input'][0]['call_id']);
        $this->assertSame('{"temperature":69}', $payload['input'][0]['output']);
    }

    public function testMapsReasoningConfigurationIntoResponsesPayload()
    {
        $mapper = new OpenAIMapper();
        $request = new Request();
        $request->model('gpt-5.6-sol')
            ->addMessage('Review this migration plan.')
            ->option('reasoning', array(
                'effort' => 'max',
                'mode' => 'pro',
                'context' => 'all_turns',
            ));

        $payload = $mapper->mapRequest($request);

        $this->assertSame(array(
            'effort' => 'max',
            'mode' => 'pro',
            'context' => 'all_turns',
        ), $payload['reasoning']);
    }

    public function testMapsOpenAIResponseIntoNormalizedResponseAndToolCalls()
    {
        $mapper = new OpenAIMapper();

        $payload = array(
            'id' => 'resp_123',
            'model' => 'gpt-5.1',
            'status' => 'completed',
            'usage' => array(
                'input_tokens' => 21,
                'input_tokens_details' => array(
                    'cached_tokens' => 4,
                ),
                'output_tokens' => 23,
                'output_tokens_details' => array(
                    'reasoning_tokens' => 7,
                ),
                'total_tokens' => 44,
            ),
            'output' => array(
                array(
                    'type' => 'function_call',
                    'call_id' => 'call_123',
                    'name' => 'get_current_temperature',
                    'arguments' => '{"location":"Atlanta, GA","unit":"Fahrenheit"}',
                ),
                array(
                    'type' => 'message',
                    'content' => array(
                        array(
                            'type' => 'output_text',
                            'text' => 'The current temperature is 69°F.',
                        ),
                    ),
                ),
            ),
        );

        $response = $mapper->mapResponse($payload);

        $this->assertSame('openai', $response->provider());
        $this->assertSame('resp_123', $response->id());
        $this->assertSame('gpt-5.1', $response->model());
        $this->assertSame('completed', $response->status());
        $this->assertSame('The current temperature is 69°F.', $response->text());
        $this->assertCount(1, $response->toolCalls());
        $this->assertSame('call_123', $response->toolCalls()[0]->id());
        $this->assertSame('get_current_temperature', $response->toolCalls()[0]->name());
        $this->assertSame('Atlanta, GA', $response->toolCalls()[0]->arguments()['location']);
        $this->assertSame(21, $response->inputTokens());
        $this->assertSame(23, $response->outputTokens());
        $this->assertSame(44, $response->totalTokens());
        $this->assertSame(4, $response->cachedInputTokens());
        $this->assertSame(7, $response->reasoningTokens());

        $expectedUsage = array(
            'input_tokens' => 21,
            'cached_input_tokens' => 4,
            'output_tokens' => 23,
            'reasoning_tokens' => 7,
            'total_tokens' => 44,
            'raw' => $payload['usage'],
        );
        $actualUsage = $response->usage();
        ksort($expectedUsage);
        ksort($actualUsage);

        $this->assertSame($expectedUsage, $actualUsage);
        $this->assertSame($payload['usage'], $response->rawUsage());
    }
}
