/**
 * @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 {AbsoluteFsPath, FileSystem} from '@angular/compiler-cli/src/ngtsc/file_system';
import {ɵParsedMessage, ɵSourceLocation} from '@angular/localize';
import {ArbJsonObject, ArbLocation, ArbMetadata} from '../../translate/translation_files/translation_parsers/arb_translation_parser';
import {TranslationSerializer} from './translation_serializer';
import {consolidateMessages, hasLocation} from './utils';

/**
 * A translation serializer that can render JSON formatted as an Application Resource Bundle (ARB).
 *
 * See https://github.com/google/app-resource-bundle/wiki/ApplicationResourceBundleSpecification
 *
 * ```
 * {
 *   "@@locale": "en-US",
 *   "message-id": "Target message string",
 *   "@message-id": {
 *     "type": "text",
 *     "description": "Some description text",
 *     "x-locations": [
 *       {
 *         "start": {"line": 23, "column": 145},
 *         "end": {"line": 24, "column": 53},
 *         "file": "some/file.ts"
 *       },
 *       ...
 *     ]
 *   },
 *   ...
 * }
 * ```
 */

/**
 * This is a semi-public bespoke serialization format that is used for testing and sometimes as a
 * format for storing translations that will be inlined at runtime.
 *
 * @see ArbTranslationParser
 */
export class ArbTranslationSerializer implements TranslationSerializer {
  constructor(
      private sourceLocale: string, private basePath: AbsoluteFsPath, private fs: FileSystem) {}

  serialize(messages: ɵParsedMessage[]): string {
    const messageGroups = consolidateMessages(messages, message => getMessageId(message));

    let output = `{\n  "@@locale": ${JSON.stringify(this.sourceLocale)}`;

    for (const duplicateMessages of messageGroups) {
      const message = duplicateMessages[0];
      const id = getMessageId(message);
      output += this.serializeMessage(id, message);
      output += this.serializeMeta(
          id, message.description, duplicateMessages.filter(hasLocation).map(m => m.location));
    }

    output += '\n}';

    return output;
  }

  private serializeMessage(id: string, message: ɵParsedMessage): string {
    return `,\n  ${JSON.stringify(id)}: ${JSON.stringify(message.text)}`;
  }

  private serializeMeta(id: string, description: string|undefined, locations: ɵSourceLocation[]):
      string {
    const meta: string[] = [];

    if (description) {
      meta.push(`\n    "description": ${JSON.stringify(description)}`);
    }

    if (locations.length > 0) {
      let locationStr = `\n    "x-locations": [`;
      for (let i = 0; i < locations.length; i++) {
        locationStr += (i > 0 ? ',\n' : '\n') + this.serializeLocation(locations[i]);
      }
      locationStr += '\n    ]';
      meta.push(locationStr);
    }

    return meta.length > 0 ? `,\n  ${JSON.stringify('@' + id)}: {${meta.join(',')}\n  }` : '';
  }

  private serializeLocation({file, start, end}: ɵSourceLocation): string {
    return [
      `      {`,
      `        "file": ${JSON.stringify(this.fs.relative(this.basePath, file))},`,
      `        "start": { "line": "${start.line}", "column": "${start.column}" },`,
      `        "end": { "line": "${end.line}", "column": "${end.column}" }`,
      `      }`,
    ].join('\n');
  }
}

function getMessageId(message: ɵParsedMessage): string {
  return message.customId || message.id;
}
