_ = require 'underscore'
Reflux = require 'reflux'
{Actions} = require 'nylas-exports'

VERBOSE = false
DISPLAY_TIME = 3000 # in ms

uuid_count = 0

class Notification
  constructor: ({@message, @type, @tag, @sticky, @actions, @icon} = {}) ->
    # Check to make sure the provided data is a valid notificaiton, since
    # notifications may be constructed by anyone developing on N1
    throw new Error "No `new` keyword when constructing Notification" unless @ instanceof Notification
    throw new Error "Type must be `info`, `developer`, `error`, or `success`" unless @type in ['info', 'error', 'success', 'developer']
    throw new Error "Message must be provided for notification" unless @message
    if @actions
      for action in @actions
        throw new Error "Actions must have an `label`" unless action['label']
        throw new Error "Actions must have an `id`" unless action['id']

    @tag ?= uuid_count++
    @creation = Date.now()
    @sticky ?= false
    unless @sticky
      @expiry = @creation + DISPLAY_TIME

    console.log "Created new notif with #{@tag}: #{@message}" if VERBOSE
    @

  valid: ->
    @sticky or @expiry > Date.now()

  toString: ->
    "Notification.#{@constructor.name}(#{@tag})"

module.exports =
NotificationStore = Reflux.createStore
  init: ->
    @_flush()

    # The notification store listens for user interaction with notififcations
    # and just removes the notifications. To implement notification actions,
    # your package should listen to notificationActionTaken and check the
    # notification and action objects.
    @listenTo Actions.notificationActionTaken, ({notification, action}) =>
      @_removeNotification(notification)() if action.dismisses
    @listenTo Actions.postNotification, (data) =>
      @_postNotification(new Notification(data))
    @listenTo Actions.multiWindowNotification, (data={}, context={}) =>
      @_postNotification(new Notification(data)) if @_inWindowContext(context)

  ######### PUBLIC #######################################################

  notifications: ->
    console.log(JSON.stringify(@_notifications)) if VERBOSE
    sorted = _.sortBy(_.values(@_notifications), (n) -> -1*(n.creation + n.tag))
    _.reject sorted, (n) -> n.sticky

  stickyNotifications: ->
    console.log(JSON.stringify(@_notifications)) if VERBOSE
    sorted = _.sortBy(_.values(@_notifications), (n) -> -1*(n.creation + n.tag))
    _.filter sorted, (n) -> n.sticky

  Notification: Notification

  ########### PRIVATE ####################################################

  _flush: ->
    @_notifications = {}

  _postNotification: (notification) ->
    console.log "Queue Notification.#{notification}" if VERBOSE
    @_notifications[notification.tag] = notification
    if notification.expiry?
      timeoutVal = Math.max(0, notification.expiry - Date.now())
      timeoutId = setTimeout(@_removeNotification(notification), timeoutVal)
      notification.timeoutId = timeoutId

    @trigger()

  # Returns a function for removing a particular notification. See usage
  # above in setTimeout()
  _removeNotification: (notification) -> =>
    console.log "Removed #{notification}" if VERBOSE

    clearTimeout(notification.timeoutId) if notification.timeoutId

    delete @_notifications[notification.tag]
    @trigger()

  # If the window matches the given context then we can show a
  # notification.
  _inWindowContext: (context={}) ->
    return true
