# Description:
#   integrazione con il protocollo matrix
#
# Requires:
#   "fast-levenshtein": "1.0.6"
#
# 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 stanze sei / dove scrivi - chiedi a hubot dove chatta
#   hubot (mi inviti / invitami) (nel stanza / in) <stanza> - chiedi ad hubot di invitardi in una stanza
#   hubot invita/aggiungi <nome> - chiedi ad hubot di invitare qualcuno nel stanza attuale
#   hubot kicka <nome> - chiedi di bannare un utente dal stanza attuale
#   hubot kickalo - chiedi ad hubot di bannare l'ultimo utente ad aver scritto un messaggio
#   hubot esci / lasciaci un (minuto / attimo) - chiedi ad hubot di lasciare la stanza
#
# Author:
#   Michele Guerini Rocco (rnhmjoj)
#

lev = require 'fast-levenshtein'

module.exports = (robot) ->
  matrix = robot.adapter.client
  return unless robot.adapterName is 'matrix' and matrix?

  # 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']

  # find closest match with levenshtein metric
  find_closest = (target, list, compare) ->
    return null if list.length is 0

    compare = compare || ((x) -> x)
    dists = [].concat.apply [], (
      for obj in list
        str = compare obj
        for word in [str].concat str.split ' '
          [(lev.get target, word), obj])
    [dist, best] = dists.reduce (x, y) ->
      if x[0] < y[0] then x else y
    return if dist < 4 then best else null


  # get hubot telegram profile information
  robot_info = ->
    self = robot.brain.userForId robot.adapter.user_id


  # return the list of chat rooms
  room_list = ->
    matrix.getRooms().map (room) ->
      create_user = (user) ->
        id: user.userId
        name: user.name
        avatar: user.getAvatarUrl matrix.baseUrl, 120, 120, allowDefault: false

      id: room.roomId
      name: room.name
      private: room.getJoinedMembers().length == 2
      members: room.getJoinedMembers().map create_user
      invitees: room.getMembersWithMembership("invite").map create_user


  # return the list of contacts
  contact_list = ->
    matrix.getUsers().map (user) ->
      id: user.userId
      name: user.displayName
      avatar: user.avatarUrl


  # get the messages history for a chat room
  get_history = (room, size, callback) ->
    room = matrix.getRoom room.id
    matrix.scrollback room, size, (err, res) ->
      return callback err, null if err?
      return callback 1, null unless res.chunk?

      callback null, res.chunk.map (event) ->
        type: event.content.msgtype
        sender: robot.brain.userForId event.sender
        content: event.content.body


  # kick user from a chat room
  kick_user = (user, room, reason, callback) ->
    matrix.kick user.id, room.id, ->
      callback() if callback?


  # add user to a group chat
  invite_user = (user, room, callback) ->
    matrix.invite room.id, user.id, ->
      callback() if callback?


  robot.respond /chi sei/, (res) ->
    matrix = robot_info()
    res.send "sono #{robot.name} ma puoi anche chiamarmi #{robot.alias}",
             "se ti serve qualcosa chiedi: il mio id è #{matrix.id}"

  robot.respond /(cosa sai di me|chi sono)/i, (res) ->
    user   = res.message.user
    id     = "il tuo id matrix è #{user.id}"
    name   = if user.name? then " ma preferisci che ti chiamino #{user.name}" else ''
    avatar = if user.avatar? then [ "il tuo avatar è", user.avatar ] else []
    res.send ([ "so che #{id}#{name}" ].concat avatar)...

  robot.respond /(in che stanze sei| dove scrivi)/, (res) ->
    intro = ['scrivo in queste stanze', 'chatto qui', 'sono attivo in']
    rooms = room_list().filter (room) -> not room.private

    res.send (res.random intro) + ':\n' +
      (rooms.map (i) -> '* '+i.name).join '\n'

  robot.respond /(invita|aggiungi) (.+)/, (res) ->
    name  = res.match[2]
    room  = res.message.room
    users = contact_list()
    user  = find_closest name, users, (x) -> x.name

    if not user?
      return res.send res.random unknown
    if user.id is res.message.user.id
      return res.send 'intendi te stesso?'
    if user.id is robot_info().id
      return res.send 'io? mi sembra di esserci già'
    if user.id in (room.members.map (x) -> x.id)
      return res.send 'è già in questa stanza...', '[facepalm]'
    if user.id in (room.invitees.map (x) -> x.id)
      return res.send 'già invitato: deve solo accettare'

    res.send res.random success ->
      invite_user user, room ->
        res.send res.random done
        robot.logger.info "invited user #{user.id} to #{room.id}"

  robot.respond /(mi )?invit(i|ami) (in|nella stanza) ([^?]+)\??/, (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
    room     = find_closest res.match[4], room_list(), (x) -> x.name
    admin_id = process.env['ADMIN_ROOM']

    if room.id is admin_id or room.private
      return res.send res.random denied
    if not room?
      return res.send res.random failed
    if user.id in (room.members.map (x) -> x.id)
      return res.send 'ma sei già nella stanza...', '[facepalm]'
    if user.id in (room.invitees.map (x) -> x.id)
      return res.send 'lo sei già: devi solo accettare'

    invite_user user, room, ->
      res.send res.random success
      res.messageRoom room, "#{user.name}: benvenuto in #{room.name}!"
      robot.logger.info "invited user #{user.id} to #{room.id}"

  robot.respond /kicka(l(o|a)| (.+))/, (res) ->
    withdrew = ['invito revocato', 'ho annullato l\'invito']
    room     = res.message.room
    target   = res.match[3]

    if not target?
      get_history room, 1, (err, history) ->
        return if err is 1
        return res.send "errore nel leggere la history: #{err}" if err?
        user = history[0].sender

        if user.id in [res.message.user.id, robot_info().id]
          return res.send 'ma sei scemo o cosa?'
        kick_user room, user, "ordini di #{res.message.user.name}. niente di personale"
    else
      user = find_closest target, room.members, (x) -> x.name
      if not user?
        user = find_closest target, room.invitees, (x) -> x.name
        if not user?
          return res.send res.random unknown
        return kick_user room, user, "", ->
          res.send res.random withdrew
          robot.logger.info "withdraw invitee #{user.id} from #{room.id}"

      if user.id in [res.message.user.id, robot_info().id]
        return res.send 'ma sei scemo o cosa?'

      kick_user room, user, "ordini di #{res.message.user.name}. niente di personale"
      robot.logger.info "kicked user #{user.id} from #{room.id}"

  robot.respond /esci|lasciaci un (minuto|attimo)|dobbiamo parlare in privato/, (res) ->
    leave = [ 'come vuoi, vado', 'ok, esco',
              'esco dalla stanza', 'me ne vado']
    res.send (res.random leave), ->
      matrix.leave res.message.room.id, ->
        matrix.forget res.message.room.id
        robot.logger.info "left room #{res.message.room.id}"