'use strict';

/**

* Web Notifications module.
* @module Growl
*/

/**

* Save timer references to avoid Sinon interfering (see GH-237).
*/

var Date = global.Date; var setTimeout = global.setTimeout; var EVENT_RUN_END = require('../runner').constants.EVENT_RUN_END;

/**

* Checks if browser notification support exists.
*
* @public
* @see {@link https://caniuse.com/#feat=notifications|Browser support (notifications)}
* @see {@link https://caniuse.com/#feat=promises|Browser support (promises)}
* @see {@link Mocha#growl}
* @see {@link Mocha#isGrowlCapable}
* @return {boolean} whether browser notification support exists
*/

exports.isCapable = function() {

var hasNotificationSupport = 'Notification' in window;
var hasPromiseSupport = typeof Promise === 'function';
return process.browser && hasNotificationSupport && hasPromiseSupport;

};

/**

* Implements browser notifications as a pseudo-reporter.
*
* @public
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/notification|Notification API}
* @see {@link https://developers.google.com/web/fundamentals/push-notifications/display-a-notification|Displaying a Notification}
* @see {@link Growl#isPermitted}
* @see {@link Mocha#_growl}
* @param {Runner} runner - Runner instance.
*/

exports.notify = function(runner) {

var promise = isPermitted();

/**
 * Attempt notification.
 */
var sendNotification = function() {
  // If user hasn't responded yet... "No notification for you!" (Seinfeld)
  Promise.race([promise, Promise.resolve(undefined)])
    .then(canNotify)
    .then(function() {
      display(runner);
    })
    .catch(notPermitted);
};

runner.once(EVENT_RUN_END, sendNotification);

};

/**

* Checks if browser notification is permitted by user.
*
* @private
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Notification/permission|Notification.permission}
* @see {@link Mocha#growl}
* @see {@link Mocha#isGrowlPermitted}
* @returns {Promise<boolean>} promise determining if browser notification
*     permissible when fulfilled.
*/

function isPermitted() {

var permitted = {
  granted: function allow() {
    return Promise.resolve(true);
  },
  denied: function deny() {
    return Promise.resolve(false);
  },
  default: function ask() {
    return Notification.requestPermission().then(function(permission) {
      return permission === 'granted';
    });
  }
};

return permitted[Notification.permission]();

}

/**

* @summary
* Determines if notification should proceed.
*
* @description
* Notification shall <strong>not</strong> proceed unless `value` is true.
*
* `value` will equal one of:
* <ul>
*   <li><code>true</code> (from `isPermitted`)</li>
*   <li><code>false</code> (from `isPermitted`)</li>
*   <li><code>undefined</code> (from `Promise.race`)</li>
* </ul>
*
* @private
* @param {boolean|undefined} value - Determines if notification permissible.
* @returns {Promise<undefined>} Notification can proceed
*/

function canNotify(value) {

if (!value) {
  var why = value === false ? 'blocked' : 'unacknowledged';
  var reason = 'not permitted by user (' + why + ')';
  return Promise.reject(new Error(reason));
}
return Promise.resolve();

}

/**

* Displays the notification.
*
* @private
* @param {Runner} runner - Runner instance.
*/

function display(runner) {

var stats = runner.stats;
var symbol = {
  cross: '\u274C',
  tick: '\u2705'
};
var logo = require('../../package').notifyLogo;
var _message;
var message;
var title;

if (stats.failures) {
  _message = stats.failures + ' of ' + stats.tests + ' tests failed';
  message = symbol.cross + ' ' + _message;
  title = 'Failed';
} else {
  _message = stats.passes + ' tests passed in ' + stats.duration + 'ms';
  message = symbol.tick + ' ' + _message;
  title = 'Passed';
}

// Send notification
var options = {
  badge: logo,
  body: message,
  dir: 'ltr',
  icon: logo,
  lang: 'en-US',
  name: 'mocha',
  requireInteraction: false,
  timestamp: Date.now()
};
var notification = new Notification(title, options);

// Autoclose after brief delay (makes various browsers act same)
var FORCE_DURATION = 4000;
setTimeout(notification.close.bind(notification), FORCE_DURATION);

}

/**

* As notifications are tangential to our purpose, just log the error.
*
* @private
* @param {Error} err - Why notification didn't happen.
*/

function notPermitted(err) {

console.error('notification error:', err.message);

}