// Represents a single source of data, e.g. a single Facebook page or RSS feed.
// Sources keep track of any errors or warnings that are encountered during fetching.
// They also track how many of these errors have been 'read' so that the user can be notified if new errors
// have occurred since they last checked.
// The actual fetching is handled by the fetching module.

var database = require('../lib/database');
var mongoose = database.mongoose;
var validator = require('validator');
var _ = require('underscore');
require('../lib/error');

var EVENTS_TO_RETURN = 50;

var lengthValidator = function(str) {
  return validator.isLength(str, {min: 0, max: 20})
}

var urlValidator = function(url) {
  return (
    (typeof url === 'undefined' || url === null)
    || (typeof url === 'string' && url.length === 0)
    || validator.isURL(url)
  );
}

var mediaValues = ['facebook', 'elmo', 'twitter', 'rss', 'dummy', 'smsgh', 'whatsapp', 'dummy-pull', 'dummy-fast'];

var sourceSchema = new mongoose.Schema({
  media: { type: String, enum: mediaValues },
  nickname: { type: String, required: true, validate: lengthValidator },
  resource_id: String,
  url: { type: String, validate: urlValidator },
  keywords: String,
  enabled: { type: Boolean, default: true },
  events: { type: Array, default: [] },
  unreadErrorCount: { type: Number, default: 0 },
  lastReportDate: Date,
  user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: false },
  tags: { type: [String], default: [] }
});

sourceSchema.pre('save', function(next) {
  // Do not allow changing media
  if (!this.isNew && this.isModified('media')) return next(new Error.Validation('source_media_change_not_allowed'));
  // Notify when changing error count
  if (!this.isNew && this.isModified('unreadErrorCount')) {
    this._sourceErrorCountUpdated = true;
  }

  if (!this.isNew && this.isModified('enabled')) {
    this._sourceStatusChanged = true;
  }

  process.nextTick(next);
});

sourceSchema.post('save', function() {
  if (!this._silent) {
    sourceSchema.emit('source:save', { _id: this._id.toString() });
  }

  if (this._sourceStatusChanged) {
    var event = this.enabled ? 'source:enable' : 'source:disable';
    sourceSchema.emit(event, { _id: this._id.toString() });
  }

  if (this._sourceErrorCountUpdated) {
    sourceSchema.emit('sourceErrorCountUpdated');
  }
});

sourceSchema.pre('remove', function(next) {
  sourceSchema.emit('source:remove', { _id: this._id.toString() });
  next();
});

// Enable source
sourceSchema.methods.enable = function() {
  this.enabled = true;
};

// Disable source
sourceSchema.methods.disable = function() {
  this.enabled = false;
};

// Log events in source
sourceSchema.methods.logEvent = function(level, message, callback) {
  this.events.push({ datetime: new Date(), type: level, message: message });
  if (level === 'error') this.disable();
  this.unreadErrorCount++;
  this._silent = true;
  this.save(callback);
};

var Source = mongoose.model('Source', sourceSchema);

// Get latest unread error messages
Source.findByIdWithLatestEvents = function(_id, callback) {
  Source.findById(_id, function(err, source) {
    if (err) return callback(err);
    if (!source) return callback(null, null);
    if (source.events) {
      source.events = _.chain(source.events).sortBy('datetime').last(EVENTS_TO_RETURN).value();
    }
    callback(null, source);
  });
};

// Reset unread error count back to zero
Source.resetUnreadErrorCount = function(_id, callback) {
  Source.findById(_id, '-events', function(err, source) {
    if (err) return callback(err);
    if (!source) return callback(null, null);
    if (source.unreadErrorCount === 0) return callback(null, source);
    source.unreadErrorCount = 0;
    source._silent = true;
    source.save(callback);
  });
};

// Determine total number of errors
Source.countAllErrors = function(callback) {
  var pipeline = [
    { $group: { _id: null, unreadErrorCount: { $sum: '$unreadErrorCount' } } }
  ];
  Source.aggregate(pipeline, function(err, total) {
    if (err) callback(err);
    else if (total.length === 0) callback(null, 0);
    else callback(null, total[0].unreadErrorCount);
  });
};

Source.getMediaValues = function() {
  return mediaValues;
};
module.exports = Source;

@@ -715,60 +715,27 @@
     
-(typeof url === 'undefined' %7C%7C url =
+url 
 == null
-)
 %0A    %7C%7C 
 (typ
@@ -730,17 +730,16 @@
 %0A    %7C%7C 
-(
 typeof u
@@ -741,17 +741,17 @@
 eof url 
-=
+!
 == 'stri
@@ -757,29 +757,26 @@
 ing'
- && url.length
+%0A    %7C%7C url
  === 
-0)
+''
 %0A   
@@ -803,17 +803,16 @@
 url)%0A  )
-;
 %0A%7D%0A%0Avar 

check if URL is a string for urlValidator