const buyableItems = require('./buyableItems')
const { commas, setHighestCoins, addAchievement, getUser, singleItemCps, chaosFilter, fuzzyMatcher, calculateCost, saveUser } = require('./utils')
const slack = require('../../slack')

const leaderboardUpdater = {}

const getItemHeader = user => ([itemName, { baseCost, description, emoji }]) => {
  const itemCost = commas(user ? calculateCost({ itemName, user }) : baseCost)
  const itemCps = Math.round(singleItemCps(user, itemName))
  return `*${itemName}* :${emoji}: - ${itemCost} HVAC Coins - ${commas(itemCps)} CPS\n_${description}_`
}
const canView = (item, highestCoins) => item.baseCost < (highestCoins || 1) * 101
const buyableText = (highestCoins, user) => Object.entries(buyableItems)
  .filter(([, item]) => canView(item, highestCoins))
  .map(getItemHeader(user))
  .join('\n\n') +
  '\n\n:grey_question::grey_question::grey_question:' +
  '\n\nJust type \'!buy item_name\' to purchase'

const buildBlock = ({ user, itemName, cost, cps }) => ({
  type: 'section',
  text: {
    type: 'mrkdwn',
    text: `${itemName} :${buyableItems[itemName].emoji}:x${user.items[itemName] || 0} - H${commas(cost)} - ${commas(cps)} CPS\n_${buyableItems[itemName].description}_`
  },
  accessory: {
    type: 'button',
    text: {
      type: 'plain_text',
      text: '1',
      emoji: true
    },
    value: 'buy_' + itemName,
    action_id: 'buy_' + itemName
  }
})

const buildBlock2 = ({ user, itemName, cost, cps }) => ({
  type: 'actions',
  elements: [
    {
      type: 'button',
      text: {
        type: 'plain_text',
        text: 'Buy 1',
        emoji: true
      },
      value: 'buy_' + itemName,
      action_id: 'buy_' + itemName
    },
    {
      type: 'button',
      text: {
        type: 'plain_text',
        text: 'Buy 1',
        emoji: true
      },
      value: 'buy_' + itemName,
      action_id: 'buy_' + itemName
    }
  ]
})

const buyText2 = (highestCoins, user, extraMessage = '') => {
  return ({
    text: (extraMessage && extraMessage + '\n')
      + `You have ${commas(user.coins)} HVAC to spend.\n`
      + buyableText(highestCoins, user),
    blocks: [
      {
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: (extraMessage && extraMessage + '\n')
            + `You have ${commas(user.coins)} HVAC to spend.\n`
          },
      },
      ...Object.entries(buyableItems)
      .filter(([, item]) => canView(item, highestCoins))
      .map(([itemName]) => {
        const cost = calculateCost({ itemName, user, quantity: 1 })
        const cps = Math.round(singleItemCps(user, itemName))
        return ({ user, itemName, cost, cps })
      }).map(buildBlock)
    ]
  })
}

const maxQuantity = ({ itemName, user, currentCoins }) => {
  let quantity = 1
  while (calculateCost({ itemName, user, quantity: quantity + 1 }) <= currentCoins) {
    quantity++
  }
  return quantity
}

const buyRoute = async ({ event, say, args, user }) => {
  const buying = args[0]
  setHighestCoins(event.user)
  const query = event?.text?.startsWith('?b ') || event?.text?.startsWith('?buy ')

  if (!buying) {
    const highestCoins = user.highestEver || user.coins || 1
    if (canView(buyableItems.quade, highestCoins)) {
      addAchievement(user, 'seeTheQuade', say)
    }
    await say(buyText2(highestCoins, user))
    return
  }

  const matcher = fuzzyMatcher(buying)
  const buyable = Object.entries(buyableItems).find(([name]) => matcher.test(name))
  if (!buyable) {
    await say('That item does not exist!')
    return
  }

  const [buyableName, buyableItem] = buyable
  let quantity
  const currentCoins = user.coins
  const max = maxQuantity({ itemName: buyableName, user, currentCoins })
  if (!args[1]) {
    quantity = 1
  } else if (args[1] === 'max') {
    quantity = max
  } else {
    if (query) {
      quantity = parseInt(args[1])
    } else {
      quantity = Math.round(chaosFilter(parseInt(args[1]), 0.2, user, max) || 1)
    }
  }
  if (!quantity || quantity < 1) {
    await say('Quantity must be a positive integer')
    return
  }

  const realCost = calculateCost({ itemName: buyableName, user, quantity })
  if (query) {
    return say(`Buying ${quantity} ${buyableName} would cost you ${commas(realCost)} HVAC`)
  }
  if (currentCoins < realCost) {
    await say(`You don't have enough coins! You need ${commas(realCost)}`)
    return
  }
  user.coins -= realCost
  user.items[buyableName] = user.items[buyableName] || 0
  user.items[buyableName] += quantity

  if (user.items[buyableName] >= 100) {
    addAchievement(user, buyableItems[buyableName].own100Achievement, say)
  }

  const countString = quantity === 1 ? 'one' : quantity
  await say(`You bought ${countString} :${buyableItem.emoji}:`)
  await saveUser(event.user, user, `Buying ${countString} :${buyableItem.emoji}:`)
}

const buyButton = async ({ body, ack, say, payload }) => {
  await ack()
  const buying = payload.action_id.substring(4)
  console.log(`buyButton ${buying} clicked`)
  const event = {
    user: body.user.id
  }
  const user = await getUser(event.user)
  const words = ['', buying, body.actions[0].text]
  const [commandName, ...args] = words

  let extraMessage = ''
  say = async text => extraMessage = text
  await buyRoute({ event, say, words, args, commandName, user })

  const highestCoins = user.highestEver || user.coins || 1
  await slack.app.client.chat.update({
    channel: body.channel.id,
    ts: body.message.ts,
    ...buyText2(highestCoins, user, extraMessage)
  })
  await leaderboardUpdater.updateAllLeaderboards()
  await saveUser(event.user, user, `buyButton ${buying} clicked`)
}

Object.keys(buyableItems).forEach(itemName => slack.app.action('buy_' + itemName, buyButton))

module.exports = { buyRoute, leaderboardUpdater }
