<?php
/**
 * Test Web API error codes.
 *
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Magento\Webapi\Routing;

use Magento\TestFramework\Helper\Bootstrap;
use Magento\Framework\Webapi\Exception as WebapiException;

class RestErrorHandlingTest extends \Magento\TestFramework\TestCase\WebapiAbstract
{
    /**
     * @var string
     */
    protected $mode;

    protected function setUp(): void
    {
        $this->_markTestAsRestOnly();
        $this->mode = Bootstrap::getObjectManager()->get(\Magento\Framework\App\State::class)->getMode();
        parent::setUp();
    }

    public function testSuccess()
    {
        $serviceInfo = [
            'rest' => [
                'resourcePath' => '/V1/errortest/success',
                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
            ],
        ];

        $item = $this->_webApiCall($serviceInfo);

        // TODO: check Http Status = 200, cannot do yet due to missing header info returned

        $this->assertEquals('a good id', $item['value'], 'Success case is correct');
    }

    public function testNotFound()
    {
        $serviceInfo = [
            'rest' => [
                'resourcePath' => '/V1/errortest/notfound',
                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
            ],
        ];

        // \Magento\Framework\Api\ResourceNotFoundException
        $this->_errorTest(
            $serviceInfo,
            ['resourceY'],
            WebapiException::HTTP_NOT_FOUND,
            'Resource with ID "%1" not found.'
        );
    }

    public function testUnauthorized()
    {
        $serviceInfo = [
            'rest' => [
                'resourcePath' => '/V1/errortest/unauthorized',
                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
            ],
        ];

        // \Magento\Framework\Api\AuthorizationException
        $this->_errorTest(
            $serviceInfo,
            [],
            WebapiException::HTTP_UNAUTHORIZED,
            "The consumer isn't authorized to access %1.",
            ['resourceN']
        );
    }

    public function testOtherException()
    {
        $serviceInfo = [
            'rest' => [
                'resourcePath' => '/V1/errortest/otherException',
                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
            ],
        ];

        /* TODO : Fix as part MAGETWO-31330
        $expectedMessage = $this->mode == \Magento\Framework\App\State::MODE_DEVELOPER
            ? 'Non service exception'
            : 'Internal Error. Details are available in Magento log file. Report ID: webapi-XXX';
        */
        $expectedMessage = 'Internal Error. Details are available in Magento log file. Report ID: webapi-XXX';
        $this->_errorTest(
            $serviceInfo,
            [],
            WebapiException::HTTP_INTERNAL_ERROR,
            $expectedMessage,
            null,
            'Magento\TestModule3\Service\V1\Error->otherException()' // Check if trace contains proper error source
        );
    }

    /**
     * Perform a negative REST api call test case and compare the results with expected values.
     *
     * @param string $serviceInfo - REST Service information (i.e. resource path and HTTP method)
     * @param array $data - Data for the cal
     * @param int $httpStatus - Expected HTTP status
     * @param string|array $errorMessage - \Exception error message
     * @param array $parameters - Optional parameters array, or null if no parameters
     * @param string $traceString - Optional trace string to verify
     */
    protected function _errorTest(
        $serviceInfo,
        $data,
        $httpStatus,
        $errorMessage,
        $parameters = [],
        $traceString = null
    ) {
        try {
            $this->_webApiCall($serviceInfo, $data);
        } catch (\Exception $e) {
            $this->assertEquals($httpStatus, $e->getCode(), 'Checking HTTP status code');

            $body = json_decode($e->getMessage(), true);

            $errorMessages = is_array($errorMessage) ? $errorMessage : [$errorMessage];
            $actualMessage = $body['message'];
            $matches = [];
            //Report ID was created dynamically, so we need to replace it with some static value in order to test
            if (preg_match('/.*Report\sID\:\s([a-zA-Z0-9\-]*)/', $actualMessage, $matches)) {
                $actualMessage = str_replace($matches[1], 'webapi-XXX', $actualMessage);
            }
            //make sure that the match for a report with an id is found if Internal error was reported
            //Refer : \Magento\Framework\Webapi\ErrorProcessor::INTERNAL_SERVER_ERROR_MSG
            if (count($matches) > 1) {
                $this->assertTrue(!empty($matches[1]), 'Report id missing for internal error.');
            }
            $this->assertContains(
                $actualMessage,
                $errorMessages,
                "Message is invalid. Actual: '{$actualMessage}'. Expected one of: {'" . implode(
                    "', '",
                    $errorMessages
                ) . "'}"
            );

            if ($parameters) {
                $this->assertEquals($parameters, $body['parameters'], 'Checking body parameters');
            }

            if ($this->mode == \Magento\Framework\App\State::MODE_DEVELOPER && $traceString) {
                // TODO : Fix as part MAGETWO-31330
                //$this->assertContains($traceString, $body['trace'], 'Trace information is incorrect.');
            }
        }
    }
}
