/**
 * @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, PathSegment, relativeFrom} from '@angular/compiler-cli/src/ngtsc/file_system';
import {runInEachFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/testing';

import {Diagnostics} from '../../../src/diagnostics';
import {SourceFileTranslationHandler} from '../../../src/translate/source_files/source_file_translation_handler';
import {TranslationBundle} from '../../../src/translate/translator';

runInEachFileSystem(() => {
  describe('SourceFileTranslationHandler', () => {
    let fs: FileSystem;
    let rootPath: AbsoluteFsPath;
    let filePath: PathSegment;
    let enTranslationPath: AbsoluteFsPath;
    let enUSTranslationPath: AbsoluteFsPath;
    let frTranslationPath: AbsoluteFsPath;

    beforeEach(() => {
      fs = getFileSystem();
      rootPath = absoluteFrom('/src/path');
      filePath = relativeFrom('relative/path.js');
      enTranslationPath = absoluteFrom('/translations/en/relative/path.js');
      enUSTranslationPath = absoluteFrom('/translations/en-US/relative/path.js');
      frTranslationPath = absoluteFrom('/translations/fr/relative/path.js');
    });

    describe('canTranslate()', () => {
      it('should return true if the path ends in ".js"', () => {
        const handler = new SourceFileTranslationHandler(fs);
        expect(handler.canTranslate(relativeFrom('relative/path'), Buffer.from('contents')))
            .toBe(false);
        expect(handler.canTranslate(filePath, Buffer.from('contents'))).toBe(true);
      });
    });

    describe('translate()', () => {
      it('should copy files for each translation locale if they contain no reference to `$localize`',
         () => {
           const diagnostics = new Diagnostics();
           const handler = new SourceFileTranslationHandler(fs);
           const translations = [
             {locale: 'en', translations: {}},
             {locale: 'fr', translations: {}},
           ];
           const contents = Buffer.from('contents');
           handler.translate(
               diagnostics, rootPath, filePath, contents, mockOutputPathFn, translations);

           expect(fs.readFileBuffer(enTranslationPath)).toEqual(contents);
           expect(fs.readFileBuffer(frTranslationPath)).toEqual(contents);
         });

      it('should copy files to the source locale if they contain no reference to `$localize` and `sourceLocale` is provided',
         () => {
           const diagnostics = new Diagnostics();
           const handler = new SourceFileTranslationHandler(fs);
           const translations: TranslationBundle[] = [];
           const contents = Buffer.from('contents');
           handler.translate(
               diagnostics, rootPath, filePath, contents, mockOutputPathFn, translations, 'en-US');
           expect(fs.readFileBuffer(enUSTranslationPath)).toEqual(contents);
         });

      it('should transform each $localize template tag', () => {
        const diagnostics = new Diagnostics();
        const handler = new SourceFileTranslationHandler(fs);
        const translations = [
          {locale: 'en', translations: {}},
          {locale: 'fr', translations: {}},
        ];
        const contents = Buffer.from(
            '$localize`a${1}b${2}c`;\n' +
            '$localize(__makeTemplateObject(["a", "b", "c"], ["a", "b", "c"]), 1, 2);');
        const output = '"a"+1+"b"+2+"c";"a"+1+"b"+2+"c";';
        handler.translate(
            diagnostics, rootPath, filePath, contents, mockOutputPathFn, translations);

        expect(fs.readFile(enTranslationPath)).toEqual(output);
        expect(fs.readFile(frTranslationPath)).toEqual(output);
      });

      it('should transform each $localize template tag and write it to the source locale if provided',
         () => {
           const diagnostics = new Diagnostics();
           const handler = new SourceFileTranslationHandler(fs);
           const translations: TranslationBundle[] = [];
           const contents = Buffer.from(
               '$localize`a${1}b${2}c`;\n' +
               '$localize(__makeTemplateObject(["a", "b", "c"], ["a", "b", "c"]), 1, 2);');
           const output = '"a"+1+"b"+2+"c";"a"+1+"b"+2+"c";';
           handler.translate(
               diagnostics, rootPath, filePath, contents, mockOutputPathFn, translations, 'en-US');

           expect(fs.readFile(enUSTranslationPath)).toEqual(output);
         });

      it('should transform `$localize.locale` identifiers', () => {
        const diagnostics = new Diagnostics();
        const handler = new SourceFileTranslationHandler(fs);
        const translations: TranslationBundle[] = [
          {locale: 'fr', translations: {}},
        ];
        const contents = Buffer.from(
            'const x = $localize.locale;\n' +
            'const y = typeof $localize !== "undefined" && $localize.locale;\n' +
            'const z = "undefined" !== typeof $localize && $localize.locale || "default";');
        const getOutput = (locale: string) =>
            `const x="${locale}";const y="${locale}";const z="${locale}"||"default";`;

        handler.translate(
            diagnostics, rootPath, filePath, contents, mockOutputPathFn, translations, 'en-US');

        expect(fs.readFile(frTranslationPath)).toEqual(getOutput('fr'));
        expect(fs.readFile(enUSTranslationPath)).toEqual(getOutput('en-US'));
      });

      it('should error if the file is not valid JS', () => {
        const diagnostics = new Diagnostics();
        const handler = new SourceFileTranslationHandler(fs);
        const translations = [{locale: 'en', translations: {}}];
        const contents = Buffer.from('this is not a valid $localize file.');
        expect(
            () => handler.translate(
                diagnostics, rootPath, filePath, contents, mockOutputPathFn, translations))
            .toThrowError();
      });
    });
  });

  function mockOutputPathFn(locale: string, relativePath: string) {
    return `/translations/${locale}/${relativePath}`;
  }
});
