text: const { EventEmitter } = require('events')
  const util = require('util')
  const { nanoid } = require('nanoid')
  
  const {
    kSocketTeamity,
    kSocketId,
    kSocketRaw,
    kSocketQuery,
    kSocketSession,
    kSocketJoins,
  
    kTeamityRooms,
    kTeamityDeserializer,
  
    kQueueReplies
  } = require('./symbols')
  
  const { throwError } = require('./hooks')
  
  const { initSocketProperties } = require('./properties')
  
  function Socket (raw, query, session) {
    this[kSocketId] = nanoid()
    this[kSocketRaw] = raw
    this[kSocketQuery] = {}
    this[kSocketSession] = session || {}
    this[kSocketJoins] = {}
  
    initSocketProperties.call(this)
  
    for (const k in query) {
      this.$query[k] = query[k]
    }
  
    this.$raw.on('close', this.emit.bind(this, 'close'))
    this.$raw.on('error', this.emit.bind(this, 'error'))
    this.$raw.on('message', this.emit.bind(this, 'message'))
    this.$raw.on('ping', () => {
      this.$raw.pong()
    })
  
    this.on('message', _onMessage.bind(this))
    this.on('close', _onClose.bind(this))
  }
  
  util.inherits(Socket, EventEmitter)
  
  Socket.prototype.join = function (roomId) {
    const joins = this[kSocketJoins]
    const teamity = this[kSocketTeamity]
  
    if (joins[roomId] === undefined) {
      joins[roomId] = true
  
      const rooms = teamity[kTeamityRooms]
      if (!(roomId in rooms)) {
        rooms[roomId] = {}
      }
  
      const room = rooms[roomId]
      room[this.$id] = true
    }
  }
  
  Socket.prototype.leave = function (roomId) {
    const joins = this[kSocketJoins]
    const teamity = this[kSocketTeamity]
    if (joins[roomId] === true) {
      delete joins[roomId]
      const rooms = teamity[kTeamityRooms]
      if (!(roomId in rooms)) {
        return
      }
      const room = rooms[roomId]
      delete room[this.$id]
    }
  }
  
  Socket.prototype.leaveAll = function () {
    const joins = this[kSocketJoins]
    for (const roomId in joins) {
      this.leave(roomId)
    }
  }
  
  Socket.prototype.replyTo = function (url, reply) {
    const teamity = this[kSocketTeamity]
    teamity[kQueueReplies].push({
      url,
      reply,
      sockets: [this]
    })
  }
  
  function _onMessage (payload) {
    const teamity = this[kSocketTeamity]
    const deserializer = teamity[kTeamityDeserializer]
    if (typeof deserializer === 'function') {
      deserializer.call(teamity, payload, (err, url, body, attr) => {
        if (err) {
          throwError(teamity, err)
        } else {
          this.emit('decode', url, body, attr || {})
        }
      })
    } else {
      teamity.$log.error('No setting deserializer!!')
    }
  }
  
  function _onClose () {
    this.leaveAll()
  }
  
  module.exports = {
    Socket
  }
  