/**
 * @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 {AST} from '../../../src/expression_parser/ast';
import {Lexer} from '../../../src/expression_parser/lexer';
import {Parser} from '../../../src/expression_parser/parser';
import * as i18n from '../../../src/i18n/i18n_ast';
import * as o from '../../../src/output/output_ast';
import {ParseSourceSpan} from '../../../src/parse_util';
import * as t from '../../../src/render3/r3_ast';
import {I18nContext} from '../../../src/render3/view/i18n/context';
import {serializeI18nMessageForGetMsg} from '../../../src/render3/view/i18n/get_msg_utils';
import {serializeIcuNode} from '../../../src/render3/view/i18n/icu_serializer';
import {serializeI18nMessageForLocalize} from '../../../src/render3/view/i18n/localize_utils';
import {I18nMeta, parseI18nMeta} from '../../../src/render3/view/i18n/meta';
import {formatI18nPlaceholderName} from '../../../src/render3/view/i18n/util';
import {LEADING_TRIVIA_CHARS} from '../../../src/render3/view/template';
import {parseR3 as parse} from './util';
const expressionParser = new Parser(new Lexer());
const i18nOf = (element: t.Node&{i18n?: i18n.I18nMeta}) => element.i18n!;
describe('I18nContext', () => {
  it('should support i18n content collection', () => {
    const ref = o.variable('ref');
    const ast = new i18n.Message([], {}, {}, '', '', '');
    const ctx = new I18nContext(5, ref, 0, null, ast);
    // basic checks
    expect(ctx.isRoot).toBe(true);
    expect(ctx.isResolved).toBe(true);
    expect(ctx.id).toBe(0);
    expect(ctx.ref).toBe(ref);
    expect(ctx.index).toBe(5);
    expect(ctx.templateIndex).toBe(null);
    const tree = parse('
');
    const [boundText, element, template] = (tree.nodes[0] as t.Element).children;
    // data collection checks
    expect(ctx.placeholders.size).toBe(0);
    ctx.appendBoundText(i18nOf(boundText));       // interpolation
    ctx.appendElement(i18nOf(element), 1);        // open tag
    ctx.appendElement(i18nOf(element), 1, true);  // close tag
    ctx.appendTemplate(i18nOf(template), 2);      // open + close tags
    expect(ctx.placeholders.size).toBe(5);
    // binding collection checks
    expect(ctx.bindings.size).toBe(0);
    ctx.appendBinding(expressionParser.parseInterpolation('{{ valueA }}', '', 0) as AST);
    ctx.appendBinding(expressionParser.parseInterpolation('{{ valueB }}', '', 0) as AST);
    expect(ctx.bindings.size).toBe(2);
  });
  it('should support nested contexts', () => {
    const template = `
      
        A {{ valueA }}
        
A
        
          B {{ valueB }}
          B
          C {{ valueC }}
        
      ${input}
`);
    const root = tree.nodes[0] as t.Element;
    return serializeI18nMessageForGetMsg(root.i18n as i18n.Message);
  };
  it('should serialize plain text for `GetMsg()`', () => {
    expect(serialize('Some text')).toEqual('Some text');
  });
  it('should serialize text with interpolation for `GetMsg()`', () => {
    expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }}'))
        .toEqual('Some text {$interpolation} and {$interpolation_1}');
  });
  it('should serialize interpolation with named placeholder for `GetMsg()`', () => {
    expect(serialize('{{ valueB + valueC // i18n(ph="PLACEHOLDER NAME") }}'))
        .toEqual('{$placeholderName}');
  });
  it('should serialize content with HTML tags for `GetMsg()`', () => {
    expect(serialize('A BC
 D'))
        .toEqual('A {$startTagSpan}B{$startTagDiv}C{$closeTagDiv}{$closeTagSpan} D');
  });
  it('should serialize simple ICU for `GetMsg()`', () => {
    expect(serialize('{age, plural, 10 {ten} other {other}}'))
        .toEqual('{VAR_PLURAL, plural, 10 {ten} other {other}}');
  });
  it('should serialize nested ICUs for `GetMsg()`', () => {
    expect(serialize(
               '{age, plural, 10 {ten {size, select, 1 {one} 2 {two} other {2+}}} other {other}}'))
        .toEqual(
            '{VAR_PLURAL, plural, 10 {ten {VAR_SELECT, select, 1 {one} 2 {two} other {2+}}} other {other}}');
  });
  it('should serialize ICU with nested HTML for `GetMsg()`', () => {
    expect(serialize('{age, plural, 10 {ten} other {other
}}'))
        .toEqual(
            '{VAR_PLURAL, plural, 10 {{START_BOLD_TEXT}ten{CLOSE_BOLD_TEXT}} other {{START_TAG_DIV}other{CLOSE_TAG_DIV}}}');
  });
  it('should serialize ICU with nested HTML containing further ICUs for `GetMsg()`', () => {
    expect(
        serialize(
            '{gender, select, male {male} female {female} other {other}}{gender, select, male {male} female {female} other {other}}
'))
        .toEqual('{$icu}{$startTagDiv}{$icu}{$closeTagDiv}');
  });
});
describe('serializeI18nMessageForLocalize', () => {
  const serialize = (input: string) => {
    const tree = parse(`${input}
`, {leadingTriviaChars: LEADING_TRIVIA_CHARS});
    const root = tree.nodes[0] as t.Element;
    return serializeI18nMessageForLocalize(root.i18n as i18n.Message);
  };
  it('should serialize plain text for `$localize()`', () => {
    expect(serialize('Some text'))
        .toEqual({messageParts: [literal('Some text')], placeHolders: []});
  });
  it('should serialize text with interpolation for `$localize()`', () => {
    expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }} done')).toEqual({
      messageParts: [literal('Some text '), literal(' and '), literal(' done')],
      placeHolders: [placeholder('INTERPOLATION'), placeholder('INTERPOLATION_1')],
    });
  });
  it('should compute source-spans when serializing text with interpolation for `$localize()`',
     () => {
       const {messageParts, placeHolders} =
           serialize('Some text {{ valueA }} and {{ valueB + valueC }} done');
       expect(messageParts[0].text).toEqual('Some text ');
       expect(messageParts[0].sourceSpan.toString()).toEqual('Some text ');
       expect(messageParts[1].text).toEqual(' and ');
       expect(messageParts[1].sourceSpan.toString()).toEqual(' and ');
       expect(messageParts[2].text).toEqual(' done');
       expect(messageParts[2].sourceSpan.toString()).toEqual(' done');
       expect(placeHolders[0].text).toEqual('INTERPOLATION');
       expect(placeHolders[0].sourceSpan.toString()).toEqual('{{ valueA }}');
       expect(placeHolders[1].text).toEqual('INTERPOLATION_1');
       expect(placeHolders[1].sourceSpan.toString()).toEqual('{{ valueB + valueC }}');
     });
  it('should serialize text with interpolation at start for `$localize()`', () => {
    expect(serialize('{{ valueA }} and {{ valueB + valueC }} done')).toEqual({
      messageParts: [literal(''), literal(' and '), literal(' done')],
      placeHolders: [placeholder('INTERPOLATION'), placeholder('INTERPOLATION_1')],
    });
  });
  it('should serialize text with interpolation at end for `$localize()`', () => {
    expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }}')).toEqual({
      messageParts: [literal('Some text '), literal(' and '), literal('')],
      placeHolders: [placeholder('INTERPOLATION'), placeholder('INTERPOLATION_1')],
    });
  });
  it('should serialize only interpolation for `$localize()`', () => {
    expect(serialize('{{ valueB + valueC }}')).toEqual({
      messageParts: [literal(''), literal('')],
      placeHolders: [placeholder('INTERPOLATION')]
    });
  });
  it('should serialize interpolation with named placeholder for `$localize()`', () => {
    expect(serialize('{{ valueB + valueC // i18n(ph="PLACEHOLDER NAME") }}')).toEqual({
      messageParts: [literal(''), literal('')],
      placeHolders: [placeholder('PLACEHOLDER_NAME')]
    });
  });
  it('should serialize content with HTML tags for `$localize()`', () => {
    expect(serialize('A BC
 D')).toEqual({
      messageParts: [literal('A '), literal('B'), literal('C'), literal(''), literal(' D')],
      placeHolders: [
        placeholder('START_TAG_SPAN'), placeholder('START_TAG_DIV'), placeholder('CLOSE_TAG_DIV'),
        placeholder('CLOSE_TAG_SPAN')
      ]
    });
  });
  it('should compute source-spans when serializing content with HTML tags for `$localize()`',
     () => {
       const {messageParts, placeHolders} = serialize('A BC
 D');
       expect(messageParts[0].text).toEqual('A ');
       expect(messageParts[0].sourceSpan.toString()).toEqual('A ');
       expect(messageParts[1].text).toEqual('B');
       expect(messageParts[1].sourceSpan.toString()).toEqual('B');
       expect(messageParts[2].text).toEqual('C');
       expect(messageParts[2].sourceSpan.toString()).toEqual('C');
       expect(messageParts[3].text).toEqual('');
       expect(messageParts[3].sourceSpan.toString()).toEqual('');
       expect(messageParts[4].text).toEqual(' D');
       expect(messageParts[4].sourceSpan.toString()).toEqual('D');
       expect(placeHolders[0].text).toEqual('START_TAG_SPAN');
       expect(placeHolders[0].sourceSpan.toString()).toEqual('');
       expect(placeHolders[1].text).toEqual('START_TAG_DIV');
       expect(placeHolders[1].sourceSpan.toString()).toEqual('');
       expect(placeHolders[2].text).toEqual('CLOSE_TAG_DIV');
       expect(placeHolders[2].sourceSpan.toString()).toEqual('
');
       expect(placeHolders[3].text).toEqual('CLOSE_TAG_SPAN');
       expect(placeHolders[3].sourceSpan.toString()).toEqual('');
     });
  it('should create the correct source-spans when there are two placeholders next to each other',
     () => {
       const {messageParts, placeHolders} = serialize('{{value}}');
       expect(messageParts[0].text).toEqual('');
       expect(humanizeSourceSpan(messageParts[0].sourceSpan)).toEqual('"" (10-10)');
       expect(messageParts[1].text).toEqual('');
       expect(humanizeSourceSpan(messageParts[1].sourceSpan)).toEqual('"" (13-13)');
       expect(messageParts[2].text).toEqual('');
       expect(humanizeSourceSpan(messageParts[2].sourceSpan)).toEqual('"" (22-22)');
       expect(messageParts[3].text).toEqual('');
       expect(humanizeSourceSpan(messageParts[3].sourceSpan)).toEqual('"" (26-26)');
       expect(placeHolders[0].text).toEqual('START_BOLD_TEXT');
       expect(humanizeSourceSpan(placeHolders[0].sourceSpan)).toEqual('"" (10-13)');
       expect(placeHolders[1].text).toEqual('INTERPOLATION');
       expect(humanizeSourceSpan(placeHolders[1].sourceSpan)).toEqual('"{{value}}" (13-22)');
       expect(placeHolders[2].text).toEqual('CLOSE_BOLD_TEXT');
       expect(humanizeSourceSpan(placeHolders[2].sourceSpan)).toEqual('"" (22-26)');
     });
  it('should create the correct placeholder source-spans when there is skipped leading whitespace',
     () => {
       const {messageParts, placeHolders} = serialize('   {{value}}');
       expect(messageParts[0].text).toEqual('');
       expect(humanizeSourceSpan(messageParts[0].sourceSpan)).toEqual('"" (10-10)');
       expect(messageParts[1].text).toEqual('   ');
       expect(humanizeSourceSpan(messageParts[1].sourceSpan)).toEqual('"   " (13-16)');
       expect(messageParts[2].text).toEqual('');
       expect(humanizeSourceSpan(messageParts[2].sourceSpan)).toEqual('"" (25-25)');
       expect(messageParts[3].text).toEqual('');
       expect(humanizeSourceSpan(messageParts[3].sourceSpan)).toEqual('"" (29-29)');
       expect(placeHolders[0].text).toEqual('START_BOLD_TEXT');
       expect(humanizeSourceSpan(placeHolders[0].sourceSpan)).toEqual('"" (10-13)');
       expect(placeHolders[1].text).toEqual('INTERPOLATION');
       expect(humanizeSourceSpan(placeHolders[1].sourceSpan)).toEqual('"{{value}}" (16-25)');
       expect(placeHolders[2].text).toEqual('CLOSE_BOLD_TEXT');
       expect(humanizeSourceSpan(placeHolders[2].sourceSpan)).toEqual('"" (25-29)');
     });
  it('should serialize simple ICU for `$localize()`', () => {
    expect(serialize('{age, plural, 10 {ten} other {other}}')).toEqual({
      messageParts: [literal('{VAR_PLURAL, plural, 10 {ten} other {other}}')],
      placeHolders: []
    });
  });
  it('should serialize nested ICUs for `$localize()`', () => {
    expect(serialize(
               '{age, plural, 10 {ten {size, select, 1 {one} 2 {two} other {2+}}} other {other}}'))
        .toEqual({
          messageParts: [
            literal(
                '{VAR_PLURAL, plural, 10 {ten {VAR_SELECT, select, 1 {one} 2 {two} other {2+}}} other {other}}')
          ],
          placeHolders: []
        });
  });
  it('should serialize ICU with embedded HTML for `$localize()`', () => {
    expect(serialize('{age, plural, 10 {ten} other {other
}}')).toEqual({
      messageParts: [
        literal(
            '{VAR_PLURAL, plural, 10 {{START_BOLD_TEXT}ten{CLOSE_BOLD_TEXT}} other {{START_TAG_DIV}other{CLOSE_TAG_DIV}}}')
      ],
      placeHolders: []
    });
  });
  it('should serialize ICU with embedded interpolation for `$localize()`', () => {
    expect(serialize('{age, plural, 10 {ten} other {{{age}} years old}}')).toEqual({
      messageParts: [
        literal(
            '{VAR_PLURAL, plural, 10 {{START_BOLD_TEXT}ten{CLOSE_BOLD_TEXT}} other {{INTERPOLATION} years old}}')
      ],
      placeHolders: []
    });
  });
  it('should serialize ICU with nested HTML containing further ICUs for `$localize()`', () => {
    expect(
        serialize(
            '{gender, select, male {male} female {female} other {other}}{gender, select, male {male} female {female} other {other}}
'))
        .toEqual({
          messageParts: [literal(''), literal(''), literal(''), literal(''), literal('')],
          placeHolders: [
            placeholder('ICU'), placeholder('START_TAG_DIV'), placeholder('ICU'),
            placeholder('CLOSE_TAG_DIV')
          ]
        });
  });
  it('should serialize nested ICUs with embedded interpolation for `$localize()`', () => {
    expect(
        serialize(
            '{age, plural, 10 {ten {size, select, 1 {{{ varOne }}} 2 {{{ varTwo }}} other {2+}}} other {other}}'))
        .toEqual({
          messageParts: [
            literal(
                '{VAR_PLURAL, plural, 10 {ten {VAR_SELECT, select, 1 {{INTERPOLATION}} 2 {{INTERPOLATION_1}} other {2+}}} other {other}}')
          ],
          placeHolders: []
        });
  });
});
describe('serializeIcuNode', () => {
  const serialize = (input: string) => {
    const tree = parse(`${input}
`);
    const rooti18n = (tree.nodes[0] as t.Element).i18n as i18n.Message;
    return serializeIcuNode(rooti18n.nodes[0] as i18n.Icu);
  };
  it('should serialize a simple ICU', () => {
    expect(serialize('{age, plural, 10 {ten} other {other}}'))
        .toEqual('{VAR_PLURAL, plural, 10 {ten} other {other}}');
  });
  it('should serialize a nested ICU', () => {
    expect(serialize(
               '{age, plural, 10 {ten {size, select, 1 {one} 2 {two} other {2+}}} other {other}}'))
        .toEqual(
            '{VAR_PLURAL, plural, 10 {ten {VAR_SELECT, select, 1 {one} 2 {two} other {2+}}} other {other}}');
  });
  it('should serialize ICU with nested HTML', () => {
    expect(serialize('{age, plural, 10 {ten} other {other
}}'))
        .toEqual(
            '{VAR_PLURAL, plural, 10 {{START_BOLD_TEXT}ten{CLOSE_BOLD_TEXT}} other {{START_TAG_DIV}other{CLOSE_TAG_DIV}}}');
  });
  it('should serialize an ICU with embedded interpolations', () => {
    expect(serialize('{age, select, 10 {ten} other {{{age}} years old}}'))
        .toEqual('{VAR_SELECT, select, 10 {ten} other {{INTERPOLATION} years old}}');
  });
});
function literal(text: string, span: any = jasmine.any(ParseSourceSpan)): o.LiteralPiece {
  return new o.LiteralPiece(text, span);
}
function placeholder(name: string, span: any = jasmine.any(ParseSourceSpan)): o.PlaceholderPiece {
  return new o.PlaceholderPiece(name, span);
}
function humanizeSourceSpan(span: ParseSourceSpan): string {
  return `"${span.toString()}" (${span.start.offset}-${span.end.offset})`;
}