const fs = require('fs');
const path = require('path');
const RestClient = require('../../api/RestClient');
const { JQTransform } = require('../../../metadata/jqStream');
const { validate, loadSelfDescribingSchema, validateSelfDescribingSchema } = require('../../../metadata/schema');
const { subcommand, pluralizeCount, isB58Multihash, println } = require('../util');


module.exports = {
  command: 'validate [filename]',
  description: 'Validate newline-delimited json objects against a schema. ' + 'Objects will be read from `filename` or stdin.\n',
  builder: {
    schema: {
      description: 'Either a path to a local schema, or the base58 object id of a published schema. ' + 'Required, unless --jsonld is present.\n',
      type: 'string'
    },
    jsonld: {
      description: 'If --jsonld is present, validate that inputs are structurally valid JSONLD.  ' + 'This does not ensure that they are semantically correct.\n',
      type: 'boolean',
      default: false,
      defaultDescription: 'False.  Setting this flag will cause the --schema argument to be ignored.'
    },
    jqFilter: {
      type: 'string',
      description: 'A jq filter to apply to input records as a pre-processing step. ' + 'The filtered output will be validated against the schema. ' + 'If you use this, idSelector should be relative to the filtered output.\n',
      default: '.'
    }
  },

  handler: subcommand(opts => {
    const { client, filename, jqFilter, jsonld } = opts;
    let { schema } = opts;
    let streamName = 'standard input';

    let stream;
    if (filename) {
      stream = fs.createReadStream(filename);
      streamName = filename;
    } else {
      stream = process.stdin;
    }

    if (jsonld === true) {
      schema = path.join(__dirname, '..', '..', '..', 'metadata', 'schemas', 'io.mediachain.jsonld-jsonschema-1-0-0.json');
    }

    if (schema == null) {
      throw new Error('You must provide either the --schema or --jsonld arguments.');
    }

    let schemaPromise;
    if (isB58Multihash(schema)) {
      schemaPromise = client.getData(schema).then(validateSelfDescribingSchema);
    } else {
      schemaPromise = Promise.resolve(loadSelfDescribingSchema(schema));
    }

    return schemaPromise.then(schema => validateStream({
      stream,
      streamName,
      schema,
      jqFilter
    }));
  })
};

function validateStream(opts) {
  const {
    stream,
    streamName,
    schema,
    jqFilter
  } = opts;

  let count = 0;

  const jq = new JQTransform(jqFilter);

  return new Promise((resolve, reject) => {
    stream.pipe(jq).on('data', jsonString => {
      const obj = JSON.parse(jsonString);
      const result = validate(schema, obj);
      if (!result.success) {
        return reject(new Error(`${ result.error.message }.\nFailed object:\n${ jsonString }`));
      }
      count += 1;
    }).on('error', err => {
      return reject(new Error(`Error reading from ${ streamName }: ${ err.message }`));
    }).on('end', () => {
      println(`${ pluralizeCount(count, 'statement') } validated successfully`);
      resolve();
    });
  });
}