<?php
namespace Aws\Test\Api\ErrorParser;

use Aws\Api\ErrorParser\RestJsonErrorParser;
use Aws\Test\TestServiceTrait;
use GuzzleHttp\Psr7;
use PHPUnit\Framework\TestCase;

/**
 * @covers \Aws\Api\ErrorParser\RestJsonErrorParser
 * @covers \Aws\Api\ErrorParser\JsonParserTrait
 */
class RestJsonErrorParserTest extends TestCase
{
    use TestServiceTrait;

    /**
     * @dataProvider errorResponsesProvider
     *
     * @param $response
     * @param $command
     * @param $parser
     * @param $expected
     */
    public function testParsesClientErrorResponses(
        $response,
        $command,
        $parser,
        $expected
    ) {
        $response = Psr7\Message::parseResponse($response);
        $parsed = $parser($response, $command);
        $this->assertCount(
            count($expected),
            $parsed
        );
        foreach($parsed as $key => $value) {
            if ($key === 'error_shape') {
                $this->assertEquals(
                    $expected['error_shape']->toArray(),
                    $value->toArray()
                );
            } else {
                $this->assertEquals($expected[$key], $value);
            }
        }
    }

    public function errorResponsesProvider()
    {
        $service = $this->generateTestService('rest-json');
        $shapes = $service->getErrorShapes();
        $errorShape = $shapes[0];
        $client = $this->generateTestClient($service);
        $command = $client->getCommand('TestOperation', []);

        return [
            // Error code in body
            [
                "HTTP/1.1 400 Bad Request\r\n" .
                "x-amzn-requestid: xyz\r\n\r\n" .
                '{ "type": "client", "message": "lorem ipsum", "code": "foo" }',
                null,
                new RestJsonErrorParser(),
                [
                    'code'       => 'foo',
                    'message'    => 'lorem ipsum',
                    'type'       => 'client',
                    'request_id' => 'xyz',
                    'parsed'     => [
                        'type'    => 'client',
                        'message' => 'lorem ipsum',
                        'code'    => 'foo'
                    ],
                    'body' => [],
                ]
            ],
            // Error code in header
            [
                "HTTP/1.1 400 Bad Request\r\n" .
                "x-amzn-RequestId: xyz\r\n" .
                "x-amzn-ErrorType: foo:bar\r\n\r\n" .
                '{"message": "lorem ipsum"}',
                null,
                new RestJsonErrorParser(),
                [
                    'code'       => 'foo',
                    'message'    => 'lorem ipsum',
                    'type'       => 'client',
                    'request_id' => 'xyz',
                    'parsed'     => [
                        'message' => 'lorem ipsum',
                    ],
                    'body' => [],
                ]
            ],
            // Error code in body, with service, modeled exception
            [
                "HTTP/1.1 400 Bad Request\r\n" .
                "TestHeader: foo-header\r\n" .
                "x-meta-foo: foo-meta\r\n" .
                "x-meta-bar: bar-meta\r\n" .
                "x-amzn-requestid: xyz\r\n\r\n" .
                '{ "TestString": "foo", "TestInt": 123, "NotModeled": "bar", "code": "TestException" }',
                $command,
                new RestJsonErrorParser($service),
                [
                    'code'       => 'TestException',
                    'type'       => 'client',
                    'request_id' => 'xyz',
                    'parsed'     => [
                        'TestString'    => 'foo',
                        'TestInt'       => 123,
                        'NotModeled'    => 'bar',
                        'code'          => 'TestException',
                    ],
                    'TestString' => 'foo',
                    'TestInt'       => 123,
                    'NotModeled'    => 'bar',
                    'body' => [
                        'TestString'        => 'foo',
                        'TestInt'           => 123,
                        'TestHeaderMember'  => 'foo-header',
                        'TestHeaders'       => [
                            'foo' => 'foo-meta',
                            'bar' => 'bar-meta',
                        ],
                        'TestStatus'        => 400,
                    ],
                    'message' => null,
                    'error_shape' => $errorShape
                ]
            ],
            // Error code in header, with service, modeled exception
            [
                "HTTP/1.1 400 Bad Request\r\n" .
                "TestHeader: foo-header\r\n" .
                "x-meta-foo: foo-meta\r\n" .
                "x-meta-bar: bar-meta\r\n" .
                "x-amzn-ErrorType: TestException\r\n" .
                "x-amzn-requestid: xyz\r\n\r\n" .
                '{ "TestString": "foo", "TestInt": 123, "NotModeled": "bar"}',
                $command,
                new RestJsonErrorParser($service),
                [
                    'code'       => 'TestException',
                    'type'       => 'client',
                    'request_id' => 'xyz',
                    'parsed'     => [
                        'TestString'    => 'foo',
                        'TestInt'       => 123,
                        'NotModeled'    => 'bar'
                    ],
                    'TestString' => 'foo',
                    'TestInt'       => 123,
                    'NotModeled'    => 'bar',
                    'body' => [
                        'TestString'        => 'foo',
                        'TestInt'           => 123,
                        'TestHeaderMember'  => 'foo-header',
                        'TestHeaders'       => [
                            'foo' => 'foo-meta',
                            'bar' => 'bar-meta',
                        ],
                        'TestStatus'        => 400,
                    ],
                    'message' => null,
                    'error_shape' => $errorShape
                ]
            ],
            // Error code in header, with service, unmodeled code
            [
                "HTTP/1.1 400 Bad Request\r\n" .
                "x-amzn-RequestId: xyz\r\n" .
                "x-amzn-ErrorType: NonExistentException\r\n\r\n" .
                '{"message": "lorem ipsum"}',
                null,
                new RestJsonErrorParser($service),
                [
                    'code'       => 'NonExistentException',
                    'message'    => 'lorem ipsum',
                    'type'       => 'client',
                    'request_id' => 'xyz',
                    'parsed'     => [
                        'message' => 'lorem ipsum',
                    ],
                    'body' => [],
                ]
            ],
            // Error code in body, with service, unmodeled code
            [
                "HTTP/1.1 400 Bad Request\r\n" .
                "x-amzn-requestid: xyz\r\n\r\n" .
                '{ "type": "client", "message": "lorem ipsum", "code": "NonExistentException" }',
                null,
                new RestJsonErrorParser($service),
                [
                    'code'       => 'NonExistentException',
                    'message'    => 'lorem ipsum',
                    'type'       => 'client',
                    'request_id' => 'xyz',
                    'parsed'     => [
                        'type'    => 'client',
                        'message' => 'lorem ipsum',
                        'code'    => 'NonExistentException'
                    ],
                    'body' => [],
                ]
            ],
            // Error code in body, with service, unmodeled code, capitalized message
            [
                "HTTP/1.1 400 Bad Request\r\n" .
                "x-amzn-requestid: xyz\r\n\r\n" .
                '{ "type": "client", "Message": "lorem ipsum", "code": "NonExistentException" }',
                null,
                new RestJsonErrorParser($service),
                [
                    'code'       => 'NonExistentException',
                    'message'    => 'lorem ipsum',
                    'Message'    => 'lorem ipsum',
                    'type'       => 'client',
                    'request_id' => 'xyz',
                    'parsed'     => [
                        'type'    => 'client',
                        'Message' => 'lorem ipsum',
                        'code'    => 'NonExistentException'
                    ],
                    'body' => [],
                ]
            ],
            // Test zero value in header
            [
                "HTTP/1.1 400 Bad Request\r\n" .
                "TestHeader: 0\r\n" .
                "x-amzn-requestid: xyz\r\n\r\n" .
                '{ "code": "TestException" }',
                $command,
                new RestJsonErrorParser($service),
                [
                    'code'       => 'TestException',
                    'type'       => 'client',
                    'request_id' => 'xyz',
                    'parsed'     => ['code' => 'TestException'],
                    'body' => [
                        'TestHeaderMember'  => '0',  // Zero preserved
                        'TestHeaders'       => [],   // Empty array
                        'TestStatus'        => 400,
                    ],
                    'message' => null,
                    'error_shape' => $errorShape
                ]
            ],
            // Test false value in header
            [
                "HTTP/1.1 400 Bad Request\r\n" .
                "TestHeader: false\r\n" .
                "x-amzn-requestid: xyz\r\n\r\n" .
                '{ "code": "TestException" }',
                $command,
                new RestJsonErrorParser($service),
                [
                    'code'       => 'TestException',
                    'type'       => 'client',
                    'request_id' => 'xyz',
                    'parsed'     => ['code' => 'TestException'],
                    'body' => [
                        'TestHeaderMember'  => 'false',  // False preserved
                        'TestHeaders'       => [],        // Empty array
                        'TestStatus'        => 400,
                    ],
                    'message' => null,
                    'error_shape' => $errorShape
                ]
            ],
            // Test empty string in header (should be skipped)
            [
                "HTTP/1.1 400 Bad Request\r\n" .
                "TestHeader: \r\n" .
                "x-amzn-requestid: xyz\r\n\r\n" .
                '{ "code": "TestException" }',
                $command,
                new RestJsonErrorParser($service),
                [
                    'code'       => 'TestException',
                    'type'       => 'client',
                    'request_id' => 'xyz',
                    'parsed'     => ['code' => 'TestException'],
                    'body' => [
                        // TestHeaderMember should NOT be present
                        'TestHeaders'       => [],   // Empty array
                        'TestStatus'        => 400,
                    ],
                    'message' => null,
                    'error_shape' => $errorShape
                ]
            ]
        ];
    }
}
