<?php

namespace CrowdinApiClient\Api;

use CrowdinApiClient\Http\ResponseDecorator\ResponseArrayDecorator;
use CrowdinApiClient\Http\ResponseDecorator\ResponseModelListDecorator;
use CrowdinApiClient\Model\DownloadFile;
use CrowdinApiClient\Model\DownloadFileTranslation;
use CrowdinApiClient\Model\PreTranslation;
use CrowdinApiClient\Model\PreTranslationReport;
use CrowdinApiClient\Model\TranslationAlignment;
use CrowdinApiClient\Model\TranslationImport;
use CrowdinApiClient\Model\TranslationImportReport;
use CrowdinApiClient\Model\TranslationProjectBuild;
use CrowdinApiClient\Model\TranslationProjectDirectory;
use CrowdinApiClient\ModelCollection;

/**
 * Translators can work with entirely untranslated project or you can pre-translate the files to ease the translations process.
 * Use API to pre-translate files via Machine Translation or Translation Memory, upload your existing translations, and download translations.
 * Pre-translate and build are asynchronous operations and shall be completed with sequence of API methods.
 *
 * @package Crowdin\Api
 */
class TranslationApi extends AbstractApi
{
    /**
     * List Pre-Translations
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.pre-translations.getMany API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.pre-translations.getMany API Documentation Enterprise
     */
    public function listPreTranslations(int $projectId, array $params = []): ModelCollection
    {
        $path = sprintf('projects/%d/pre-translations', $projectId);
        return $this->_list($path, PreTranslation::class, $params);
    }

    /**
     * Apply Pre-Translation
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.pre-translations.post API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.pre-translations.post API Documentation Enterprise
     *
     * @param int $projectId
     * @param array $params
     * string[] $params[languageIds] required<br>
     * int[] $params[fileIds] required<br>
     * string $params[method]<br>
     * int $params[engineId]<br>
     * string $params[autoApproveOption]<br>
     * boolean $params[duplicateTranslations] Works only with TM pre-translation method<br>
     * boolean $params[translateUntranslatedOnly] Works only with TM pre-translation method<br>
     * boolean $params[translateWithPerfectMatchOnly] Works only with TM pre-translation method<br>
     * @return PreTranslation|null
     */
    public function applyPreTranslation(int $projectId, array $params): ?PreTranslation
    {
        $path = sprintf('projects/%s/pre-translations', $projectId);
        return $this->_post($path, PreTranslation::class, $params);
    }

    /**
     * Pre-Translation Status
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.pre-translations.get API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.pre-translations.get API Documentation Enterprise
     */
    public function getPreTranslation(int $projectId, string $preTranslationId): ?PreTranslation
    {
        $path = sprintf('projects/%d/pre-translations/%s', $projectId, $preTranslationId);
        return $this->_get($path, PreTranslation::class);
    }

    /**
     * Edit Pre-Translation
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.pre-translations.patch API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.pre-translations.patch API Documentation Enterprise
     */
    public function updatePreTranslation(int $projectId, PreTranslation $preTranslation): ?PreTranslation
    {
        $path = sprintf('projects/%d/pre-translations/%s', $projectId, $preTranslation->getIdentifier());
        return $this->_update($path, $preTranslation);
    }

    /**
     * Pre-Translation Report
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.pre-translations.patch API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.pre-translations.patch API Documentation Enterprise
     */
    public function getPreTranslationReport(int $projectId, string $preTranslationId): ?PreTranslationReport
    {
        $path = sprintf('projects/%d/pre-translations/%s/report', $projectId, $preTranslationId);
        return $this->_get($path, PreTranslationReport::class);
    }

    /**
     * Batch Update Pre-Translations
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.pre-translations.patchBatch API Documentation
     *
     * @param int $projectId
     * @param array $data JSON Patch array with operations (replace)
     * @return ModelCollection
     */
    public function updatePreTranslations(int $projectId, array $data): ModelCollection
    {
        $path = sprintf('projects/%d/pre-translations', $projectId);

        $options = [
            'body' => json_encode($data),
            'headers' => $this->getHeaders(),
        ];

        return $this->client->apiRequest(
            'patch',
            $path,
            new ResponseModelListDecorator(PreTranslation::class),
            $options
        );
    }

    /**
     * Build Project Directory Translation
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.builds.directories.post API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.builds.directories.post API Documentation Enterprise
     *
     * @param int $projectId
     * @param int $directoryId
     * @param array $params
     * string[] $params[targetLanguageIds] Leave this field empty to build all target languages<br>
     * boolean $params[skipUntranslatedStrings] Default: false<br>
     * boolean $params[skipUntranslatedFiles] Default: false<br>
     * boolean $params[exportApprovedOnly] Default: false<br>
     * boolean $params[preserveFolderHierarchy] Default: false<br>
     * @return TranslationProjectDirectory|null
     */
    public function buildProjectDirectoryTranslation(
        int $projectId,
        int $directoryId,
        array $params = []
    ): ?TranslationProjectDirectory {
        $path = sprintf('projects/%d/translations/builds/directories/%d', $projectId, $directoryId);

        return $this->_post($path, TranslationProjectDirectory::class, $params);
    }

    /**
     * Build Project File Translation
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.builds.files.post API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.builds.files.post API Documentation Enterprise
     *
     * @param int $projectId
     * @param int $fileId
     * @param array $params
     * @param string|null $ifNoneMatch
     * @return DownloadFile|null
     * string $params[targetLanguageId]<br>
     * boolean $params[exportAsXliff]<br>
     * boolean $params[skipUntranslatedStrings] true value can't be used with skipUntranslatedFiles=true in same request<br>
     * boolean $params[skipUntranslatedFiles] true value can't be used with skipUntranslatedStrings=true in same request<br>
     * boolean $params[exportApprovedOnly]
     */
    public function buildProjectFileTranslation(
        int $projectId,
        int $fileId,
        array $params = [],
        ?string $ifNoneMatch = null
    ): ?DownloadFile {
        $path = sprintf('projects/%d/translations/builds/files/%d', $projectId, $fileId);

        if ($ifNoneMatch) {
            $this->setHeader('If-None-Match', $ifNoneMatch);
        } else {
            $this->removeHeader('If-None-Match');
        }

        return $this->_post($path, DownloadFileTranslation::class, $params);
    }

    /**
     * List Project Builds
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.builds.getMany API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.builds.getMany API Documentation Enterprise
     *
     * @param int $projectId
     * @param array $params
     * integer $params[branchId]<br>
     * integer $params[limit]<br>
     * integer $params[offset]
     * @return ModelCollection
     */
    public function getProjectBuilds(int $projectId, array $params = []): ModelCollection
    {
        $path = sprintf('projects/%d/translations/builds', $projectId);
        return $this->_list($path, TranslationProjectBuild::class, $params);
    }

    /**
     * Build Project Translation
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.builds.build API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.builds.build API Documentation Enterprise
     *
     * @param int $projectId
     * @param array $params
     * integer $params[branchId]<br>
     * array $params[targetLanguageIds]<br>
     * bool $params[skipUntranslatedStrings] true value can't be used with skipUntranslatedFiles=true in same request<br>
     * bool $params[skipUntranslatedFiles] true value can't be used with skipUntranslatedStrings=true in same request<br>
     * bool $params[exportApprovedOnly]<br>
     * integer $params[exportWithMinApprovalsCount]
     * @return TranslationProjectBuild|null
     */
    public function buildProject(int $projectId, array $params = []): ?TranslationProjectBuild
    {
        $path = sprintf('projects/%d/translations/builds', $projectId);
        return $this->_post($path, TranslationProjectBuild::class, $params);
    }

    /**
     * Import Translations
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.imports API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.imports API Documentation Enterprise
     *
     * @param int $projectId
     * @param array $params
     *  integer $params[storageId] required<br>
     *  integer $params[branchId] required for string-based projects<br>
     *  string[] $params[languageIds]<br>
     *  integer $params[fileId] required for file-based projects (except XLIFF format)<br>
     *  bool $params[importEqSuggestions]<br>
     *  bool $params[autoApproveImported]<br>
     *  bool $params[translateHidden]<br>
     *  bool $params[addToTm]<br>
     * @return TranslationImport|null
     */
    public function importTranslations(int $projectId, array $params): ?TranslationImport
    {
        $path = sprintf('projects/%d/translations/imports', $projectId);
        return $this->_post($path, TranslationImport::class, $params);
    }

    /**
     * Check Translation Import Status
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.imports.get API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.imports.get API Documentation Enterprise
     */
    public function getTranslationImportStatus(int $projectId, string $importTranslationId): ?TranslationImport
    {
        $path = sprintf('projects/%d/translations/imports/%s', $projectId, $importTranslationId);
        return $this->_get($path, TranslationImport::class);
    }

    /**
     * Download Translation Import Report
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.imports.report.get API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.imports.report.get API Documentation Enterprise
     */
    public function downloadTranslationImportReport(
        int $projectId,
        string $importTranslationId
    ): ?TranslationImportReport {
        $path = sprintf('projects/%d/translations/imports/%s/report', $projectId, $importTranslationId);
        return $this->_get($path, TranslationImportReport::class);
    }

    /**
     * Upload Translations
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.postOnLanguage API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.postOnLanguage API Documentation Enterprise
     *
     * @deprecated Use importTranslations() instead.
     *
     * @param int $projectId
     * @param string $languageId
     * @param array $params
     *  integer $params[storageId] required<br>
     *  integer $params[fileId] required<br>
     *  bool $params[importEqSuggestions]<br>
     *  bool $params[autoApproveImported]<br>
     * @return array
     */
    public function uploadTranslations(int $projectId, string $languageId, array $params): array
    {
        $path = sprintf('projects/%d/translations/%s', $projectId, $languageId);

        $options = [
            'body' => json_encode($params),
            'headers' => $this->getHeaders(),
        ];

        return $this->client->apiRequest('post', $path, new ResponseArrayDecorator(), $options);
    }

    /**
     * Download Project Translations
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.builds.download.download API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.builds.download.download API Documentation Enterprise
     */
    public function downloadProjectBuild(int $projectId, int $buildId): ?DownloadFile
    {
        $path = sprintf('projects/%d/translations/builds/%d/download', $projectId, $buildId);
        return $this->_get($path, DownloadFile::class);
    }

    /**
     * Check Project Build Status
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.builds.get API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.builds.get API Documentation Enterprise
     */
    public function getProjectBuildStatus(int $projectId, int $buildId): ?TranslationProjectBuild
    {
        $path = sprintf('projects/%d/translations/builds/%d', $projectId, $buildId);
        return $this->_get($path, TranslationProjectBuild::class);
    }

    /**
     * Cancel Build
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.builds.delete API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.builds.delete API Documentation Enterprise
     *
     * @return mixed
     */
    public function deleteProjectBuild(int $projectId, int $buildId)
    {
        $path = sprintf('projects/%d/translations/builds/%d', $projectId, $buildId);
        return $this->_delete($path);
    }

    /**
     * Export Project Translation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.exports.post API Documentation
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.exports.post API Documentation Enterprise
     *
     * @param int $projectId
     * @param array $params
     * string $params[targetLanguageId] required<br>
     * string $params[format]<br>
     * int[] $params[branchIds] Note: Can't be used with directoryIds or fileIds in same request<br>
     * int[] $params[directoryIds] Note: Can't be used with branchIds or fileIds in same request<br>
     * int[] $params[fileIds] Note: Can't be used with branchIds or directoryIds in same request<br>
     * bool $params[skipUntranslatedStrings] Note: Can't be used with skipUntranslatedFiles in same request<br>
     * bool $params[skipUntranslatedFiles] Note: Can't be used with skipUntranslatedStrings in same request<br>
     * bool $params[exportApprovedOnly]<br>
     * integer $params[exportWithMinApprovalsCount]
     *
     * @return DownloadFile
     */
    public function exportProjectTranslation(int $projectId, array $params = []): DownloadFile
    {
        $path = sprintf('projects/%d/translations/exports', $projectId);
        return $this->_post($path, DownloadFile::class, $params);
    }

    /**
     * Translation Alignment
     * @link https://developer.crowdin.com/api/v2/#operation/api.projects.translations.alignment.post API Documentation
     * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.translations.alignment.post API Documentation Enterprise
     *
     * @param int $projectId
     * @param array $params
     * string $params[sourceLanguageId] required<br>
     * string $params[targetLanguageId] required<br>
     * string $params[text] required
     *
     * @return TranslationAlignment|null
     */
    public function alignment(int $projectId, array $params): ?TranslationAlignment
    {
        $path = sprintf('projects/%d/translations/alignment', $projectId);
        return $this->_post($path, TranslationAlignment::class, $params);
    }
}
