const streamifier = require('streamifier')
const fetch = require('node-fetch')
const ObjectPersistor = require('@overleaf/object-persistor')

const { expect } = require('chai')

module.exports = {
  uploadStringToPersistor,
  getStringFromPersistor,
  expectPersistorToHaveFile,
  expectPersistorToHaveSomeFile,
  expectPersistorNotToHaveFile,
  streamToString,
  getMetric,
}

async function getMetric(filestoreUrl, metric) {
  const res = await fetch(`${filestoreUrl}/metrics`)
  expect(res.status).to.equal(200)
  const metricRegex = new RegExp(`^${metric}{[^}]+} ([0-9]+)$`, 'gm')
  const body = await res.text()
  let v = 0
  // Sum up size="lt-128KiB" and size="gte-128KiB"
  for (const [, found] of body.matchAll(metricRegex)) {
    v += parseInt(found, 10) || 0
  }
  return v
}

function streamToString(stream) {
  const chunks = []
  return new Promise((resolve, reject) => {
    stream.on('data', chunk => chunks.push(chunk))
    stream.on('error', reject)
    stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')))
    stream.resume()
  })
}

async function uploadStringToPersistor(persistor, bucket, key, content) {
  const fileStream = streamifier.createReadStream(content)
  await persistor.sendStream(bucket, key, fileStream)
}

async function getStringFromPersistor(persistor, bucket, key) {
  const stream = await persistor.getObjectStream(bucket, key, {})
  return await streamToString(stream)
}

async function expectPersistorToHaveFile(persistor, bucket, key, content) {
  const foundContent = await getStringFromPersistor(persistor, bucket, key)
  expect(foundContent).to.equal(content)
}

async function expectPersistorToHaveSomeFile(persistor, bucket, keys, content) {
  let foundContent
  for (const key of keys) {
    try {
      foundContent = await getStringFromPersistor(persistor, bucket, key)
      break
    } catch (err) {
      if (err instanceof ObjectPersistor.Errors.NotFoundError) {
        continue
      }
      throw err
    }
  }
  if (foundContent === undefined) {
    expect.fail(`Could not find any of the specified keys: ${keys}`)
  }
  expect(foundContent).to.equal(content)
}

async function expectPersistorNotToHaveFile(persistor, bucket, key) {
  await expect(
    getStringFromPersistor(persistor, bucket, key)
  ).to.eventually.have.been.rejected.with.property('name', 'NotFoundError')
}
