var colors = require('colors');
var request = require('sync-request');
var WebSocketClient = require('websocket').client;

function SlackUser(username, token, emojiName) {
  this.uname = username;
  this.atoken = token;
  this.ename = emojiName;
  this.userData = {};
  this.currentMarks = [];
  this.allConversations = [];
  this.conversationNames = {};

  //this.LogToConsole('getting user data and setting up websocket connection');
  this.GetUserData();
  this.GetAllConversations();
  this.OpenWebsocket(this);
}

SlackUser.prototype.GetUserData = function() {
  this.userData = JSON.parse(request('GET', 'https://slack.com/api/rtm.start?token=' + this.atoken + '&pretty=1').body);
};

SlackUser.prototype.OpenWebsocket = function(context) {
  context.ws = new WebSocketClient();
  context.ws.on('connectFailed', function(error) {
    context.LogToConsole('connect error: ' + error.toString(), 'red');
  });

  context.ws.on('connect', function(connection) {
    context.LogToConsole('websocket client connected', 'green');
    context.InitializeAllConversations();
    connection.on('error', function(error) {
      context.LogToConsole('connection error: ' + error.toString(), 'red');

      // @TODO: retry in some seconds
      process.exit(1);
    });

    connection.on('close', function() {
      context.LogToConsole('websocket connection closed', 'yellow');

      // @TODO: retry in some seconds
      process.exit(1);
    });

    connection.on('message', function(message) {
      var messageData = JSON.parse(message.utf8Data);
      switch (messageData.type) {
        case 'channel_marked':
          context.UpdateMarker(messageData.channel, messageData.ts);
          break;
        case 'im_marked':
          context.UpdateMarker(messageData.channel, messageData.ts);
          break;
        case 'group_marked':
          context.UpdateMarker(messageData.channel, messageData.ts);
          break;
        default:
          break;
      }
    });
  });

  context.ws.connect(context.userData.url);
};

SlackUser.prototype.UpdateMarker = function(channel, timestamp) {
  this.GetMarks();
  this.SetMark(channel, timestamp);
};

// Compares current markers with last_read position
SlackUser.prototype.SetMark = function(channelId, timestamp) {
  var marks = this.currentMarks.length;
  var addMarker = true;
  while (marks--) {
    if (this.currentMarks[marks][channelId] === timestamp) {
      //this.LogToConsole('skipping ' + channelId + ' because it hasn\'t changed');
      this.currentMarks.splice(marks, 1);
      addMarker = false;
      continue;
    }

    if (this.currentMarks[marks][channelId] != undefined) {
      //this.LogToConsole('removing old marker in ' + channelId);
      var oldTimestamp = this.currentMarks[marks][channelId];
      var removeUrl = 'https://slack.com/api/reactions.remove?token=' + this.atoken + '&channel=' + channelId + '&timestamp=' + oldTimestamp + '&name=' + this.ename + '&pretty=1';

      try {
        request('GET', removeUrl).body;
      } catch (err) {
        this.LogToConsole('error: ' + err, 'red');
      }

      this.currentMarks.splice(marks, 1);
    }
  }

  if (addMarker) {
    this.LogToConsole('updating marker in ' + this.conversationNames[channelId]);
    var markUrl = 'https://slack.com/api/reactions.add?token=' + this.atoken + '&channel=' + channelId + '&timestamp=' + timestamp + '&name=' + this.ename + '&pretty=1';
    try {
      request('GET', markUrl);
    } catch (err) {
      this.LogToConsole('error: ' + err, 'red');
    }
  }
};

// Creates an array of all conversation IDs
SlackUser.prototype.GetAllConversations = function() {
  for (c = 0; c < this.userData.channels.length; c++) {
    if (this.userData.channels[c].is_member) {
      this.allConversations.push(this.userData.channels[c].id);
      this.conversationNames[this.userData.channels[c].id] = '#' + this.userData.channels[c].name;
    }
  }

  for (g = 0; g < this.userData.groups.length; g++) {
    this.allConversations.push(this.userData.groups[g].id);
    this.conversationNames[this.userData.groups[g].id] = this.userData.groups[g].name + ' group';
  }

  for (i = 0; i < this.userData.ims.length; i++) {
    this.allConversations.push(this.userData.ims[i].id);
    this.conversationNames[this.userData.ims[i].id] = this.GetUserName(this.userData.ims[i].user) + ' IM';
  }
};

// Sets read markers of all conversations when application is first started
SlackUser.prototype.InitializeAllConversations = function() {
  this.GetMarks();
  for (c = 0; c < this.userData.channels.length; c++) {
    if (this.userData.channels[c].is_member) {
      this.SetMark(this.userData.channels[c].id, this.userData.channels[c].last_read);
    }
  }

  for (g = 0; g < this.userData.groups.length; g++) {
    this.SetMark(this.userData.groups[g].id, this.userData.groups[g].last_read);
  }

  for (i = 0; i < this.userData.ims.length; i++) {
    if (this.userData.ims[i].latest)
      this.SetMark(this.userData.ims[i].id, this.userData.ims[i].last_read);
  }
};

// Remove markers from channels that user no longer subscribes to
SlackUser.prototype.CleanupChannelMarkers = function() {
  this.GetMarks();
  for (m = 0; m < this.currentMarks.length; m++) {
    var channelKey = Object.keys(this.currentMarks[m])[0];
    if (!this.allConversations.contains(channelKey)) {
      this.LogToConsole('removing old marker in ' + channelKey);
      var oldTimestamp = this.currentMarks[m][channelKey];
      var removeUrl = 'https://slack.com/api/reactions.remove?token=' + this.atoken + '&channel=' + channelKey + '&timestamp=' + oldTimestamp + '&name=' + this.ename + '&pretty=1';
      try {
        request('GET', removeUrl).body;
      } catch (err) {
        this.LogToConsole('error: ' + err, 'red');
      }
    }
  }
};

// Finds all reactions for the user that match their emoji marker name
SlackUser.prototype.GetMarks = function() {
  this.currentMarks = [];
  var reactionsUrl = 'https://slack.com/api/reactions.list?token=' + this.atoken + '&user=' + this.userData.self.id + '&full=false&count=100&page=1&pretty=1';
  try {
    var reactionRes = JSON.parse(request('GET', reactionsUrl).body);
    for (var r = 0; r < reactionRes.items.length; r++) {
      if (typeof reactionRes.items[r].message != 'undefined') {
        for (var rs = 0; rs < reactionRes.items[r].message.reactions.length; rs++) {
          if (reactionRes.items[r].message.reactions[rs].name == this.ename) {
            var tempObj = {};
            tempObj[reactionRes.items[r].channel] = reactionRes.items[r].message.ts;
            this.currentMarks.push(tempObj);
          }
        }
      }
    }
  } catch (err) {
    this.LogToConsole('error: ' + err, 'red');
  }
};

SlackUser.prototype.GetUserName = function(userId) {
  for (u = 0; u < this.userData.users.length; u++) {
    if (this.userData.users[u].id == userId)
      return this.userData.users[u].name;
  }
};

SlackUser.prototype.LogToConsole = function(txt, color) {
  if (typeof color === 'undefined')
    color = 'white';
  var dt = new Date();
  console.log(colors[color](dt.toLocaleString() + ' ' + this.uname + ' ' + txt));
};

Array.prototype.contains = function(obj) {
  var i = this.length;
  while (i--) {
    if (this[i] === obj) {
      return true;
    }
  }

  return false;
};

exports = module.exports = SlackUser;
