import fetch from 'node-fetch'

import { config, logger } from '@things-factory/env'

import { initializeApp, cert } from 'firebase-admin/app'
import { getMessaging } from 'firebase-admin/messaging'

const notificationConfig = config.get('notification') || {}
const { fcm, vapidKey } = notificationConfig
const { serviceAccount, appConfig, serverKey } = fcm || {}
const { publicKey } = vapidKey || {}

var messaging

if (serviceAccount) {
  try {
    const app = initializeApp({
      credential: cert(serviceAccount)
    })

    messaging = getMessaging(app)
  } catch (err) {
    logger.error('incorrect notification configuration')
    logger.error(err)
  }
}

export function getConfig() {
  return messaging
    ? {
        appConfig,
        vapidPublicKey: publicKey
      }
    : {}
}

/**
 * sendNotification
 *
 * @param receiver user.id who will receive notification
 * @param message message object to be sent
 */
export async function sendNotification({ receivers, message }) {
  if (!messaging) {
    return
  }

  notify({
    receivers,
    ...message
  })
}

export async function register(user, { subscription }) {
  if (!messaging || !subscription || !user) {
    return false
  }

  try {
    var notification_key = await getDeviceGroup(user.id)

    if (!notification_key) {
      await postDeviceGroup({
        operation: 'create',
        notification_key_name: user.id,
        registration_ids: [subscription]
      })
    } else {
      await postDeviceGroup({
        operation: 'add',
        notification_key_name: user.id,
        notification_key,
        registration_ids: [subscription]
      })
    }

    return true
  } catch (err) {
    return
  }
}

export async function unregister(user, { subscription }) {
  if (!messaging || !subscription || !user) {
    return false
  }

  try {
    var notification_key = await getDeviceGroup(user.id)

    if (notification_key) {
      await postDeviceGroup({
        operation: 'remove',
        notification_key_name: user.id,
        notification_key,
        registration_ids: [subscription]
      })
    }

    return true
  } catch (err) {
    return
  }
}

export async function notify({ receivers, privileges, tokens, topic, title, body, data, image, actions }) {
  if (!messaging) {
    return
  }

  // Caution: non-string attributes are not allowed in FCM payload validation
  var notification = {
    title: title || '',
    body: body || '',
    image: image || ''
  }

  if (tokens && tokens instanceof Array && tokens.length > 0) {
    if (tokens.length > 1) {
      const response = await messaging.sendMulticast({
        notification,
        data,
        tokens
      })

      if (response.failureCount > 0) {
        const failedTokens = []
        response.responses.forEach((resp, idx) => {
          if (!resp.success) {
            failedTokens.push(tokens[idx])
          }
        })
        logger.error('List of tokens that caused failures: ' + failedTokens)
      }
    } else {
      const token = tokens[0]

      try {
        const response = await messaging.send({
          notification,
          data,
          token
        })
        logger.log('Successfully sent message:', response)
      } catch (err) {
        logger.error('Error sending message:', err)
      }
    }
  }

  if (topic) {
    try {
      const response = await messaging.send({
        notification,
        data,
        topic
      })
      logger.log('Successfully sent message:', response)
    } catch (err) {
      logger.error('Error sending message:', err)
    }
  }

  if (receivers && receivers instanceof Array) {
    for (let receiver of receivers) {
      const notification_key = await getDeviceGroup(receiver)

      if (notification_key) {
        await messaging.sendToDeviceGroup(notification_key, {
          notification,
          data
        })
      }
    }
  }
}

async function postDeviceGroup(body) {
  var response = await fetch('https://fcm.googleapis.com/fcm/notification', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `key=${serverKey}`,
      project_id: appConfig.messagingSenderId
    },
    body: JSON.stringify(body)
  })

  if (response.ok) {
    const { notification_key } = await response.json()
    return notification_key
  } else {
    console.error('postDeviceGroup-notok', response.status, await response.text())
  }
}

async function getDeviceGroup(notificationKeyName) {
  const response = await fetch(
    `https://fcm.googleapis.com/fcm/notification?notification_key_name=${notificationKeyName}`,
    {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `key=${serverKey}`,
        project_id: appConfig.messagingSenderId
      }
    }
  )

  if (response.ok) {
    const { notification_key } = await response.json()
    return notification_key
  }
}
