const slack = require('../slack')
const { updateAll } = require('../games/hvacoins/utils')

const tie = 'TIE'

const messageFromBoard = ({ dataName, gameName, textFromBoard, board, player1, player2, channelMap }) =>
  gameName + ' between ' + player1.toUpperCase() + ' and ' + player2.toUpperCase() + ' ' + encodeGame(dataName, board, [player1, player2], channelMap) + '\n' +
  '```' + textFromBoard(board) + '\n```'

const addChoiceEmojis = async ({ choices, channel, ts }) => {
  const addEmoji = async emojiName => {
    try {
      await slack.app.client.reactions.add({
        channel,
        timestamp: ts,
        name: emojiName
      })
    } catch (ignore) {
    }
  }
  for (const choice of choices) {
    await addEmoji(choice)
  }
}

const buildGameStarter = ({ startTriggers, dataName, gameName, textFromBoard, initialBoard, turnChoiceEmojis }) => async ({ event, say }) => {
  if (event.channel_type !== 'im') {
    return;
  }
  const eventText = event.text?.toLowerCase()
  if (!(eventText && startTriggers.find(keyword => eventText.startsWith('!' + keyword)))) {
    return;
  }
  try {
    console.log('Trigger found')
    const opponent = event.text.toUpperCase().match(/<@[^>]*>/)[0]
    console.log('Messaging opponent ' + slack.users[opponent.substring(2, opponent.length - 1)])
    const channelMap = {}
    const msg = () => messageFromBoard({
      dataName,
      gameName,
      textFromBoard,
      board: initialBoard(),
      player1: '<@' + event.user + '>',
      player2: opponent,
      channelMap
    })
    const sent = await say(msg())
    channelMap[event.user] = {
      channel: sent.channel,
      ts: sent.ts
    }
    await updateAll({ name: gameName, text: msg(), add: sent })
    await addChoiceEmojis({...sent, choices: turnChoiceEmojis})
  } catch (e) {
    console.error(e)
  }
}

const encodeGame = (dataKey, board, players, channelMap = {}) => slack.encodeData(dataKey, { board, players, channelMap })

const decodeGame = (dataKey, message) => slack.decodeData(dataKey, message)

const getMessages = winner => {
  const buildMessage = state => !winner ? '' : winner === tie ? '\nIt\'s a tie!' : `\nYou ${state}!`
  return {
    you: buildMessage('win'),
    opponent: buildMessage('lost')
  }
}

const buildTurnHandler = ({ gameName, dataName, checkWinner, textFromBoard, turnChoiceEmojis, makeMove }) => async ({ event, say }) => {
  if (event.item_user !== slack.users.Hvacker || !turnChoiceEmojis.includes(event.reaction)) {
    console.log('bad item_user/reaction')
    return
  }

  const message = await slack.getMessage(event.item)
  if (!message.messages[0]?.text?.includes(gameName)) {
    return
  }

  const game = decodeGame(dataName, message.messages[0].text)
  if (!game) {
    console.log('could not decode game')
    return
  }

  game.channelMap ??= {}
  const { board, players, channelMap } = game
  let winner = checkWinner(board)
  if (winner) {
    console.log('winner found: ' + winner)
    return
  }

  const [player1, player2] = players
  let opponent = (player1.includes(event.user) ? player2 : player1)
  opponent = opponent.replace(/[^A-Z0-9]/gi, '').toUpperCase()

  if (!makeMove(event.reaction, board)) {
    await say('You can\'t go there!')
    return
  }
  winner = checkWinner(board)

  const boardMessage = () => messageFromBoard({
    dataName,
    gameName,
    textFromBoard,
    board,
    player1,
    player2,
    channelMap
  })
  if (winner) {
    await updateAll({ name: gameName, text: boardMessage() + '\nSomebody won! I do not yet know who!' })
    const removeEmoji = emojiName => Object.values(channelMap).forEach(({ channel, ts }) =>
      slack.app.client.reactions.remove({
        channel,
        timestamp: ts,
        name: emojiName
      }))
    turnChoiceEmojis.forEach(removeEmoji)
    return
  }
  const winnerMessages = getMessages(winner)
  // await say(boardMessage() + winnerMessages.you)
  console.log('TurnHandler', { gameName, boardMessage: boardMessage() })
  // await updateAll({ name: gameName, text: boardMessage() + '\nTurnHandler' })
  // if (!winner) {
  //   await say('Waiting for opponent\'s response...')
  // }

  // const removeEmoji = async emojiName =>
  //   slack.app.client.reactions.remove({
  //     channel: event.item.channel,
  //     timestamp: message.messages[0]?.ts,
  //     name: emojiName
  //   })
  // turnChoiceEmojis.forEach(removeEmoji)
  console.log('SENDING to ' + opponent)
  if (!channelMap[opponent]) {
    const sentBoard = await slack.app.client.chat.postMessage({
      channel: opponent,
      text: boardMessage() + winnerMessages.opponent
    })
    channelMap[opponent] = {
      channel: sentBoard.channel,
      ts: sentBoard.ts
    }
    console.log('BOARD MESSAGE AFTER ')
    await updateAll({ name: gameName, text: boardMessage(), add: sentBoard })
  } else {
    await updateAll({ name: gameName, text: boardMessage() })
  }
  if (!winner) {
    const sentBoard = channelMap[opponent]
    await addChoiceEmojis({ ...sentBoard, choices: turnChoiceEmojis })
  }
}

module.exports = {
  tie,
  build: ({
    startTriggers,
    initialBoard,
    turnChoiceEmojis,
    gameName,
    textFromBoard,
    checkWinner,
    makeMove
  }) => {
    const dataName = gameName.replace(/[^0-9A-Z]/gi, '')
    const gameStarter = buildGameStarter({
      startTriggers,
      gameName,
      dataName,
      initialBoard,
      textFromBoard,
      turnChoiceEmojis
    })
    slack.onMessage(gameStarter)

    const turnHandler = buildTurnHandler({
      gameName,
      dataName,
      checkWinner,
      textFromBoard,
      turnChoiceEmojis,
      makeMove
    })
    slack.onReaction(turnHandler)
  }
}
