text: 'use strict'
  
  const Joi = require('@hapi/joi')
  const Boom = require('@hapi/boom')
  const Chalk = require('chalk')
  const RestHapi = require('rest-hapi')
  const errorHelper = require('../utilities/error-helper')
  
  const Config = require('../../config')
  
  const CHAT_TYPES = Config.get('/constants/CHAT_TYPES')
  const authStrategy = Config.get('/restHapiConfig/authStrategy')
  
  const headersValidation = Joi.object({
    authorization: Joi.string().required()
  }).options({ allowUnknown: true })
  
  module.exports = function(server, mongoose, logger) {
    // Create the chat subscription
    ;(function() {
      // const Log = logger.bind(Chalk.magenta('Chat Subscription'))
  
      server.subscription('/chat/{userId}', {
        filter: function(path, message, options) {
          return true
        },
        auth: {
          scope: ['root', 'receiveChatMessages', '!-receiveChatMessages'],
          entity: 'user',
          index: true
        },
        onSubscribe: function(socket, path, params) {}
      })
    })(
      // Mark conversation as read
      (function() {
        const Log = logger.bind(Chalk.magenta('Mark Conversation As Read'))
  
        Log.note('Generating Mark Conversation As Read Endpoint for Chat')
  
        const markAsReadHandler = async function(request, h) {
          try {
            const Conversation = mongoose.model('conversation')
            const User = mongoose.model('user')
  
            return await RestHapi.addOne(
              Conversation,
              request.params._id,
              User,
              request.auth.credentials.user._id,
              'userData',
              { hasRead: true }
            )
          } catch (err) {
            errorHelper.handleError(err, Log)
          }
        }
  
        server.route({
          method: 'PUT',
          path: '/conversation/{_id}/read',
          config: {
            handler: markAsReadHandler,
            auth: {
              strategy: authStrategy,
              scope: [
                'root',
                'markConversationAsRead',
                '!-markConversationAsRead'
              ]
            },
            description: 'Mark the conversation as read for the current user.',
            tags: ['api', 'Chat', 'Mark as read'],
            validate: {
              headers: headersValidation
            },
            plugins: {
              'hapi-swagger': {
                responseMessages: [
                  { code: 200, message: 'Success' },
                  { code: 400, message: 'Bad Request' },
                  { code: 404, message: 'Not Found' },
                  { code: 500, message: 'Internal Server Error' }
                ]
              }
            }
          }
        })
      })()
    )
  
    // Mark conversation as unread
    ;(function() {
      const Log = logger.bind(Chalk.magenta('Mark Conversation As Unread'))
  
      Log.note('Generating Mark Conversation As Unread Endpoint for Chat')
  
      const markAsUnreadHandler = async function(request, h) {
        try {
          const Conversation = mongoose.model('conversation')
          const User = mongoose.model('user')
  
          return await RestHapi.addOne(
            Conversation,
            request.params._id,
            User,
            request.auth.credentials.user._id,
            'userData',
            { hasRead: false }
          )
        } catch (err) {
          errorHelper.handleError(err, Log)
        }
      }
  
      server.route({
        method: 'PUT',
        path: '/conversation/{_id}/unread',
        config: {
          handler: markAsUnreadHandler,
          auth: {
            strategy: authStrategy,
            scope: [
              'root',
              'markConversationAsUnread',
              '!-markConversationAsUnread'
            ]
          },
          description: 'Mark the conversation as unread for the current user.',
          tags: ['api', 'Chat', 'Mark as unread'],
          validate: {
            headers: headersValidation
          },
          plugins: {
            'hapi-swagger': {
              responseMessages: [
                { code: 200, message: 'Success' },
                { code: 400, message: 'Bad Request' },
                { code: 404, message: 'Not Found' },
                { code: 500, message: 'Internal Server Error' }
              ]
            }
          }
        }
      })
    })()
  
    // Get the current user's conversations
    ;(function() {
      const Log = logger.bind(Chalk.magenta('Get Current User Conversations'))
  
      Log.note('Generating Get Current User Conversations Endpoint for Chat')
  
      const getConversationsHandler = async function(request, h) {
        try {
          const Conversation = mongoose.model('conversation')
          const query = {
            $where: {
              users: { $elemMatch: { $eq: request.auth.credentials.user._id } }
            }
          }
  
          query.$embed = ['users', 'lastMessage.user', 'userData']
          query.$sort = ['-updatedAt']
  
          let result = await RestHapi.list(Conversation, query, Log)
  
          result.docs.forEach(function(conversation) {
            formatConversation(request, conversation)
          })
  
          return result
        } catch (err) {
          errorHelper.handleError(err, Log)
        }
      }
  
      server.route({
        method: 'GET',
        path: '/conversations/my',
        config: {
          handler: getConversationsHandler,
          auth: {
            strategy: authStrategy,
            scope: ['root', 'readMyConversations', '!-readMyConversations']
          },
          description: "Get the current user's conversations.",
          tags: ['api', 'Chat', 'Get Conversations'],
          validate: {
            headers: headersValidation
          },
          plugins: {
            'hapi-swagger': {
              responseMessages: [
                { code: 200, message: 'Success' },
                { code: 400, message: 'Bad Request' },
                { code: 404, message: 'Not Found' },
                { code: 500, message: 'Internal Server Error' }
              ]
            }
          }
        }
      })
    })()
  
    // Get the conversation between the current user and other users
    ;(function() {
      const Log = logger.bind(Chalk.magenta('Get Current User Conversation'))
  
      Log.note('Generating Get Current User Conversation Endpoint for Chat')
  
      const getConversationHandler = async function(request, h) {
        try {
          const Conversation = mongoose.model('conversation')
          const User = mongoose.model('user')
  
          let promise = {}
          let conversation = {}
          let newConversation = false
  
          const query = {
            $embed: ['messages.user', 'users', 'userData', 'lastMessage.user']
          }
  
          // Find the conversation by _id if provided
          if (request.query.conversation) {
            promise = RestHapi.find(
              Conversation,
              request.query.conversation,
              query,
              Log
            )
          } else if (request.query.user) {
            if (
              request.query.user === request.auth.credentials.user._id.toString()
            ) {
              throw Boom.badRequest('No chatting with yourself.')
            }
  
            // The query below searches for the direct chat conversation between the current user and the user provided
            query.$where = {
              $and: [
                {
                  users: {
                    $elemMatch: { $eq: request.auth.credentials.user._id }
                  }
                },
                { users: { $elemMatch: { $eq: request.query.user } } },
                { chatType: { $eq: CHAT_TYPES.DIRECT } }
              ]
            }
  
            promise = RestHapi.list(Conversation, query, Log)
          } else {
            throw Boom.badRequest(
              'Must provide either conversation or user query params.'
            )
          }
  
          let result = await promise
  
          if (request.query.conversation) {
            let me = result.users.find(function(user) {
              return (
                user._id.toString() ===
                request.auth.credentials.user._id.toString()
              )
            })
            if (!me) {
              throw Boom.badRequest(
                'Current user is not part of this conversation.'
              )
            }
            return result
          } else {
            // If the conversation doesn't exist, create it
            if (!result.docs[0]) {
              newConversation = true
              let promises = []
              let users = [request.auth.credentials.user._id, request.query.user]
              promises.push(
                RestHapi.create(
                  Conversation,
                  { users, chatType: CHAT_TYPES.DIRECT },
                  Log
                )
              )
              promises.push(
                RestHapi.list(
                  User,
                  {
                    _id: users,
                    $select: ['_id', 'firstName', 'lastName', 'profileImageUrl']
                  },
                  Log
                )
              )
              result = await Promise.all(promises)
            } else {
              result = result.docs[0]
            }
          }
  
          if (newConversation) {
            // Add the user docs to the new conversation object
            conversation = result[0]
            conversation.users = result[1].docs
            result = conversation
          }
  
          conversation = result
          if (newConversation) {
            // Associate the user data with the new conversation
            let users = conversation.users.map(function(user) {
              return user._id
            })
            conversation.hasRead = false
            await RestHapi.addMany(
              Conversation,
              conversation._id,
              User,
              'userData',
              users,
              Log
            )
          }
  
          formatConversation(request, conversation)
          return conversation
        } catch (err) {
          errorHelper.handleError(err, Log)
        }
      }
  
      server.route({
        method: 'GET',
        path: '/conversation/my',
        config: {
          handler: getConversationHandler,
          auth: {
            strategy: authStrategy,
            scope: ['root', 'readMyConversations', '!-readMyConversations']
          },
          description:
            'Get the conversation between the current user and other specified users.',
          tags: ['api', 'Chat', 'Get Conversation'],
          validate: {
            headers: headersValidation,
            query: {
              user: RestHapi.joiHelper.joiObjectId(),
              conversation: RestHapi.joiHelper.joiObjectId()
            }
          },
          plugins: {
            'hapi-swagger': {
              responseMessages: [
                { code: 200, message: 'Success' },
                { code: 400, message: 'Bad Request' },
                { code: 404, message: 'Not Found' },
                { code: 500, message: 'Internal Server Error' }
              ]
            }
          }
        }
      })
    })()
  
    // Post chat messages
    ;(function() {
      const Log = logger.bind(Chalk.magenta('Post Message'))
  
      Log.note('Generating Post Message endpoint for Chat')
  
      const postMessageHandler = async function(request, h) {
        try {
          const Message = mongoose.model('message')
          const Conversation = mongoose.model('conversation')
  
          const promises = []
  
          const payload = {
            text: request.payload.text,
            conversation: request.params.conversationId,
            user: request.auth.credentials.user._id
          }
  
          promises.push(
            RestHapi.find(Conversation, payload.conversation, {}, Log)
          )
          promises.push(RestHapi.create(Message, payload, Log))
  
          let result = await Promise.all(promises)
  
          let conversation = result[0]
          let message = result[1]
          Log.debug('MESSAGE:', message)
          conversation.users.forEach(function(userId) {
            server.publish('/chat/' + userId, message)
          })
          return 'published'
        } catch (err) {
          errorHelper.handleError(err, Log)
        }
      }
  
      // TODO: Authorize correct users for posting to a conversation
      server.route({
        method: 'POST',
        path: '/message/{conversationId}',
        config: {
          id: 'message',
          handler: postMessageHandler,
          auth: {
            strategy: authStrategy,
            scope: ['root', 'postChatMessage', '!-postChatMessage']
          },
          description: 'Post a message to a conversation.',
          tags: ['api', 'Chat', 'Post Message'],
          validate: {
            // headers: headersValidation,
            params: {
              conversationId: RestHapi.joiHelper.joiObjectId()
            },
            payload: {
              text: Joi.string().required()
            }
          },
          plugins: {
            'hapi-swagger': {
              responseMessages: [
                { code: 200, message: 'Success' },
                { code: 400, message: 'Bad Request' },
                { code: 404, message: 'Not Found' },
                { code: 500, message: 'Internal Server Error' }
              ]
            }
          }
        }
      })
    })()
  
    const formatConversation = function(request, conversation) {
      // Remove the current user from the list of users since it's implied
      conversation.users = conversation.users.filter(function(user) {
        return (
          user._id.toString() !== request.auth.credentials.user._id.toString()
        )
      })
      conversation.users = conversation.users.map(function(user) {
        return {
          _id: user._id,
          firstName: user.firstName,
          lastName: user.lastName,
          profileImageUrl: user.profileImageUrl
        }
      })
      if (conversation.lastMessage) {
        conversation.lastMessage.user = {
          _id: conversation.lastMessage.user._id,
          firstName: conversation.lastMessage.user.firstName,
          lastName: conversation.lastMessage.user.lastName,
          profileImageUrl: conversation.lastMessage.user.profileImageUrl
        }
        conversation.lastMessage.me =
          conversation.lastMessage.user._id.toString() ===
          request.auth.credentials.user._id.toString()
      }
      if (conversation.userData) {
        let currentUserData = conversation.userData.find(function(userData) {
          return (
            userData.user._id.toString() ===
            request.auth.credentials.user._id.toString()
          )
        })
  
        conversation.hasRead = currentUserData ? currentUserData.hasRead : false
        delete conversation.userData
      } else {
        conversation.hasRead = false
      }
  
      return conversation
    }
  }
  