<?php

namespace Smartling\Vendor\Smartling\Tests\Unit;

use PHPUnit\Framework\MockObject\MockObject;
use Smartling\Vendor\Smartling\Exceptions\SmartlingApiException;
use Smartling\Vendor\Smartling\File\FileApi;
use Smartling\Vendor\Smartling\File\Params\DownloadFileParameters;
use Smartling\Vendor\Smartling\File\Params\DownloadMultipleFilesParameters;
use Smartling\Vendor\Smartling\File\Params\UploadFileParameters;

/**
 * Test class for Smartling\Vendor\Smartling\File\FileApi.
 */
class FileApiTest extends ApiTestAbstract
{
    private function prepareFileApiMock()
    {
        $this->object = $this->getMockBuilder('Smartling\Vendor\Smartling\File\FileApi')
            ->setMethods(['readFile'])
            ->setConstructorArgs([
                $this->projectId,
                $this->client,
                null,
                FileApi::ENDPOINT_URL,
            ])
            ->getMock();

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

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

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

    /**
     * Tests constructor.
     *
     * @param string $projectId
     *   Project Id string.
     * @param \Smartling\Vendor\GuzzleHttp\ClientInterface $client
     *   Mock of Guzzle http client instance.
     * @param string|null $expected_base_url
     *   Base Url string that will be used as based url.
     *
     * @covers \Smartling\Vendor\Smartling\File\FileApi::__construct
     *
     * @dataProvider constructorDataProvider
     */
    public function testConstructor($projectId, $client, $expected_base_url)
    {
        $this->prepareClientResponseMock();
        $fileApi = new FileApi($projectId, $client, null, $expected_base_url);

        $this->assertEquals(\rtrim($expected_base_url, '/') . '/' . $projectId,
            $this->invokeMethod($fileApi, 'getBaseUrl'));
        $this->assertEquals($projectId, $this->invokeMethod($fileApi, 'getProjectId'));
        $this->assertEquals($client, $this->invokeMethod($fileApi, 'getHttpClient'));
    }

    /**
     * Data provider for testConstructor method.
     *
     * Tests if base url will be set correctly depending on income baseurl
     * and mode.
     *
     * @return array
     */
    public function constructorDataProvider()
    {
        $this->prepareHttpClientMock();

        $mockedClient = $this->client;

        return [
            ['product-id', $mockedClient, FileApi::ENDPOINT_URL],
            ['product-id', $mockedClient, FileApi::ENDPOINT_URL],
            ['product-id', $mockedClient, FileApi::ENDPOINT_URL . '/'],
            ['product-id', $mockedClient, 'https://www.google.com.ua/webhp'],
        ];
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::uploadFile
     */
    public function testUploadFile()
    {
        $this->prepareClientResponseMock();
        $this->client
            ->expects($this->once())
            ->method('request')
            ->willReturnCallback(function(string $method, string $uri, array $options) {
                $this->assertEquals('post', $method);
                $this->assertEquals(FileApi::ENDPOINT_URL . '/' . $this->projectId . '/file', $uri);
                $this->assertEquals([
                    'headers' => [
                        'Accept' => 'application/json',
                        'Authorization' => \vsprintf('%s %s', [
                            $this->authProvider->getTokenType(),
                            $this->authProvider->getAccessToken(),
                        ]),
                    ],
                    'exceptions' => false,
                    'multipart' => [
                        [
                            'name' => 'authorize',
                            'contents' => 0,
                        ],
                        [
                            'name' => 'smartling.client_lib_id',
                            'contents' => '{"client":"smartling-api-sdk-php","version":"4.0.7"}',
                        ],
                        [
                            'name' => 'localeIdsToAuthorize[]',
                            'contents' => 'es',
                        ],
                        [
                            'name' => 'smartling.namespace',
                            'contents' => 'namespace_for_deduplication',
                        ],
                        [
                            'name' => 'file',
                            'contents' => $this->streamPlaceholder,
                            'filename' => FileApi::FILE_NAME_NOT_SET
                        ],
                        [
                            'name' => 'fileUri',
                            'contents' => 'test.xml',
                        ],
                        [
                            'name' => 'fileType',
                            'contents' => 'xml',
                        ],
                    ],
                ], $options);
                return $this->responseMock;
            });

        $params = new UploadFileParameters();
        $params->setAuthorized(true);
        $params->setLocalesToApprove(['es']);
        $params->setNamespace('namespace_for_deduplication');

        $this->object->uploadFile('tests/resources/test.xml', 'test.xml', 'xml', $params);
    }


    /**
     * Tests AutoAuthorize logic
     */
    public function testFileUploadParams()
    {
        $this->prepareClientResponseMock();
        $fileUploadParams = new UploadFileParameters();

        $fileUploadParams->setAuthorized(false);
        $exportedSettings = $fileUploadParams->exportToArray();
        $this->assertEquals($exportedSettings['authorize'], false);

        $fileUploadParams->setAuthorized(true);
        $exportedSettings = $fileUploadParams->exportToArray();
        $this->assertEquals($exportedSettings['authorize'], true);

        $fileUploadParams->setLocalesToApprove(['locale']);

        $fileUploadParams->setAuthorized(false);
        $exportedSettings = $fileUploadParams->exportToArray();
        $this->assertEquals($exportedSettings['authorize'], false);

        $fileUploadParams->setAuthorized(true);
        $exportedSettings = $fileUploadParams->exportToArray();
        $this->assertEquals($exportedSettings['authorize'], false);

        $fileUploadParams->setNamespace('namespace_for_deduplication');
        $exportedSettings = $fileUploadParams->exportToArray();
        $this->assertEquals($exportedSettings['smartling.namespace'], 'namespace_for_deduplication');
    }

    /**
     * Tests failures of file upload parameters namespace logic
     *
     * @dataProvider invalidFileUploadNamespaceParams
     *
     * @param mixed $invalidValue
     */
    public function testFileUploadParamsNamespaceFailures($invalidValue)
    {
        $fileUploadParams = new UploadFileParameters();
        $this->expectException(SmartlingApiException::class);
        $this->expectExceptionMessage('Invalid namespace found, not a string or empty string');
        $fileUploadParams->setNamespace($invalidValue);
    }

    public function invalidFileUploadNamespaceParams() {
        return [
            [''],
            [[]],
            [null],
        ];
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::downloadFile
     *
     * @dataProvider downloadFileParams
     *
     * @param DownloadFileParameters|null $options
     * @param string $expected_translated_file
     */
    public function testDownloadFile($options, $locale, $expected_translated_file)
    {
        $this->responseMock = $this->getResponse($expected_translated_file);

        $endpointUrl = \vsprintf(
            '%s/%s/locales/%s/file',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId,
                $locale
            ]
        );

        $params = $options instanceof DownloadFileParameters
            ? $options->exportToArray()
            : [];

        $params['fileUri'] = 'test.xml';

        $this->client
            ->expects($this->once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'query' => $params,
            ])
            ->willReturn($this->responseMock);

        $actual_xml = $this->object->downloadFile('test.xml', $locale, $options);

        $this->assertEquals($expected_translated_file, $actual_xml);
    }

    public function downloadFileParams()
    {
        return [
            [
                (new DownloadFileParameters())->setRetrievalType(DownloadFileParameters::RETRIEVAL_TYPE_PSEUDO),
                'en-EN',
                '<?xml version="1.0"?><response><item key="6"></item></response>'
            ],
            [
                null,
                'en-EN',
                '<?xml version="1.0"?><response><item key="6"></item></response>'
            ],
            [
                null,
                'en',
                '{"string1":"translation1", "string2":"translation2"}'
            ],
        ];
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::downloadAllTranslationsOfFile
     */
    public function testDownloadAllTranslationsOfFile()
    {
        $this->prepareClientResponseMock(false);

        $endpointUrl = \vsprintf(
            '%s/%s/locales/all/file/zip',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId
            ]
        );

        $params = new DownloadFileParameters();
        $params->setRetrievalType(DownloadFileParameters::RETRIEVAL_TYPE_PSEUDO);

        $this->client
            ->expects($this->once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'query' => [
                    'retrievalType' => 'pseudo',
                    'fileUri' => 'test.xml'
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->downloadAllTranslationsOfFile('test.xml', $params);
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::downloadMultipleTranslationsOfFiles
     */
    public function testDownloadMultipleTranslationsOfFiles()
    {
        $this->prepareClientResponseMock(false);

        $endpointUrl = \vsprintf(
            '%s/%s/files/zip',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId
            ]
        );

        $params = new DownloadMultipleFilesParameters();
        $params->setRetrievalType(DownloadMultipleFilesParameters::RETRIEVAL_TYPE_PSEUDO);
        $params->setFileNameMode(DownloadMultipleFilesParameters::FILE_NAME_MODE_LOCALE_LAST);
        $params->setLocaleMode(DownloadMultipleFilesParameters::LOCALE_MODE_LOCALE_IN_NAME_AND_PATH);
        $params->setFileUris([
            "test1.xml",
            "test2.xml"
        ]);
        $params->setLocaleIds([
            "fr",
            "de"
        ]);
        $params->setIncludeOriginalStrings(true);
        $params->setZipFileName("zipFileName.zip");

        $this->client
            ->expects($this->once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'query' => 'retrievalType=pseudo&fileNameMode=LOCALE_LAST&localeMode=LOCALE_IN_NAME_AND_PATH&fileUris[]=test1.xml&fileUris[]=test2.xml&localeIds[]=fr&localeIds[]=de&includeOriginalStrings=1&zipFileName=zipFileName.zip',
            ])
            ->willReturn($this->responseMock);

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

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::getDefaultRequestData
     */
    public function testGetDefaultRequestDataMethodUrlEncoding()
    {
        $this->prepareClientResponseMock(false);

        $endpointUrl = \vsprintf(
            '%s/%s/files/zip',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId
            ]
        );

        $params = new DownloadMultipleFilesParameters();
        $params->setFileUris([
            "123:/content/page.html#anchor?param1=!&param2=*"
        ]);

        $this->client
            ->expects($this->once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'query' => 'fileUris[]=123%3A%2Fcontent%2Fpage.html%23anchor%3Fparam1%3D%21%26param2%3D%2A',
            ])
            ->willReturn($this->responseMock);

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

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::downloadFile
     * @dataProvider downloadFileLocaleCheckSuccessParams
     *
     * @param $options
     * @param $locale
     */
    public function testDownloadFileLocaleCheckFails($options, $locale)
    {
        $this->prepareClientResponseMock();
        $this->expectException(SmartlingApiException::class);
        $this->object->downloadFile('test.xml', $locale, $options);
    }

    public function downloadFileLocaleCheckSuccessParams()
    {
        return [
            [
                (new DownloadFileParameters())->setRetrievalType(DownloadFileParameters::RETRIEVAL_TYPE_PSEUDO),
                'e',
            ],
            [
                (new DownloadFileParameters())->setRetrievalType(DownloadFileParameters::RETRIEVAL_TYPE_PSEUDO),
                '',
            ],
            [
                (new DownloadFileParameters())->setRetrievalType(DownloadFileParameters::RETRIEVAL_TYPE_PSEUDO),
                [],
            ],
            [
                (new DownloadFileParameters())->setRetrievalType(DownloadFileParameters::RETRIEVAL_TYPE_PSEUDO),
                null,
            ],
            [
                (new DownloadFileParameters())->setRetrievalType(DownloadFileParameters::RETRIEVAL_TYPE_PSEUDO),
                (object)['foo'],
            ]
        ];
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::lastModified
     */
    public function testLastModifiedInvalidResponseNoDataFoundNoItems()
    {
        $this->responseMock = $this->getResponse('{"response":{"code":"SUCCESS","messages":[], "data":{}}}');

        $this->client->expects($this->once())
            ->method('request')
            ->willReturn($this->responseMock);

        $this->expectException(SmartlingApiException::class);
        $this->expectErrorMessage('No data found for file test.xml.');
        $this->object->lastModified('test.xml');
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::lastModified
     */
    public function testLastModifiedInvalidResponseNoDataFoundItemsNotArray()
    {
        $this->responseMock = $this->getResponse('{"response":{"code":"SUCCESS","messages":[], "data":{"totalCount":1629,"items": "not_array"}}}');

        $this->client->expects($this->once())
            ->method('request')
            ->willReturn($this->responseMock);

        $this->expectException(SmartlingApiException::class);
        $this->expectExceptionMessage('No data found for file test.xml.');
        $this->object->lastModified('test.xml');
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::lastModified
     */
    public function testLastModifiedInvalidResponseCantParseFormattedTimeString()
    {
        $this->responseMock = $this->getResponse('{"response":{"code":"SUCCESS","messages":[], "data":{"totalCount":1629,"items": [{"localId": "locale-test","lastModified": "test"}]}}}');

        $this->client->expects($this->once())
            ->method('request')
            ->willReturn($this->responseMock);

        $this->expectException(SmartlingApiException::class);
        $this->expectExceptionMessage("Can't parse formatted time string: test.");

        $this->object->lastModified('test.xml');
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::lastModified
     */
    public function testLastModifiedInvalidResponseLastModifiedIsNotSet()
    {
        $this->responseMock = $this->getResponse('{"response":{"code":"SUCCESS","messages":[], "data":{"totalCount":1629,"items": [{"localId": "locale-test"}]}}}');

        $this->client->expects($this->once())
            ->method('request')
            ->willReturn($this->responseMock);

        $this->expectException(SmartlingApiException::class);
        $this->expectExceptionMessage('Property "lastModified" is not found.');
        $this->object->lastModified('test.xml');
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::lastModified
     */
    public function testLastModifiedTimeZone()
    {
        $this->responseMock = $this->getResponse('{"response":{"code":"SUCCESS","messages":[], "data":{"totalCount":1629,"items": [{"localId": "locale-test","lastModified": "2018-01-01T00:00:00Z"}]}}}');

        $this->client->expects($this->exactly(2))
            ->method('request')
            ->willReturn($this->responseMock);

        \date_default_timezone_set('UTC');
        $result_default_utc = $this->object->lastModified('test.xml');
        \date_default_timezone_set('Pacific/Auckland');
        $result_default_auckland = $this->object->lastModified('test.xml');

        $this->assertEquals(
            $result_default_utc['items'][0]['lastModified']->getTimestamp(),
            $result_default_auckland['items'][0]['lastModified']->getTimestamp()
        );

        $this->assertEquals(
            $result_default_utc['items'][0]['lastModified']->getTimeZone()->getName(),
            'UTC'
        );

        $this->assertEquals(
            $result_default_auckland['items'][0]['lastModified']->getTimeZone()->getName(),
            'UTC'
        );
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::lastModified
     */
    public function testLastModified()
    {
        $this->responseMock = $this->getResponse('{"response":{"code":"SUCCESS","messages":[], "data":{"totalCount":1629,"items": [{"localId": "locale-test","lastModified": "1970-01-01T00:00:00Z"}]}}}');
        $endpointUrl = \vsprintf(
            '%s/%s/file/last-modified',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId
            ]
        );

        $this->client->expects($this->once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'query' => [
                    'fileUri' => 'test.xml',
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->lastModified('test.xml');
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::getStatusForAllLocales
     */
    public function testGetStatusForAllLocales()
    {
        $this->prepareClientResponseMock();
        $endpointUrl = \vsprintf(
            '%s/%s/file/status',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId
            ]
        );

        $this->client->expects($this->once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'query' => [
                    'fileUri' => 'test.xml',
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->getStatusForAllLocales('test.xml');
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::getStatus
     */
    public function testGetStatus()
    {
        $this->prepareClientResponseMock();
        $endpointUrl = \vsprintf(
            '%s/%s/locales/%s/file/status',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId,
                'en-EN'
            ]
        );

        $this->client->expects($this->once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'query' => [
                    'fileUri' => 'test.xml',
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->getStatus('test.xml', 'en-EN');
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::getList
     */
    public function testGetList()
    {
        $this->prepareClientResponseMock();
        $endpointUrl = \vsprintf(
            '%s/%s/files/list',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId
            ]
        );

        $this->client->expects($this->once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'query' => [],
            ])
            ->willReturn($this->responseMock);

        $this->object->getList();
    }


    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::getExtendedList
     */
    public function testGetExtendedList()
    {
        $this->prepareClientResponseMock();
        $locale = 'nl-NL';
        $endpointUrl = \vsprintf(
            '%s/%s/locales/%s/files/list',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId,
                $locale
            ]
        );

        $this->client->expects($this->once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'query' => [],
            ])
            ->willReturn($this->responseMock);

        $this->object->getExtendedList($locale);
    }


    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::sendRequest
     */
    public function testValidationErrorSendRequest()
    {
        $this->responseMock = $this->getResponse($this->responseWithException, 400);

        $endpointUrl = \vsprintf(
            '%s/%s/context/html',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId
            ]
        );

        $this->client->expects($this->once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'query' => [],
            ])
            ->willReturn($this->responseMock);

        $requestData = $this->invokeMethod($this->object, 'getDefaultRequestData', ['query', []]);
        $this->expectException(SmartlingApiException::class);

        $this->invokeMethod($this->object, 'setBaseUrl', [FileApi::ENDPOINT_URL . '/' . $this->projectId]);
        $this->invokeMethod($this->object, 'sendRequest', ['context/html', $requestData, 'get']);
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::sendRequest
     */
    public function testBadJsonFormatSendRequest()
    {
        $this->responseMock = $this->getResponse(\rtrim($this->responseWithException, '}'), 400);

        $endpointUrl = \vsprintf(
            '%s/%s/context/html',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId
            ]
        );

        $this->client->expects($this->once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'query' => [],
            ])
            ->willReturn($this->responseMock);

        $requestData = $this->invokeMethod($this->object, 'getDefaultRequestData', ['query', []]);
        $this->expectException(SmartlingApiException::class);
        $this->expectExceptionMessage('Bad response format from Smartling');

        $this->invokeMethod($this->object, 'setBaseUrl', [FileApi::ENDPOINT_URL . '/' . $this->projectId]);
        $this->invokeMethod($this->object, 'sendRequest', ['context/html', $requestData, 'get']);
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::sendRequest
     */
    public function testBadJsonFormatInErrorMessageSendRequest()
    {
        $this->responseMock = $this->getResponse(\rtrim($this->responseWithException, '}'), 400);

        $endpointUrl = \vsprintf(
            '%s/%s/context/html',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId
            ]
        );

        $this->client->expects($this->once())
            ->method('request')
            ->with('get', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'query' => [],
            ])
            ->willReturn($this->responseMock);

        $requestData = $this->invokeMethod($this->object, 'getDefaultRequestData', ['query', []]);
        $this->expectException(SmartlingApiException::class);
        $this->expectExceptionMessage('Bad response format from Smartling');

        $this->invokeMethod($this->object, 'setBaseUrl', [FileApi::ENDPOINT_URL . '/' . $this->projectId]);
        $this->invokeMethod($this->object, 'sendRequest', ['context/html', $requestData, 'get']);
    }

    /**
     * @param string $uri
     * @param array $requestData
     * @param string $method
     * @param array $params
     * @param $paramsType
     * @covers \Smartling\Vendor\Smartling\File\FileApi::sendRequest
     * @dataProvider sendRequestValidProvider
     */
    public function testSendRequest($uri, $requestData, $method, $params, $paramsType)
    {
        $this->prepareClientResponseMock();
        $defaultRequestData = $this->invokeMethod($this->object, 'getDefaultRequestData', [$paramsType, $requestData]);

        $params['headers']['Authorization'] = \vsprintf('%s %s', [
            $this->authProvider->getTokenType(),
            $this->authProvider->getAccessToken(),
        ]);

        $this->client->expects($this->once())
            ->method('request')
            ->willReturnCallback(function(string $actualMethod, string $actualUri, array $options) use ($method, $uri, $params) {
                $this->assertEquals($method, $actualMethod);
                $this->assertEquals(FileApi::ENDPOINT_URL . '/' . $this->projectId . '/' . $uri, $actualUri);
                $this->assertEquals($params, $options);
                return $this->responseMock;
            });

        $this->invokeMethod($this->object, 'setBaseUrl', [FileApi::ENDPOINT_URL . '/' . $this->projectId]);

        $result = $this->invokeMethod($this->object, 'sendRequest', [$uri, $defaultRequestData, $method]);
        self::assertEquals(['wordCount' => 1629, 'stringCount' => 503, 'overWritten' => false], $result);
    }

    /**
     * Data provider callback for testSendRequest method.
     *
     * @return array
     */
    public function sendRequestValidProvider()
    {
        return [
            [
                'uri',
                [],
                'get',
                [
                    'headers' => [
                        'Accept' => 'application/json',
                    ],
                    'exceptions' => false,
                    'query' => [],
                ],
                'query',
            ],
            [
                'uri',
                [
                    'key' => 'value',
                    'boolean_false' => false,
                    'boolean_true' => true,
                    'file' => './tests/resources/test.xml',
                ],
                'post',
                [
                    'headers' => [
                        'Accept' => 'application/json',
                    ],
                    'exceptions' => false,
                    'multipart' => [
                        [
                            'name' => 'key',
                            'contents' => 'value',
                        ],
                      [
                        'name' => 'boolean_false',
                        'contents' => '0',
                      ],
                      [
                        'name' => 'boolean_true',
                        'contents' => '1',
                      ],
                      [
                        'name' => 'file',
                        'contents' => $this->streamPlaceholder,
                        'filename' => FileApi::FILE_NAME_NOT_SET,
                      ],
                    ],
                ],
                'multipart',
            ],
        ];
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::renameFile
     */
    public function testRenameFile()
    {
        $this->prepareClientResponseMock();
        $endpointUrl = \vsprintf(
            '%s/%s/file/rename',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId
            ]
        );

        $this->client->expects($this->once())
            ->method('request')
            ->with('post', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'form_params' => [
                    'fileUri' => 'test.xml',
                    'newFileUri' => 'new_test.xml',
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->renameFile('test.xml', 'new_test.xml');
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::deleteFile
     */
    public function testDeleteFile()
    {
        $this->prepareClientResponseMock();
        $endpointUrl = \vsprintf('%s/%s/file/delete', [FileApi::ENDPOINT_URL, $this->projectId]);

        $this->client->expects($this->once())
            ->method('request')
            ->with('post', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'form_params' => [
                    'fileUri' => 'test.xml',
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->deleteFile('test.xml');
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::import
     */
    public function testImport()
    {
        $this->prepareClientResponseMock();
        $locale = 'en-EN';
        $endpointUrl = \vsprintf(
            '%s/%s/locales/%s/file/import',
            [
                FileApi::ENDPOINT_URL,
                $this->projectId,
                $locale
            ]
        );

        $this->client->expects($this->once())
            ->method('request')
            ->with('post', $endpointUrl, [
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => \vsprintf('%s %s', [
                        $this->authProvider->getTokenType(),
                        $this->authProvider->getAccessToken(),
                    ]),
                ],
                'exceptions' => false,
                'multipart' => [
                    [
                        'name' => 'fileUri',
                        'contents' => 'test.xml',
                    ],
                    [
                        'name' => 'fileType',
                        'contents' => 'xml',
                    ],
                    [
                        'name' => 'file',
                        'contents' => $this->streamPlaceholder,
                        'filename' => FileApi::FILE_NAME_NOT_SET
                    ],
                    [
                        'name' => 'translationState',
                        'contents' => 'PUBLISHED',
                    ],
                    [
                        'name' => 'overwrite',
                        'contents' => '0',
                    ],
                ],
            ])
            ->willReturn($this->responseMock);

        $this->object->import(
            $locale,
            'test.xml',
            'xml',
            'tests/resources/test.xml',
            'PUBLISHED',
            false
        );
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::readFile
     */
    public function testReadFile()
    {
        $this->prepareClientResponseMock();
        $validFilePath = './tests/resources/test.xml';

        /**
         * @var MockObject|FileApi
         */
        $fileApi = $this->getMockBuilder('Smartling\Vendor\Smartling\\Vendor\\Smartling\\File\\FileApi')
            ->setConstructorArgs([$this->projectId, $this->client])
            ->getMock();

        $stream = $this->invokeMethod($fileApi, 'readFile', [$validFilePath]);

        $this->assertEquals('stream', \get_resource_type($stream));
    }

    /**
     * @covers \Smartling\Vendor\Smartling\File\FileApi::readFile
     */
    public function testFailedReadFile()
    {
        $this->prepareClientResponseMock();
        $invalidFilePath = 'unexisted';

        /**
         * @var MockObject|FileApi
         */
        $fileApi = $this->getMockBuilder('Smartling\Vendor\Smartling\\Vendor\\Smartling\\File\\FileApi')
            ->setConstructorArgs([$this->projectId, $this->client])
            ->getMock();

        $this->expectException(SmartlingApiException::class);
        $this->expectExceptionMessage('File unexisted was not able to be read.');

        $stream = $this->invokeMethod($fileApi, 'readFile', [$invalidFilePath]);

        $this->assertEquals('stream', \get_resource_type($stream));
    }

    /**
     * Test async response with ACCEPTED code.
     *
     * It should not throw "Bad response format" exception.
     */
    public function testAcceptResponse() {
        $responseMock = $this->getResponse($this->responseAsync, 202);

        $this->client->expects(self::once())
            ->method('request')
            ->willReturn($responseMock);

        // Just random api call to mock async response of 'send' method.
        $this->object->renameFile('test.xml', 'new_test.xml');
    }
}
