<?php

namespace Smartling\Vendor\Smartling\Tests;
use Smartling\Vendor\Smartling\Context\ContextApi;
use Smartling\Vendor\Smartling\Context\Params\MatchContextParameters;
use Smartling\Vendor\Smartling\Context\Params\MissingResourcesParameters;
use Smartling\Vendor\Smartling\Context\Params\UpdateResourceStateParameters;
use Smartling\Vendor\Smartling\Context\Params\UploadContextParameters;
use Smartling\Vendor\Smartling\Exceptions\SmartlingApiException;
use Smartling\Vendor\Smartling\Tests\Unit\ApiTestAbstract;
use Smartling\Vendor\Smartling\Context\Params\UploadResourceParameters;


/**
 * Test class for Smartling\Vendor\Smartling\Context\ContextApi.
 */
class ContextApiTest extends ApiTestAbstract
{

    /**
     * Sets up the fixture, for example, opens a network connection.
     * This method is called before a test is executed.
     */
    protected function setUp(): void
    {
        parent::setUp();
        $this->prepareContextApiMock();
    }

    private function prepareContextApiMock()
    {
        $this->object = $this->getMockBuilder('Smartling\Vendor\Smartling\Context\ContextApi')
            ->setMethods(['readFile'])
            ->setConstructorArgs([
                $this->projectId,
                $this->client,
                null,
                ContextApi::ENDPOINT_URL,
            ])
            ->getMock();

        $this->object->expects(self::any())
            ->method('readFile')
            ->willReturn($this->streamPlaceholder);

        $this->invokeMethod(
            $this->object,
            'setAuth',
            [
                $this->authProvider
            ]
        );
    }

    /**
     * @covers \Smartling\Vendor\Smartling\Context\ContextApi::uploadContext
     */
    public function testUploadContext() {
        $params = new UploadContextParameters();
        $params->setContent('./tests/resources/context.html');
        $params->setName('test_context.html');
        $endpointUrl = \vsprintf('%s/%s/contexts', [
            ContextApi::ENDPOINT_URL,
            $this->projectId
        ]);

        $this->client
            ->expects(self::once())
            ->method('request')
            ->with('post', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                    'X-SL-Context-Source' => $this->invokeMethod($this->object, 'getXSLContextSourceHeader'),
                ],
                'exceptions' => FALSE,
                'multipart' => [
                    [
                        'name' => 'content',
                        'contents' => $this->streamPlaceholder,
                    ],
                    [
                        'name' => 'name',
                        'contents' => 'test_context.html',
                    ],
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->uploadContext($params);
    }

    /**
     * @covers \Smartling\Vendor\Smartling\Context\ContextApi::uploadContext
     */
    public function testMatchContext() {
        $fileUri = './tests/resources/context.html';
        $contextUid = 'someContextUid';

        $params = new MatchContextParameters();
        $params->setContentFileUri($fileUri);

        $endpointUrl = \vsprintf('%s/%s/contexts/%s/match/async', [
            ContextApi::ENDPOINT_URL,
            $this->projectId,
            $contextUid
        ]);

        $this->client
            ->expects(self::once())
            ->method('request')
            ->with('post', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                    'X-SL-Context-Source' => $this->invokeMethod($this->object, 'getXSLContextSourceHeader'),
              ],
              'exceptions' => FALSE,
              'json' => [
                  'contentFileUri' => $fileUri,
              ],
            ])
            ->willReturn($this->responseMock);

        $this->object->matchContext($contextUid, $params);
    }

    /**
     * @covers \Smartling\Vendor\Smartling\Context\ContextApi::uploadAndMatchContext
     */
    public function testUploadAndMatchContext() {
        $fileUri = './tests/resources/context.html';

        $matchParams = new MatchContextParameters();
        $matchParams->setContentFileUri($fileUri);

        $params = new UploadContextParameters();
        $params->setContent($fileUri);
        $params->setMatchParams($matchParams);

        $endpointUrl = \vsprintf('%s/%s/contexts/upload-and-match-async', [
            ContextApi::ENDPOINT_URL,
            $this->projectId
        ]);

        $this->client
            ->expects(self::once())
            ->method('request')
            ->with('post', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                    'X-SL-Context-Source' => $this->invokeMethod($this->object, 'getXSLContextSourceHeader'),
                ],
                'exceptions' => FALSE,
                'multipart' => [
                    [
                        'name' => 'content',
                        'contents' => $this->streamPlaceholder,
                    ],
                    [
                        'name' => 'matchParams',
                        'contents' => '{"contentFileUri":".\/tests\/resources\/context.html"}',
                        'headers' => [
                            'Content-Type' => 'application/json',
                        ]
                    ],
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->uploadAndMatchContext($params);
    }

    /**
     * @covers \Smartling\Vendor\Smartling\Context\ContextApi::uploadAndMatchContext
     */
    public function testUploadAndMatchContextWithDataProtocol() {
        $fileUri = './tests/resources/context.html';
        $name = 'test_context.html';

        $matchParams = new MatchContextParameters();
        $matchParams->setContentFileUri($fileUri);

        $params = new UploadContextParameters();
        $params->setContent(sprintf('data://text/html;base64,%s', base64_encode(file_get_contents($fileUri))));
        $params->setName($name);
        $params->setMatchParams($matchParams);

        $endpointUrl = vsprintf('%s/%s/contexts/upload-and-match-async', [
            ContextApi::ENDPOINT_URL,
            $this->projectId
        ]);

        $this->client
            ->expects(self::once())
            ->method('request')
            ->with('post', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                    'X-SL-Context-Source' => $this->invokeMethod($this->object, 'getXSLContextSourceHeader'),
                ],
                'exceptions' => FALSE,
                'multipart' => [
                    [
                        'name' => 'content',
                        'contents' => $this->streamPlaceholder,
                        'filename' => $name,
                    ],
                    [
                        'name' => 'name',
                        'contents' => $name,
                    ],
                    [
                        'name' => 'matchParams',
                        'contents' => '{"contentFileUri":".\/tests\/resources\/context.html"}',
                        'headers' => [
                            'Content-Type' => 'application/json',
                        ]
                    ],
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->uploadAndMatchContext($params);
    }

    /**
     * @covers \Smartling\Vendor\Smartling\Context\ContextApi::uploadAndMatchContext
     */
    public function testUploadAndMatchContextWithDataProtocolFailsWhenNoNameSupplied() {
        $fileUri = './tests/resources/context.html';

        $matchParams = new MatchContextParameters();
        $matchParams->setContentFileUri($fileUri);

        $params = new UploadContextParameters();
        $params->setContent(sprintf('data://text/html;base64,%s', base64_encode(file_get_contents($fileUri))));
        $params->setMatchParams($matchParams);

        $this->expectException(SmartlingApiException::class);
        $this->expectExceptionMessage('When content is specified using the data:// protocol, name is a required request body field');
        $this->object->uploadAndMatchContext($params);
    }

    /**
     * @covers \Smartling\Vendor\Smartling\Context\ContextApi::getMissingResources
     */
    public function testGetMatchStatus() {
        $matchId = 'test_match_id';
        $endpointUrl = \vsprintf('%s/%s/processes/%s', [
            ContextApi::ENDPOINT_URL,
            $this->projectId,
            $matchId,
        ]);

        $this->client
            ->expects(self::once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                    'X-SL-Context-Source' => $this->invokeMethod($this->object, 'getXSLContextSourceHeader'),
                ],
                'exceptions' => FALSE,
                'query' => [],
            ])
            ->willReturn($this->responseMock);

        $this->object->getMatchStatus($matchId);
    }

    /**
     * @covers \Smartling\Vendor\Smartling\Context\ContextApi::getMissingResources
     */
    public function testGetMissingResources() {
        $offset = 'some_offset';
        $params = new MissingResourcesParameters();
        $params->setOffset($offset);
        $endpointUrl = \vsprintf('%s/%s/missing-resources', [
            ContextApi::ENDPOINT_URL,
            $this->projectId
        ]);

        $this->client
            ->expects(self::once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                    'X-SL-Context-Source' => $this->invokeMethod($this->object, 'getXSLContextSourceHeader'),
                ],
                'exceptions' => FALSE,
                'query' => [
                    'offset' => $offset,
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->getMissingResources($params);
    }

    /**
     * @covers \Smartling\Vendor\Smartling\Context\ContextApi::uploadResource
     */
    public function testUploadResource() {
        $resourceId = 'some_resource_id';
        $params = new UploadResourceParameters();
        $params->setFile('./tests/resources/test.png');
        $endpointUrl = \vsprintf('%s/%s/resources/%s', [
            ContextApi::ENDPOINT_URL,
            $this->projectId,
            $resourceId,
        ]);

        $this->client
            ->expects(self::once())
            ->method('request')
            ->with('put', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                    'X-SL-Context-Source' => $this->invokeMethod($this->object, 'getXSLContextSourceHeader'),
                ],
                'exceptions' => FALSE,
                'multipart' => [
                    [
                        'name' => 'resource',
                        'contents' => $this->streamPlaceholder,
                    ],
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->uploadResource($resourceId, $params);
    }

    /**
     * @covers \Smartling\Vendor\Smartling\Context\ContextApi::updateResourceState
     */
    public function testUpdateResourceState() {
        $resourceId = 'some_resource_id';
        $params = new UpdateResourceStateParameters();
        $params->setState(UpdateResourceStateParameters::STATE_FAILED);
        $endpointUrl = \vsprintf('%s/%s/resources/%s/state', [
            ContextApi::ENDPOINT_URL,
            $this->projectId,
            $resourceId,
        ]);

        $this->client
            ->expects(self::once())
            ->method('request')
            ->with('put', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                    'X-SL-Context-Source' => $this->invokeMethod($this->object, 'getXSLContextSourceHeader'),
                ],
                'exceptions' => FALSE,
                'json' => [
                    'state' => UpdateResourceStateParameters::STATE_FAILED
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->updateResourceState($resourceId, $params);
    }

    /**
     * @covers \Smartling\Vendor\Smartling\Context\ContextApi::renderContext
     */
    public function testRenderContext() {
        $contextUid = 'someContextUid';
        $endpointUrl = \vsprintf('%s/%s/contexts/%s/render', [
            ContextApi::ENDPOINT_URL,
            $this->projectId,
            $contextUid
        ]);

        $this->client
            ->expects(self::once())
            ->method('request')
            ->with('post', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                    'X-SL-Context-Source' => $this->invokeMethod($this->object, 'getXSLContextSourceHeader'),
                ],
                'exceptions' => FALSE,
                'form_params' => [],
            ])
            ->willReturn($this->responseMock);

        $this->object->renderContext($contextUid);
    }

}
