const _ = require('lodash/fp')
const crypto = require('crypto')
const { utils: coinUtils } = require('@lamassu/coins')
const prettyMs = require('pretty-ms')

const {
  CODES_DISPLAY,
  NETWORK_DOWN_TIME,
  PING,
  ALERT_SEND_INTERVAL,
} = require('./codes')

const DETAIL_TEMPLATE = {
  deviceId: '',
  cryptoCode: '',
  code: '',
  cassette: '',
  age: '',
  customerId: '',
  cryptoAddress: '',
  direction: '',
  fiat: '',
  fiatCode: '',
}

function parseEventNote(event) {
  return _.set('note', JSON.parse(event.note), event)
}

function checkPing(device) {
  const age = Date.now() - new Date(device.lastPing).getTime()
  if (age > NETWORK_DOWN_TIME)
    return [{ code: PING, age, machineName: device.name }]
  return []
}

const getDeviceTime = _.flow(_.get('device_time'), Date.parse)

const isActive = it => it.active && (it.balance || it.errors)

const codeDisplay = code => CODES_DISPLAY[code]

const alertFingerprint = {
  fingerprint: null,
  lastAlertTime: null,
}

const getAlertFingerprint = () => alertFingerprint.fingerprint

const getLastAlertTime = () => alertFingerprint.lastAlertTime

const setAlertFingerprint = (fp, time = Date.now()) => {
  alertFingerprint.fingerprint = fp
  alertFingerprint.lastAlertTime = time
}

const shouldNotAlert = currentAlertFingerprint => {
  return (
    currentAlertFingerprint === getAlertFingerprint() &&
    getLastAlertTime() - Date.now() < ALERT_SEND_INTERVAL
  )
}

function buildAlertFingerprint(alertRec, notifications) {
  const sms = getAlertTypes(alertRec, notifications.sms)
  const email = getAlertTypes(alertRec, notifications.email)
  if (sms.length === 0 && email.length === 0) return null

  const smsTypes = _.map(codeDisplay, _.uniq(_.map('code', sms))).sort()
  const emailTypes = _.map(codeDisplay, _.uniq(_.map('code', email))).sort()

  const subject = _.concat(smsTypes, emailTypes).join(', ')
  return crypto.createHash('sha256').update(subject).digest('hex')
}

function sendNoAlerts(plugins, smsEnabled, emailEnabled) {
  const subject = '[Lamassu] All clear'

  let rec = {}
  if (smsEnabled) {
    rec = _.set(['sms', 'body'])(subject)(rec)
  }

  if (emailEnabled) {
    rec = _.set(['email', 'subject'])(subject)(rec)
    rec = _.set(['email', 'body'])('No errors are reported for your machines.')(
      rec,
    )
  }

  return plugins.sendMessage(rec)
}

const buildTransactionMessage = (
  tx,
  rec,
  highValueTx,
  machineName,
  customer,
) => {
  const isCashOut = tx.direction === 'cashOut'
  const direction = isCashOut ? 'Cash Out' : 'Cash In'
  const crypto = `${coinUtils.toUnit(tx.cryptoAtoms, tx.cryptoCode)} ${
    tx.cryptoCode
  }`
  const fiat = `${tx.fiat} ${tx.fiatCode}`
  const customerName = customer.name || customer.id
  const phone = customer.phone ? `- Phone: ${customer.phone}` : ''

  let status = null
  if (rec.error) {
    status = `Error - ${rec.error}`
  } else {
    status = !isCashOut
      ? 'Successful'
      : !rec.isRedemption
        ? 'Successful & awaiting redemption'
        : 'Successful & dispensed'
  }

  const body = `
  - Transaction ID: ${tx.id}
  - Status: ${status}
  - Machine name: ${machineName} 
  - ${direction}
  - ${fiat}
  - ${crypto}
  - Customer: ${customerName}
  ${phone}
`
  const smsSubject = `A ${highValueTx ? 'high value ' : ''}${direction.toLowerCase()} transaction just happened at ${machineName} for ${fiat}`
  const emailSubject = `A ${highValueTx ? 'high value ' : ''}transaction just happened`

  return [
    {
      sms: {
        body: `${smsSubject} – ${status}`,
      },
      email: {
        emailSubject,
        body,
      },
      webhook: {
        topic: `New transaction`,
        content: body,
      },
    },
    highValueTx,
  ]
}

function formatCurrency(num = 0, code) {
  const formattedNumber = Number(num).toLocaleString(undefined, {
    maximumFractionDigits: 2,
    minimumFractionDigits: 2,
  })
  return `${formattedNumber} ${code}`
}

function formatAge(age, settings) {
  return prettyMs(age, settings)
}

function buildDetail(obj) {
  // obj validation
  const objKeys = _.keys(obj)
  const detailKeys = _.keys(DETAIL_TEMPLATE)
  if (_.difference(objKeys, detailKeys).length > 0) {
    return Promise.reject(
      new Error('Error when building detail object: invalid properties'),
    )
  }
  return { ...DETAIL_TEMPLATE, ...obj }
}

function deviceAlerts(config, alertRec, device) {
  let alerts = []
  if (config.balance) {
    alerts = _.concat(alerts, alertRec.devices[device].balanceAlerts)
  }

  if (config.errors) {
    alerts = _.concat(alerts, alertRec.devices[device].deviceAlerts)
  }
  return alerts
}

function getAlertTypes(alertRec, config) {
  let alerts = []
  if (!isActive(config)) return alerts

  if (config.balance) {
    alerts = _.concat(alerts, alertRec.general)
  }

  _.forEach(device => {
    alerts = _.concat(alerts, deviceAlerts(config, alertRec, device))
  }, _.keys(alertRec.devices))
  return alerts
}

module.exports = {
  codeDisplay,
  parseEventNote,
  getDeviceTime,
  checkPing,
  isActive,
  getAlertFingerprint,
  getLastAlertTime,
  setAlertFingerprint,
  shouldNotAlert,
  buildAlertFingerprint,
  sendNoAlerts,
  buildTransactionMessage,
  formatCurrency,
  formatAge,
  buildDetail,
  deviceAlerts,
}
