const express = require('express')
const app = express()
const port = process.env.HVACKER_API_PORT || 3001
const crypto = require('crypto')
const base64 = require('base-64')
const slack = require('../../slack')
const { game: { users }, getUser, fuzzyMatcher, saveUser } = require('./utils')

const apiGetUserId = hash => {
  return Object.entries(users)
    .filter(([id, user]) => user.pwHash === hash)
    .map(([id, user]) => id)[0]
}

const makeHash = pw =>
  crypto.createHash('md5')
    .update(pw)
    .digest('hex')

const emojiMaps = [
  ['mouse2', '🐭'],
  ['male-office-worker', '🧑‍💼'],
  ['whale', '🐋'],
  ['train2', '🚂'],
  ['fire', '🔥'],
  ['boomerang', '🪃'],
  ['new_moon_with_face', '🌚'],
  ['butterfly', '🦋'],
  ['mirror', '🪞'],
  ['quade', '🧔‍♂️'],
  ['hvacker_angery', '🦆'],
  ['grey_question', '❓'],
  ['convenience_store', '🏪'],
  ['office', '🏢'],
  ['japanese_castle', '🏯']
]

app.get('/alive', (req, res) => {
  res.send('OK')
})

const illegalCommands = ['!', '!c', '!coin', '!mine']
const lastCalls = {}
const addCommand = ({ commandNames, helpText, action, condition, hidden }) => {
  const route = async (req, res) => {
    let sent
    const say = async msg => {
      if (sent) {
        console.log('attempted double-send of', { msg })
        return
      }
      const isObj = typeof msg === 'object'
      if (isObj) {
        msg = JSON.stringify(msg, null, 2)
      }
      for (const entry of emojiMaps) {
        msg = msg.replaceAll(`:${entry[0]}:`, entry[1])
      }
      msg = msg.replaceAll(/\*([^*]+)\*/g, '$1')
          .replaceAll(/_([^_]+)_/g, '$1')
      res.send((typeof msg === 'object' ? JSON.stringify(msg, null, 2) : msg) + '\n')
      sent = true
    }
    try {
      const keys = [...Object.keys(req.query)]
      keys.reverse()
      const words = [commandNames[0], ...keys]
      console.log({ words })
      const [commandName, ...args] = words
      console.log('INCOMING API CALL:', commandName, words)
      const encoded = req.header('Authorization').substring(5)
      const decoded = base64.decode(encoded).substring(1)
      const hash = makeHash(decoded)

      const event = {
        user: apiGetUserId(hash)
      }
      const user = await getUser(event.user)
      if (user.name !== 'TEST-USER' && illegalCommands.includes(commandName?.toLowerCase())) {
        res.send('Command is illegal over the web api!')
        return
      }
      if (!event.user) {
        res.status(400)
        res.send(`User with that password does not exist, or user does not have a password.
See '!setpw help' for assistance.\n`)
        return
      }

      if (words[1] === 'help') {
        await say(commandNames.map(name => `\`${name}\``).join(', ') + ': ' + helpText)
        if (commandNames.includes('!coin')) {
          addAchievement(user, 'weAllNeedHelp', say)
        }
        return
      }
      const haunted = false
      const canUse = await condition({ event, say, words, commandName, args, user, userId: event.user, isAdmin: event.user.includes(slack.users.Admin) })
      if (!canUse) {
        await say(`Command '${words[0]}' not found`)
        return
      }
      await action({ event, say, trueSay: say, words, args, commandName, user, userId: event.user, haunted })
      await saveUser(event.user, user, 'webapi action')
    } catch (e) {
      console.error('route error', e)
      const example = "`curl --location-trusted -u ':your-pw' quacker.sagev.space/lb`"
      await say(`Routing error. Make sure you've set up API access with the !setpw command in slack!
Then you can use calls like ${example}
N.b. --location-trusted is needed because quacker.sagev.space is technically a redirect, and your headers need to be forwarded.`)
    }
  }
  commandNames.forEach(name => name !== '!!help' &&
    app.get('/' + name.replace(/!/gi, ''), async (req, res) => console.log('route', name.replace('/' + /!/gi, '')) || await route(req, res))
  )
}

module.exports = {
  addCommand,
  makeHash,
  launch: () => app.listen(port, () => {
    console.log(`Express listening on port ${port}`)
  })
}
