import fetch from 'node-fetch'
import { config } from './config.js'
import { getRedisKey, hash } from './util.js'
import { logger } from './logger.js'
import * as redis from './redis.js'

let latestVersion

export const fetchLatestVersion = async () => {
  const infos = await Promise.all(config.urls.map(async baseUrl => {
    try {
      const response = await fetch(new URL('meta.json', baseUrl))
      if (!response.ok) throw new Error()
      const meta = await response.json()
      const version = meta.commitSha || meta.buildDate || meta.version
      if (!version) throw new Error()
      return version
    } catch (err) {
      logger.debug(`[Version] UI container at ${baseUrl} does not have meta.json. Fall back to version hash based on manifest.`)
    }
    try {
      const response = await fetch(new URL('manifest.json', baseUrl))
      if (!response.ok) throw new Error()
      const manifest = await response.json()
      return hash(manifest)
    } catch (err) {
      logger.error(`[Version] Cannot fetch manifest from ${baseUrl}. Version info will not be correct.`)
    }
  }))
  return hash(infos)
}

export async function getLatestVersion () {
  if (latestVersion) return latestVersion

  if (redis.isEnabled()) {
    const version = await redis.client.get(getRedisKey({ name: 'latestVersion' }))
    if (version) return (latestVersion = version)
  }

  const newVersion = await fetchLatestVersion()

  if (redis.isEnabled()) {
    redis.pubClient.publish(getRedisKey({ name: 'updateLatestVersion' }), newVersion)
    await redis.client.set(getRedisKey({ name: 'latestVersion' }), newVersion)
  }

  return (latestVersion = newVersion)
}

export function registerLatestVersionListener (client) {
  if (redis.isEnabled()) {
    const key = getRedisKey({ name: 'updateLatestVersion' })
    client.subscribe(key, (errs, count) => logger.info(`[Redis] Subscribed to ${key}.`))
    client.on('message', async (channel, message) => {
      if (channel !== key) return
      await config.load()
      latestVersion = message
    })
  }
}

export async function updateVersionProcessor () {
  await config.load()
  const [storedVersion, fetchedVersion] = await Promise.all([
    getLatestVersion(),
    fetchLatestVersion()
  ])
  if (storedVersion === fetchedVersion) return fetchedVersion
  if (redis.isEnabled()) {
    redis.pubClient.publish(getRedisKey({ name: 'updateLatestVersion' }), fetchedVersion)
    await redis.client.set(getRedisKey({ name: 'latestVersion' }), fetchedVersion)
  }
  return (latestVersion = fetchedVersion)
}