'use strict';

var CoreObject = require('core-object');
var fs = require('fs-extra');
var RSVP = require('rsvp');
var path = require('path');
var extend = require('extend');
var debug = require('debug')('ember-try:dependency-manager-adapter:npm');
var rimraf = RSVP.denodeify(require('rimraf'));
var execFileSync = require('child_process').execFileSync;

module.exports = CoreObject.extend({
  init: function() {
    this._super.apply(this, arguments);
    this.run = this.run || require('../utils/run');
    this.adapterInit();
  },
  adapterInit: function() {
    try {
      execFileSync('yarn', [].concat(['--version']));
      this.useYarn = true;
      debug('Using yarn as dependency manager');
    } catch (e) {
      this.useYarn = false;
      debug('Using npm as dependency manager');
    }
  },
  useYarn: false,
  noLock: false,
  yarnLock: 'yarn.lock',
  yarnLockBackupFileName: 'yarn.lock.ember-try',
  configKey: 'npm',
  packageJSON: 'package.json',
  packageJSONBackupFileName: 'package.json.ember-try',
  nodeModules: 'node_modules',
  nodeModulesBackupLocation: '.node_modules.ember-try',
  setup: function() {
    return this._backupOriginalDependencies();
  },
  changeToDependencySet: function(depSet) {
    var adapter = this;
    depSet = depSet[adapter.configKey];

    debug('Changing to dependency set: %s', JSON.stringify(depSet));

    if (!depSet) { return RSVP.resolve([]); }
    var backupPackageJSON = path.join(adapter.cwd, adapter.packageJSONBackupFileName);
    var packageJSONFile = path.join(adapter.cwd, adapter.packageJSON);
    var packageJSON = JSON.parse(fs.readFileSync(backupPackageJSON));
    var newPackageJSON = adapter._packageJSONForDependencySet(packageJSON, depSet);

    debug('Write package.json with: \n', JSON.stringify(newPackageJSON));

    fs.writeFileSync(packageJSONFile, JSON.stringify(newPackageJSON, null, 2));
    if (this.useYarn) {
      this.noLock = true;
    }
    return adapter._install().then(function() {
      var deps = extend({}, depSet.dependencies || {}, depSet.devDependencies || {});
      var currentDeps = Object.keys(deps).map(function(dep) {
        return {
          name: dep,
          versionExpected: deps[dep],
          versionSeen: adapter._findCurrentVersionOf(dep),
          packageManager: adapter.useYarn ? 'yarn' : 'npm'
        };
      });

      debug('Switched to dependencies: \n', currentDeps);

      return RSVP.Promise.resolve(currentDeps);
    });
  },
  cleanup: function() {
    var adapter = this;
    var backupYarnLockPresent = fs.existsSync(path.join(this.cwd, this.yarnLockBackupFileName));
    return this._restoreOriginalDependencies(backupYarnLockPresent).then(function() {
      debug('Remove backup package.json and node_modules');

      var cleanupTasks = [rimraf(path.join(adapter.cwd, adapter.packageJSONBackupFileName)),
        rimraf(path.join(adapter.cwd, adapter.nodeModulesBackupLocation))];

      if (backupYarnLockPresent) {
        cleanupTasks.push(rimraf(path.join(adapter.cwd, adapter.yarnLockBackupFileName)));
        adapter.noLock = false;
      }

      return RSVP.all(cleanupTasks);
    }).catch(function(e) {
      console.log('Error cleaning up npm scenario:', e);
    })
    .then(function() {
      return adapter._install();
    });
  },
  _findCurrentVersionOf: function(packageName) {
    var filename = path.join(this.cwd, this.nodeModules, packageName, this.packageJSON);
    if (fs.existsSync(filename)) {
      return JSON.parse(fs.readFileSync(filename)).version;
    } else {
      return null;
    }
  },
  _install: function() {
    var adapter = this;
    var options = this.managerOptions || [];

    debug('Run npm install with options %s', options);

    var cmd = this.useYarn ? 'yarn' : 'npm';
    if (this.useYarn && this.noLock && options.indexOf('--no-lockfile') === -1) {
      options = options.concat(['--no-lockfile']);
    }

    return this.run(cmd, [].concat(['install'], options), { cwd: this.cwd }).then(function() {
      if (!adapter.useYarn) {
        debug('Run npm prune');
        return adapter.run(adapter.configKey, [].concat(['prune'], options), { cwd: adapter.cwd });
      }
    });
  },
  _packageJSONForDependencySet: function(packageJSON, depSet) {

    this._overridePackageJSONDependencies(packageJSON, depSet, 'dependencies');
    this._overridePackageJSONDependencies(packageJSON, depSet, 'devDependencies');
    this._overridePackageJSONDependencies(packageJSON, depSet, 'peerDependencies');

    return packageJSON;
  },
  _overridePackageJSONDependencies: function(packageJSON, depSet, kindOfDependency) {
    if (!depSet[kindOfDependency]) { return; }
    var pkgs = Object.keys(depSet[kindOfDependency]);

    pkgs.forEach(function(pkg) {
      if (!packageJSON[kindOfDependency]) {
        packageJSON[kindOfDependency] = {};
      }

      var version = depSet[kindOfDependency][pkg];
      if (version === null) {
        delete packageJSON[kindOfDependency][pkg];
      } else {
        packageJSON[kindOfDependency][pkg] = version;
      }
    });
  },
  _restoreOriginalDependencies: function(backupYarnLockPresent) {
    var copy = RSVP.denodeify(fs.copy);

    debug('Restoring original package.json and node_modules');

    var restoreTasks = [
      copy(path.join(this.cwd, this.packageJSONBackupFileName),
           path.join(this.cwd, this.packageJSON)),
      copy(path.join(this.cwd, this.nodeModulesBackupLocation),
           path.join(this.cwd, this.nodeModules), { clobber: true })
    ];

    if (backupYarnLockPresent) {
      restoreTasks.push(copy(path.join(this.cwd, this.yarnLockBackupFileName),
        path.join(this.cwd, this.yarnLock)));
    }

    return RSVP.all(restoreTasks);
  },
  _backupOriginalDependencies: function() {
    var copy = RSVP.denodeify(fs.copy);

    debug('Backing up package.json and node_modules');

    var backupTasks = [
      copy(path.join(this.cwd, this.packageJSON),
           path.join(this.cwd, this.packageJSONBackupFileName)),
      copy(path.join(this.cwd, this.nodeModules),
           path.join(this.cwd, this.nodeModulesBackupLocation), { clobber: true })];

    if (fs.existsSync(path.join(this.cwd, this.yarnLock)) && this.useYarn) {
      backupTasks.push(copy(path.join(this.cwd, this.yarnLock),
        path.join(this.cwd, this.yarnLockBackupFileName)));
    }

    return RSVP.all(backupTasks);
  }
});

@@ -822,25 +822,8 @@
 se,%0A
-  noLock: false,%0A
   ya
@@ -843,16 +843,16 @@
 .lock',%0A
+
   yarnLo
@@ -1856,64 +1856,8 @@
 ));%0A
-    if (this.useYarn) %7B%0A      this.noLock = true;%0A    %7D%0A
     
@@ -1880,16 +1880,28 @@
 install(
+this.useYarn
 ).then(f
@@ -2988,40 +2988,8 @@
 ));%0A
-        adapter.noLock = false;%0A
     
@@ -3174,16 +3174,21 @@
 install(
+false
 );%0A    %7D
@@ -3484,32 +3484,38 @@
 stall: function(
+noLock
 ) %7B%0A    var adap
@@ -3673,16 +3673,16 @@
  'npm';%0A
+
     if (
@@ -3697,21 +3697,16 @@
 Yarn && 
-this.
 noLock &

use noLock as parameter to _install instead of using as adapter object property