const { exec } = require('child_process')
const { promisify } = require('util')
const path = require('path')
const execp = promisify(exec)
const assert = require('node:assert').strict

test()
  .catch(e => console.error(e))

async function test () {
  await testProjects()
  await testInstances()
}

async function testInstances () {
  const projectId = (await ej('project create --cores 20 --instances 10 --ram 5000 CoolNewProject'))[0].id

  const { state, id: instanceId } = (await ej(`instance create ranchu 12.0.0 ${projectId}`))[0]
  assert.equal(state, 'creating')

  const instances = await ej('instance list')
  assert.ok(instances.map(p => p.id).includes(instanceId))
  // TODO: instance stop
  // TODO: instance start
  await e(`instance delete ${instanceId}`)

  await pollUntilNoThrow(instanceId)

  // Cleanup
  await e(`project delete ${projectId}`)
}

async function testProjects () {
  const { id: projectId } = (await ej('project create --cores 10 --instances 2 --ram 2000 CoolNewProject'))[0]

  // The list has the newly created project in it
  let projects = await ej('project list')
  assert.ok(projects.map(p => p.id).includes(projectId))

  await e(`project delete ${projectId}`)

  // The list has no longer has the project
  projects = await ej('project list')
  assert.equal(-1, projects.map(p => p.id).indexOf(projectId))
}

async function sleep (ms) {
  return new Promise((resolve) => setTimeout(resolve, ms))
}

async function pollUntilNoThrow (instanceId, tries = 1) {
  if (tries > 10) {
    return
  }
  console.log(`try ${tries} looking for ${instanceId}`)

  try {
    const instances = await ej('instance list')
    assert.equal(-1, instances.map(i => i.id).indexOf(instanceId))
  } catch (err) {
    if (err.code === 'ERR_ASSERTION') {
      const ms = 1 + Math.random() * (2 ** tries * 10)
      await sleep(ms)
      await pollUntilNoThrow(instanceId, tries + 1)
    } else {
      throw err
    }
  }
}

// short for "execJSON"
// Used to exec things we want the return value of
async function ej (s) {
  const { stdout, stderr } = await execp(`node index.js ${s} --format json`, { cwd: path.join(__dirname, '../') })
  if (stderr) {
    console.log(stderr)
  }
  return JSON.parse(stdout)
}

// short for "exec"
// Used mainly for deletes, which have no --format flag and return no parseable data (204, generally)
async function e (s) {
  const { stdout, stderr } = await execp(`node index.js ${s}`, { cwd: path.join(__dirname, '../') })
  if (stderr) {
    console.log(stderr)
  }
  return stdout
}
