/**
 * Merges a source .model_info file with metadata extracted from .kps file and
 * compiled files to produce a comprehensive .model_info file.
 */

/// <reference path="../lexical-model-compiler/lexical-model.ts" />
/// <reference path="../lexical-model-compiler/model-info-file.ts" />

import * as fs from "fs";
import * as path from "path";
import { minKeymanVersion } from "../util/min-keyman-version";
import {SysExits} from "../util/sysexits";


export class ModelInfoOptions {
  /** The identifier for the model */
  model_id: string;

  /** The data from the .kps file, transformed to kmp.json */
  kmpJsonData: KmpJsonFile;

  /** The path in the keymanapp/lexical-models repo where this model may be found (optional) */
  sourcePath?: string;

  /** The compiled model filename and relative path (.js) */
  modelFileName: string;

  /** The compiled package filename and relative path (.kmp) */
  kmpFileName: string;
};

/**
 * Merges source .model_info file with metadata from the model and package source file.
 * This function is intended for use within the lexical-models repository. While many of the
 * parameters could be deduced from each other, they are specified here to reduce the
 * number of places the filenames are constructed.
 *
 * @param sourceModelInfoFileName  Path for the source .model_info file
 * @param destModelInfoFileName    Path to write the merged .model_info file to
 * @param options                  Details on files from which to extract additional metadata
 */
export function writeMergedModelMetadataFile(
    sourceModelInfoFileName: string,
    destModelInfoFileName: string,
    options: ModelInfoOptions
  ) {

  /*
    * Model info looks like this:
    *
    *  {
    *    "name": "Example Template Model"
    *    "license": "mit",
    *    "version": "1.0.0",
    *    "languages": ["en"],
    *    "authorName": "Example Author",
    *    "authorEmail": "nobody@example.com",
    *    "description": "Example wordlist model"
    *  }
    *
    * For full documentation, see:
    * https://help.keyman.com/developer/cloud/model_info/1.0/
    */
  let model_info: ModelInfoFile = JSON.parse(fs.readFileSync(sourceModelInfoFileName, 'utf8'));

  //
  // Build merged .model_info file
  // https://api.keyman.com/schemas/model_info.source.json and
  // https://api.keyman.com/schemas/model_info.distribution.json
  // https://help.keyman.com/developer/cloud/model_info/1.0
  //

  function setModelMetadata(field: keyof ModelInfoFile, expected: unknown, warn: boolean = true) {
    if (model_info[field] && model_info[field] !== expected) {
      if (warn || typeof warn === 'undefined')
        console.warn(`Warning: source ${sourceModelInfoFileName} field ${field} value "${model_info[field]}" does not match "${expected}" found in source file metadata.`);
    }
    // TypeScript gets upset with this assignment, because it cannot deduce
    // the exact type of model_info[field] -- there are many possibilities!
    // So we assert that it's unknown so that TypeScript can chill.
    (<unknown> model_info[field]) = model_info[field] || expected;
  }

  //
  // Merge model info file -- some fields have "special" behaviours -- see below
  //

  setModelMetadata('id', options.model_id);

  setModelMetadata('name', options.kmpJsonData.info.name.description);

  let author = options.kmpJsonData.info.author;
  setModelMetadata('authorName', author.description);

  if (author.url) {
    // we strip the mailto: from the .kps file for the .model_info
    let match = author.url.match(/^(mailto\:)?(.+)$/);
    if (match === null) {
      console.error(`Invalid author email: ${author.url}`);
      process.exit(SysExits.EX_DATAERR);
    }

    let email = match[2];
    setModelMetadata('authorEmail', email, false);
  }

  // extract the language identifiers from the language metadata
  // arrays for each of the lexical models in the kmp.json file,
  // and merge into a single array of identifiers in the
  // .model_info file.

  model_info.languages = model_info.languages || options.kmpJsonData.lexicalModels.reduce((a, e) => [].concat(a, e.languages.map((f) => f.id)), []);

  setModelMetadata('lastModifiedDate', (new Date).toISOString());
  setModelMetadata('packageFilename', path.basename(options.kmpFileName));

  // Always overwrite with actual file size
  model_info.packageFileSize = fs.statSync(options.kmpFileName).size;

  setModelMetadata('jsFilename', path.basename(options.modelFileName));

  // Always overwrite with actual file size
  model_info.jsFileSize = fs.statSync(options.modelFileName).size;

  // Always overwrite source data
  model_info.packageIncludes = options.kmpJsonData.files.filter((e) => !!e.name.match(/.[ot]tf$/i)).length ? ['fonts'] : [];

  setModelMetadata('version', options.kmpJsonData.info.version.description);

  // The minimum Keyman version detected in the package file may be manually set higher by the developer
  setModelMetadata('minKeymanVersion', minKeymanVersion, false);

  if(options.sourcePath) {
    setModelMetadata('sourcePath', options.sourcePath);
  }

  fs.writeFileSync(destModelInfoFileName, JSON.stringify(model_info, null, 2));
}
