# Description:
#   funzioni speciali per telegram-cli
#
# Configuration:
#   Uses hubot-tg enviroment variables
#
# Commands
#   hubot chi sei - chiedi a hubot di identificarsi
#   hubot chi sono / cosa sai di me - mostra il tuo contatto nel registro di hubot
#   hubot in che gruppi sei / dove scrivi - chiedi a hubot dove chatta
#   hubot invita/aggiungi <nome> - chiedi ad hubot di invitare qualcuno nel gruppo attuale
#   hubot (mi inviti / invitami) (nel gruppo / in) <gruppo> - chiedi ad hubot di invitardi in un gruppo
#   hubot (aggiungi[mi] / crea) (il mio / [tra]i contatto/i) [telefono] - chiedi ad hubot di creare il tuo contatto
#   hubot banna <nome> - chiedi di bannare un utente dal gruppo attuale (solo se hubot l'ha invitato)
#   hubot bannalo - chiedi ad hubot di bannare l'ultimo utente ad aver scritto un messaggio dal gruppo
#
# Author:
#   Michele Guerini Rocco (rnhmjoj)
#

net = require 'net'
lev = require 'fast-levenshtein'

module.exports = (robot) ->
  # messages
  error   = 'oh no: c\'è stato un errore, non so' 
  done    = ['fatto', 'ecco qui', 'ecco fatto']
  success = ['provvedo subito', 'ok', 'certo', 'va bene']
  unknown = ['non so chi sia', 'chi?', 'mai sentito', 'sicuro? non lo trovo']
  failed  = ['eh, mi piacerebbe molto ma non posso', 'i have no powers here',
             'nope, non l\'ho invitato io', 'non sono admin qui, sorry']
  banned  = [', ora non sarai più un problema', ' terminato',
             ' non farti più vedere', ', questa è la tua fine'
             ' sparito per sempre']

  # find closest match with levenshtein metric
  find_closest = (target, list) ->
    dists = [].concat.apply [], (
      for str in list
        for word in [str].concat str.split ' '
          [(lev.get target, word), str] )
    [dist, name] = dists.reduce (x, y) ->
      if x[0] < y[0] then x else y
    return if dist < 4 then name else null

  # format a name as telegram-cli likes it
  norm = (x) -> x.replace /\ /g, '_'

  # return a list of every match for a regex
  match_all = (regex, str) ->
    matches = []
    str.replace regex, ->
      arr = [].slice.call arguments, 0
      extras = arr.splice -2
      arr.index = extras[0]
      arr.input = extras[1]
      matches.push arr
    matches

  # directly run a command in telegram-cli and return its output
  # (a list of strings)
  run_command = (command, callback) ->
    client = net.connect robot.adapter.port, robot.adapter.host, ->
      client.write command+'\n'
    client.setEncoding 'utf8'
    client.on 'data', (reply) ->
      if callback?
        res = (reply.split '\n')[1..-3]
        [..., err] = res[0].split ' '
        callback res, err
        client.end()

  # return an object with the known chat information
  chat_info = (name, callback) ->
    run_command "chat_info #{norm name}", (data) ->
      match = data[0].match ///
        Chat\ (\w+(\ ?\w+)*)  # chat name
        \ \(id\ (\d+)\)       # chat id
        ///
      chat =
        name: match[1]
        id: 'chat#'+match[3]
        members: data[1..].map (i) -> (i.split ' invited')[0].replace /\t/g, ''
      robot.logger.info 'parsed chat data: ' + JSON.stringify chat, null, 2
      callback chat

  # return an object with the known user information
  user_info = (name, callback) ->
    run_command "user_info #{norm name}", (data) ->
      match = (data.join '\n').match ///
        User\ (\w+(\ ?\w+)*)  # contact name
        (\ @(\w+))?           # telegram username
        (\ \(\#(\d+)\))?      # telegram id
        [\s\S]+phone:\ (\d+)? # phone number
        ///
      user =
        name:     match[1]
        username: match[4]
        id:       'user#'+match[6]
        phone:    match[7]
      robot.logger.info 'parsed user data: ' + JSON.stringify user, null, 2
      callback user

  # get hubot telegram profile information
  robot_info = (callback) ->
    run_command 'get_self', (data) ->
      match = (data.join '\n').match ///
        User\ (\w+(\ ?\w+)*)  # contact name
        (\ @(\w+))?           # telegram username
        (\ \(\#(\d+)\))?      # telegram id
        [\s\S]+phone:\ (\d+)? # phone number
        ///
      user =
        name:     match[1]
        username: match[4]
        id:       'user#'+match[6]
        phone:    match[7]
      robot.logger.info 'parsed robot data: ' + JSON.stringify user, null, 2
      callback user

  # return the list of active chat groups
  chat_list = (callback) ->
    run_command 'dialog_list', (list) ->
      chats = list.filter (item) -> not item.lastIndexOf 'Chat', 0
      chats = chats.map (chat) -> (chat.match /Chat (.+):/)[1]
      robot.logger.info 'parsed bot chats list: ' + JSON.stringify chats, null, 2
      callback chats

  # return the list of contacts (names only)
  contact_list = (callback) ->
    run_command 'contact_list', callback

  # add a user to the contact list
  add_contact = (name, phone, callback) ->
    robot.logger.info "create contact for user #{name}"
    user_info name, (user) ->
      [first, last...] = user.name.split ' '
      phone = phone or user.phone
      if not phone?
        return callback 'serve per forza il tuo numero'
      run_command "add_contact #{phone} '#{first}' '#{last.join ' '}'", (reply) ->
        robot.logger.info 'result: ' + reply
        reply = reply[0].split ' '
        if reply[0] is 'FAIL:'
          return callback reply[1..]
        callback()

  # get the messages history for a chat group
  get_history = (chat, size, callback) ->
    regex = /\[(.+)\] (.+) [>«»]+ ((?:(?!\n(\[|\d))[\s\S])+)/g
    parse_line = (x) ->
      date: x[1]
      peer: x[2].replace(chat, '').trim()
      text: x[3].trim().replace '\n', ' '
    run_command "history #{norm chat} #{size}", (lines) ->
      callback ((match_all regex, lines.join '\n').map parse_line)

  # ban user from a group chat
  delete_user = (user, chat, res) ->
    run_command "chat_del_user #{chat.id} #{user.id}", (reply, err) ->
      robot.logger.info "delete user #{user.name} from chat #{chat.name}"
      robot.logger.info 'result: ' + reply[0]
      if err is 'SUCCESS'
        robot.logger.info 'user deleted'
        res.send user.name + res.random banned
      else if err is 'CHAT_ADMIN_REQUIRED'
        robot.logger.warning 'cannot delete user: not authorized'
        res.send res.random failed
      else
        robot.logger.error 'error:\n' + reply.join '\n'
        res.send error 

  # add user to a group chat
  add_user = (user, chat, res) ->
    run_command "chat_add_user #{chat.id} #{user.id}", (reply, err) ->
      robot.logger.info "add user #{user.name} to chat #{chat.name}"
      robot.logger.info 'result: ' + reply[0]
      if err is 'SUCCESS'
        robot.messageRoom chat.id, res.random done
      else if err is 'USER_NOT_MUTUAL_CONTACT'
        robot.logger.warning 'cannot add user: not authorized'
        res.send 'ah, non posso: ho bisogno del contatto'
      else if err is 'USER_ALREADY_PARTICIPANT'
        robot.logger.warning 'cannot add user: user already present'
        res.send 'ma è già qui!'
      else if err is 'CHAT_ADMIN_REQUIRED'
        robot.logger.warning 'cannot add user: not authorized'
        res.send res.random failed
      else
        robot.logger.error 'error:\n' + reply.join '\n'
        res.send error

  robot.respond /chi sei/, (res) ->
    robot_info (user) ->
      username = if user.username? then " noto anche come #{user.username}" else ''
      phone    = if user.phone?    then " chiamatemi al #{user.phone}. "    else '. '
      id       = "il mio id di telegram è #{user.id}"
      res.send "sono #{user.name}#{username},#{phone}#{id}"

  robot.respond /(cosa sai di me|chi sono)/i, (res) ->
    user_info res.message.user.name, (user) ->
      username = if user.username? then " ma ti chiamano anche #{user.username}" else ''
      phone    = if user.phone?    then " so il tuo numero: #{user.phone} e "    else ' '
      id       = "il tuo id di telegram è #{user.id}"
      res.send "so che sei #{user.name}#{username},#{phone}#{id}"

  robot.respond /(in che gruppi sei| dove scrivi)/, (res) ->
    intro = ['scrivo in questi gruppi', 'chatto qui', 'sono attivo in']
    chat_list (list) ->
      res.send (res.random intro) + ':\n' + (list.map (i) -> '* '+i).join '\n'

  robot.respond /(aggiungi(mi)?|crea) ((trai? (i )?contatti)|(il mio contatto))( (\d+))?/, (res) ->
    add_contact res.message.user.name, res.match[8], (err) ->
      if err?
        res.send "c'è un problema: " + err
      else
        res.send "fatto!"

  robot.respond /(invita|aggiungi) (.+)/, (res) ->
    name = res.match[2]
    id = res.message.room
    if id.match /^user/
      return res.send 'non è un gruppo questo'
    contact_list (contacts) ->
      name = find_closest(name, contacts)
      if not name?
        return res.send res.random unknown
      res.send res.random success
      user_info name, (user) ->
        chat_info id, (chat) ->
          add_user user, chat, res

  robot.respond /(mi )?invit(i|ami) (in|nel gruppo) ([^?]+)\??/, (res) ->
    denied = ['BZBZ ADMIN-NOT-DETECTED', 'BZBZ IS-NOT-AUTHORIZED',
              'BZBZ ACCESS-DENIED']
    failed = ['ahahah NO', 'mai sentito questo',
              'invita anche me magari', 'che?']
    user = res.message.user
    name = res.match[4]
    admin_id = process.env['ADMIN_ROOM']
    chat_list (list) ->
      if not (name in list)
          return res.send res.random failed
      chat_info name, (chat) ->
        if chat.id is admin_id
          return res.send res.random denied
        if user.name in chat.members
          return res.send 'ma sei già nel gruppo... [facepalm]'
        res.send res.random success
        add_user user, chat, res

  robot.respond /banna(l(o|a)| (.+))/, (res) ->
    if res.message.room.match /^user/
      return res.send 'non è un gruppo questo'
    chat_info res.message.room, (chat) ->
      get_history chat.name, 2, (history) ->
        robot_info (self) ->
          target = res.match[3]
          if not target?
            name = history[0].peer
          else
            name = find_closest(target, chat.members)
            if not name?
              return res.send res.random unknown 
            if name in [res.message.user.name, self.name]
              return res.send 'ma sei scemo o cosa?'
          user_info name, (user) -> delete_user user, chat, res