#!/usr/bin/env node

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Jeremy Whitlock
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

'use strict';

var JsonRefs = require('..');
var program = require('commander');
var pkg = require('../package.json');
var YAML = require('js-yaml');

// Load promises polyfill if necessary
/* istanbul ignore if */
if (typeof Promise === 'undefined') {
  require('native-promise-only');
}

function exitWithError (err) {
  console.error();
  console.error('  error: ' + err.message);
  console.error(); // Here only to match the output of commander.js

  process.exit(1);
}

function optionArrayAppender (values) {
  values = values || [];

  return function (value) {
    values.push(value);

    return values;
  };
}

function handleUnknownCommand (command) {
  // Using log instead of error since commander.js uses console.log for help output
  console.log(program._name + ' does not support the ' + command + ' command.');

  program.outputHelp();
};

// Set name and version
program._name = 'json-refs';
program.version(pkg.version);

// Help command
program
  .command('help [command]')
  .description('Display help information')
  .action(function (name) {
    var command;

    if (typeof name !== 'undefined') {
      this.parent.commands.forEach(function (cmd) {
        if (cmd._name === name) {
          command = cmd;
        }
      });

      if (typeof command === 'undefined') {
        handleUnknownCommand(name);
      } else {
        command.help();
      }
    } else {
      program.outputHelp();
    }
  });

// Resolve command
program
  .command('resolve <location>')
  .description('Prints document at location with its JSON References resolved')
  .option('-f, --force', 'Do not fail when the document has invalid JSON References')
  .option('-H, --header <header>', 'The header to use when retrieving a remote document', optionArrayAppender(), [])
  .option('-I, --filter <type>', 'The type of JSON References to resolved', optionArrayAppender(), [])
  .option('-w, --warnings-as-errors', 'Treat warnings as errors')
  .option('-y, --yaml', 'Output as YAML')
  .action(function (location) {
    var options = {
      loaderOptions: {
        processContent: function (res, callback) {
          callback(undefined, YAML.safeLoad(res.text));
        }
      }
    };
    var that = this;

    if (this.filter.length > 0) {
      options.filter = this.filter;
    }

    if (!this.force) {
      options.includeInvalid = true;
    }

    if (this.header.length > 0) {
      options.loaderOptions.prepareRequest = function (req, callback) {
        that.header.forEach(function (header) {
          var headerParts = header.split(': ');

          req.set(headerParts[0], headerParts[1]);
        });

        callback(undefined, req);
      };
    }

    JsonRefs.resolveRefsAt(location, options)
      .then(function (results) {
        var errors = [];

        if (!that.force) {
          Object.keys(results.refs).sort().forEach(function (refPtr) {
            var refDetails = results.refs[refPtr];

            if (refDetails.type === 'invalid' || refDetails.error || (that.warningsAsErrors && refDetails.warning)) {
              errors.push('  ' + refPtr + ': ' + (refDetails.warning ? refDetails.warning : refDetails.error));
            }
          });
        }

        if (errors.length > 0 && !that.force) {
          throw new Error('Document has invalid references:\n\n' + errors.join('\n'));
        } else {
          console.log(that.yaml || location.endsWith('.yaml') ?
                        YAML.safeDump(results.resolved, {noRefs: true}) :
                        JSON.stringify(results.resolved, null, 2));
        }
      })
      .catch(exitWithError);
  });

// Default command (handles all unregistered commands)
program
  .command('*', null, {noHelp: true}) // null is required to avoid the implicit 'help' command being added
  .action(function (cmd) {
    handleUnknownCommand(cmd);
  });

// Process the CLI arguments and run
if (!process.argv.slice(2).length) {
  program.outputHelp();
} else {
  program.parse(process.argv);
}
