/*
 * grunt-notify
 * https://github.com/dylang/grunt-notify
 *
 * Copyright (c) 2013 Dylan Greene
 * Licensed under the MIT license.
 */
'use strict';

module.exports = function (grunt, options) {

  var message_count = 0;
  var StackParser = require('stack-parser');
  var notify = require('../notify-lib');
  var removeColor = require('../util/removeColor');

  var cwd = process.cwd();

  function toggleSuccessNotify() {
    if (!!options.success) {
      grunt.util.hooker.hook(grunt.log, 'success', notifyHook);
    } else {
      grunt.util.hooker.unhook(grunt.log, 'success');
    }
  }

  function watchForContribWatchWarnings(e) {

    if (!e || typeof e !== 'string') {
      return;
    }

    var msg = removeColor(e);
    msg = msg.replace(' Use --force to continue.', '');

    if (msg.indexOf('Warning:') === 0) {
      return notifyHook(msg.replace('Warning: ', ''));
    }
    if (msg.indexOf('Fatal error:') === 0) {
      return notifyHook(msg.replace('Fatal error: ', ''));
    }

    return e;
  }

  function exception(e) {
    var stackDump, stack, message;

    if (e.stack && typeof e.stack !== 'string') {
      stackDump = StackParser.parse(e.stack);
      stack = stackDump[0];

      // Find the first stack that isn't a node module or an internal Node function
      while (stack && (stack.file.match('/node_modules') || !stack.file.match('/'))) {
        stack = stackDump.shift();
      }
    }

    if (stack && stack.file) {
      message = [
        stack.file.replace(cwd, ''),
        'Line ' + stack.line,
        e.message
      ].join('\n');
    } else {
      message = e.message;
    }

    return message;
  }

  /**
   * Hook for showing the automatic message
   * @param e Exception or message
   * @returns {*}
   */
  function notifyHook(e) {

    message_count++;

    var message;

    if (!options.enabled) {
      return;
    }

    if (!e) {
      return;
    }

    if (e && e.length === 1) {
      e = e[0];
    }

    if (/Task .* failed\./.test(e.message)) {
      message = e.message;
    } else if (e.message && e.stack) {
      message = exception(e);
    } else {
      message = e + '';
    }

    if (message_count > 0 && message === 'Aborted due to warnings.') {
      // skip unhelpful message because there was probably another one that was more helpful
      return;
    }

    // shorten message by removing full path
    // TODO - make a global replace
    message = message.replace(cwd, '').replace('\x07', '');

    return notify({
      title: options.title + (grunt.task.current.nameArgs ? ' ' + grunt.task.current.nameArgs : ''),
      message: message,
      duration: options.duration
    });
  }

  // run on warning
  grunt.util.hooker.hook(grunt, 'warn', notifyHook);
  grunt.util.hooker.hook(grunt.fail, 'warn', notifyHook);
  // run on error
  grunt.util.hooker.hook(grunt.fail, 'error', notifyHook);
  grunt.util.hooker.hook(grunt.log, 'fail', notifyHook);
  grunt.util.hooker.hook(grunt.log, 'error', notifyHook);
  // run on fatal
  grunt.util.hooker.hook(grunt.fail, 'fatal', notifyHook);
  // run on success
  toggleSuccessNotify();

  // grunt-contrib-watch replaces some of these functions so
  // we need to watch all writeln's too
  // https://github.com/gruntjs/grunt-contrib-watch/issues/232
  grunt.util.hooker.hook(grunt.log, 'writeln', watchForContribWatchWarnings);

  function setOptions(opts) {
    options = opts;
    toggleSuccessNotify();
  }

  return {
    setOptions: setOptions
  };
};
