/**
 * @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.dev/license
 */

const { readFileSync, writeFileSync, mkdirSync } = require('fs');
const { resolve, dirname } = require('path');

/**
 * Generator the Angular CLI workspace schema file.
 */
function generate(inPath, outPath) {
  // While on paper we could use quicktype for this.
  // Quicktype doesn't handle `patternProperties` and `oneOf` that well.

  const jsonSchema = readFileSync(inPath, 'utf8');
  const nestedDefinitions = {};
  const schemaParsed = JSON.parse(jsonSchema, (key, value) => {
    if (key === '$ref' && typeof value === 'string' && !value.startsWith('#')) {
      // Resolve $ref and camelize key
      const definitionKey = value
        .replace(/(\.json|src)/g, '')
        .split(/\\|\/|_|-|\./)
        .filter((p) => !!p)
        .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
        .join('');

      const nestedSchemaPath = resolve(dirname(inPath), value);
      const nestedSchema = readFileSync(nestedSchemaPath, 'utf8');
      const nestedSchemaJson = JSON.parse(nestedSchema, (key, value) => {
        switch (key) {
          case '$ref':
            if (value.startsWith('#/definitions/')) {
              return value.replace('#/definitions/', `#/definitions/${definitionKey}/definitions/`);
            } else {
              throw new Error(`Error while resolving $ref ${value} in ${nestedSchemaPath}.`);
            }
          case '$id':
          case 'id':
          case '$schema':
          case 'required':
          case 'x-prompt':
          case 'x-user-analytics':
            return undefined;
          default:
            return value;
        }
      });

      nestedDefinitions[definitionKey] = nestedSchemaJson;

      return `#/definitions/${definitionKey}`;
    }

    return key === ''
      ? {
          ...value,
          definitions: {
            ...value.definitions,
            ...nestedDefinitions,
          },
        }
      : value;
  });

  const buildWorkspaceDirectory = process.env['BUILD_WORKSPACE_DIRECTORY'] || '.';
  outPath = resolve(buildWorkspaceDirectory, outPath);

  mkdirSync(dirname(outPath), { recursive: true });
  writeFileSync(
    outPath,
    JSON.stringify(
      schemaParsed,
      (key, value) => {
        if (key === 'x-deprecated') {
          // Needed for IDEs, and will be replaced to 'deprecated' later on. This must be a boolean.
          // https://json-schema.org/draft/2020-12/json-schema-validation.html#name-deprecated
          return !!value;
        }

        return value;
      },
      2,
    ).replace(/"x-deprecated"/g, '"deprecated"'),
  );
}

if (require.main === module) {
  const argv = process.argv.slice(2);
  if (argv.length !== 2) {
    console.error('Must include 2 arguments.');
    process.exit(1);
  }

  const [inPath, outPath] = argv;

  try {
    generate(inPath, outPath);
  } catch (error) {
    console.error('An error happened:');
    console.error(error);
    process.exit(127);
  }
}

exports.generate = generate;
