/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */
import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system';
import {loadTestDirectory} from '@angular/compiler-cli/src/ngtsc/testing';
import path from 'path';
import url from 'url';

import {Diagnostics} from '../../../src/diagnostics';
import {translateFiles} from '../../../src/translate/index';
import {getOutputPathFn} from '../../../src/translate/output_path';
import {runInNativeFileSystem} from '../../helpers';

const currentDir = path.dirname(url.fileURLToPath(import.meta.url));

runInNativeFileSystem(() => {
  describe('translateFiles()', () => {
    let fs: FileSystem;
    let testDir: AbsoluteFsPath;
    let testFilesDir: AbsoluteFsPath;
    let translationFilesDir: AbsoluteFsPath;

    beforeEach(() => {
      fs = getFileSystem();
      testDir = absoluteFrom('/test');

      testFilesDir = fs.resolve(testDir, 'test_files');
      loadTestDirectory(fs, path.resolve(path.join(currentDir, 'test_files')), testFilesDir);
      translationFilesDir = fs.resolve(testDir, 'test_files');
      loadTestDirectory(fs, path.resolve(path.join(currentDir, 'locales')), translationFilesDir);
    });

    it('should copy non-code files to the destination folders', () => {
      const diagnostics = new Diagnostics();
      const outputPathFn = getOutputPathFn(fs, fs.resolve(testDir, '{{LOCALE}}'));
      translateFiles({
        sourceRootPath: testFilesDir,
        sourceFilePaths: ['test-1.txt', 'test-2.txt'],
        outputPathFn,
        translationFilePaths: resolveAll(
            translationFilesDir,
            ['messages.de.json', 'messages.es.xlf', 'messages.fr.xlf', 'messages.it.xtb']),
        translationFileLocales: [],
        diagnostics,
        missingTranslation: 'error',
        duplicateTranslation: 'error',
      });

      expect(diagnostics.messages.length).toEqual(0);

      expect(fs.readFile(fs.resolve(testDir, 'fr', 'test-1.txt')))
          .toEqual('Contents of test-1.txt');
      expect(fs.readFile(fs.resolve(testDir, 'fr', 'test-2.txt')))
          .toEqual('Contents of test-2.txt');
      expect(fs.readFile(fs.resolve(testDir, 'de', 'test-1.txt')))
          .toEqual('Contents of test-1.txt');
      expect(fs.readFile(fs.resolve(testDir, 'de', 'test-2.txt')))
          .toEqual('Contents of test-2.txt');
      expect(fs.readFile(fs.resolve(testDir, 'es', 'test-1.txt')))
          .toEqual('Contents of test-1.txt');
      expect(fs.readFile(fs.resolve(testDir, 'es', 'test-2.txt')))
          .toEqual('Contents of test-2.txt');
      expect(fs.readFile(fs.resolve(testDir, 'it', 'test-1.txt')))
          .toEqual('Contents of test-1.txt');
      expect(fs.readFile(fs.resolve(testDir, 'it', 'test-2.txt')))
          .toEqual('Contents of test-2.txt');
    });

    it('should translate and copy source-code files to the destination folders', () => {
      const diagnostics = new Diagnostics();
      const outputPathFn = getOutputPathFn(fs, fs.resolve(testDir, '{{LOCALE}}'));
      translateFiles({
        sourceRootPath: testFilesDir,
        sourceFilePaths: ['test.js'],
        outputPathFn,
        translationFilePaths: resolveAll(
            translationFilesDir,
            ['messages.de.json', 'messages.es.xlf', 'messages.fr.xlf', 'messages.it.xtb']),
        translationFileLocales: [],
        diagnostics,
        missingTranslation: 'error',
        duplicateTranslation: 'error',
      });

      expect(diagnostics.messages.length).toEqual(0);

      expect(fs.readFile(fs.resolve(testDir, 'fr', 'test.js')))
          .toEqual(`var name="World";var message="Bonjour, "+name+"!";`);
      expect(fs.readFile(fs.resolve(testDir, 'de', 'test.js')))
          .toEqual(`var name="World";var message="Guten Tag, "+name+"!";`);
      expect(fs.readFile(fs.resolve(testDir, 'es', 'test.js')))
          .toEqual(`var name="World";var message="Hola, "+name+"!";`);
      expect(fs.readFile(fs.resolve(testDir, 'it', 'test.js')))
          .toEqual(`var name="World";var message="Ciao, "+name+"!";`);
    });

    it('should translate and copy source-code files overriding the locales', () => {
      const diagnostics = new Diagnostics();
      const outputPathFn = getOutputPathFn(fs, fs.resolve(testDir, '{{LOCALE}}'));
      translateFiles({
        sourceRootPath: testFilesDir,
        sourceFilePaths: ['test.js'],
        outputPathFn,
        translationFilePaths: resolveAll(
            translationFilesDir,
            ['messages.de.json', 'messages.es.xlf', 'messages.fr.xlf', 'messages.it.xtb']),
        translationFileLocales: ['xde', undefined, 'fr'],
        diagnostics,
        missingTranslation: 'error',
        duplicateTranslation: 'error',
      });

      expect(diagnostics.messages.length).toEqual(1);
      expect(diagnostics.messages).toContain({
        type: 'warning',
        message:
            `The provided locale "xde" does not match the target locale "de" found in the translation file "${
                fs.resolve(translationFilesDir, 'messages.de.json')}".`
      });

      expect(fs.readFile(fs.resolve(testDir, 'xde', 'test.js')))
          .toEqual(`var name="World";var message="Guten Tag, "+name+"!";`);
      expect(fs.readFile(fs.resolve(testDir, 'es', 'test.js')))
          .toEqual(`var name="World";var message="Hola, "+name+"!";`);
      expect(fs.readFile(fs.resolve(testDir, 'fr', 'test.js')))
          .toEqual(`var name="World";var message="Bonjour, "+name+"!";`);
      expect(fs.readFile(fs.resolve(testDir, 'it', 'test.js')))
          .toEqual(`var name="World";var message="Ciao, "+name+"!";`);
    });

    it('should merge translation files, if more than one provided, and translate source-code', () => {
      const diagnostics = new Diagnostics();
      const outputPathFn = getOutputPathFn(fs, fs.resolve(testDir, '{{LOCALE}}'));
      translateFiles({
        sourceRootPath: testFilesDir,
        sourceFilePaths: ['test-extra.js'],
        outputPathFn,
        translationFilePaths: resolveAllRecursive(
            translationFilesDir,
            [['messages.de.json', 'messages-extra.de.json'], 'messages.es.xlf']),
        translationFileLocales: [],
        diagnostics,
        missingTranslation: 'error',
        duplicateTranslation: 'error',
      });

      expect(diagnostics.messages.length).toEqual(1);
      // There is no "extra" translation in the `es` locale translation file.
      expect(diagnostics.messages[0]).toEqual({
        type: 'error',
        message: 'No translation found for "customExtra" ("Goodbye, {$PH}!").'
      });

      // The `de` locale translates the `customExtra` message because it is in the
      // `messages-extra.de.json` file that was merged.
      expect(fs.readFile(fs.resolve(testDir, 'de', 'test-extra.js')))
          .toEqual(
              `var name="World";var message="Guten Tag, "+name+"!";var message="Auf wiedersehen, "+name+"!";`);
      // The `es` locale does not translate `customExtra` because there is no translation for it.
      expect(fs.readFile(fs.resolve(testDir, 'es', 'test-extra.js')))
          .toEqual(
              `var name="World";var message="Hola, "+name+"!";var message="Goodbye, "+name+"!";`);
    });

    it('should transform and/or copy files to the destination folders', () => {
      const diagnostics = new Diagnostics();
      const outputPathFn = getOutputPathFn(fs, fs.resolve(testDir, '{{LOCALE}}'));
      translateFiles({
        sourceRootPath: testFilesDir,
        sourceFilePaths: ['test-1.txt', 'test-2.txt', 'test.js'],
        outputPathFn,
        translationFilePaths: resolveAll(
            translationFilesDir,
            ['messages.de.json', 'messages.es.xlf', 'messages.fr.xlf', 'messages.it.xtb']),
        translationFileLocales: [],
        diagnostics,
        missingTranslation: 'error',
        duplicateTranslation: 'error',
      });

      expect(diagnostics.messages.length).toEqual(0);

      expect(fs.readFile(fs.resolve(testDir, 'fr', 'test-1.txt')))
          .toEqual('Contents of test-1.txt');
      expect(fs.readFile(fs.resolve(testDir, 'fr', 'test-2.txt')))
          .toEqual('Contents of test-2.txt');
      expect(fs.readFile(fs.resolve(testDir, 'de', 'test-1.txt')))
          .toEqual('Contents of test-1.txt');
      expect(fs.readFile(fs.resolve(testDir, 'de', 'test-2.txt')))
          .toEqual('Contents of test-2.txt');
      expect(fs.readFile(fs.resolve(testDir, 'es', 'test-1.txt')))
          .toEqual('Contents of test-1.txt');
      expect(fs.readFile(fs.resolve(testDir, 'es', 'test-2.txt')))
          .toEqual('Contents of test-2.txt');
      expect(fs.readFile(fs.resolve(testDir, 'it', 'test-1.txt')))
          .toEqual('Contents of test-1.txt');
      expect(fs.readFile(fs.resolve(testDir, 'it', 'test-2.txt')))
          .toEqual('Contents of test-2.txt');

      expect(fs.readFile(fs.resolve(testDir, 'fr', 'test.js')))
          .toEqual(`var name="World";var message="Bonjour, "+name+"!";`);
      expect(fs.readFile(fs.resolve(testDir, 'de', 'test.js')))
          .toEqual(`var name="World";var message="Guten Tag, "+name+"!";`);
      expect(fs.readFile(fs.resolve(testDir, 'es', 'test.js')))
          .toEqual(`var name="World";var message="Hola, "+name+"!";`);
      expect(fs.readFile(fs.resolve(testDir, 'it', 'test.js')))
          .toEqual(`var name="World";var message="Ciao, "+name+"!";`);
    });

    function resolveAll(rootPath: string, paths: string[]): string[] {
      return paths.map(p => fs.resolve(rootPath, p));
    }
    function resolveAllRecursive(
        rootPath: string, paths: (string|string[])[]): (string|string[])[] {
      return paths.map(
          p => Array.isArray(p) ? p.map(p2 => fs.resolve(rootPath, p2)) : fs.resolve(rootPath, p));
    }
  });
});
