import path from 'path'
import zlib from 'zlib'
import fse from 'fs-extra'
import tar from 'tar-fs'
import lazyRequire from '@sanity/util/lib/lazyRequire'

export default async (args, context) => {
  const {apiClient, workDir, chalk, output, prompt} = context
  const flags = Object.assign({build: true}, args.extOptions)
  const sourceDir = path.resolve(
    process.cwd(),
    args.argsWithoutOptions[0] || path.join(workDir, 'dist')
  )

  const client = apiClient({
    requireUser: true,
    requireProject: true
  })

  // Check that the project has a studio hostname
  let spinner = output.spinner('Checking project info').start()
  const project = await client.projects.getById(client.config().projectId)
  let studioHostname = project && (project.studioHostname || project.studioHost)
  spinner.succeed()

  if (!studioHostname) {
    output.print('Your project has not been assigned a studio hostname.')
    output.print('To deploy your Sanity Studio to our hosted Sanity.Studio service,')
    output.print('you will need one. Please enter the part you want to use.')


    studioHostname = await prompt.single({
      type: 'input',
      filter: inp => inp.replace(/\.sanity\.studio$/i, ''),
      message: 'Studio hostname (<value>.sanity.studio):',
      validate: name => validateHostname(name, client)
    })
  }

  // Always build the project, unless --no-build is passed
  const shouldBuild = flags.build
  if (shouldBuild) {
    const buildStaticAssets = lazyRequire(require.resolve('../build/buildStaticAssets'))
    await buildStaticAssets({extOptions: {}, argsWithoutOptions: []}, context)
  }

  // Ensure that the directory exists, is a directory and seems to have valid content
  spinner = output.spinner('Verifying local content').start()
  try {
    await checkDir(sourceDir)
    spinner.succeed()
  } catch (err) {
    spinner.fail()
    throw err
  }

  // Now create a tarball of the given directory
  const parentDir = path.dirname(sourceDir)
  const base = path.basename(sourceDir)
  const tarball = tar.pack(parentDir, {entries: [base]}).pipe(zlib.createGzip())

  spinner = output.spinner('Deploying to Sanity.Studio').start()
  try {
    const response = await client.request({
      method: 'POST',
      url: '/deploy',
      body: tarball,
      maxRedirects: 0
    })

    spinner.succeed()

    // And let the user know we're done
    output.print(`\nSuccess! Studio deployed to ${chalk.cyan(response.location)}`)
  } catch (err) {
    spinner.fail()
    throw err
  }
}

async function checkDir(sourceDir) {
  try {
    const stats = await fse.stat(sourceDir)
    if (!stats.isDirectory()) {
      throw new Error(`Directory ${sourceDir} is not a directory`)
    }
  } catch (err) {
    const error = err.code === 'ENOENT'
      ? new Error(`Directory "${sourceDir}" does not exist`)
      : err

    throw error
  }

  try {
    await fse.stat(path.join(sourceDir, 'index.html'))
  } catch (err) {
    const error = err.code === 'ENOENT' ? new Error([
      `"${sourceDir}/index.html" does not exist -`,
      '[SOURCE_DIR] must be a directory containing',
      'a Sanity studio built using "sanity build"'
    ].join(' ')) : err

    throw error
  }
}

function validateHostname(value, client) {
  const projectId = client.config().projectId
  const uri = `/projects/${projectId}/host`
  const host = value || ''

  // Check that it matches allowed character range
  if (!/^[a-z0-9_-]+$/i.test(host)) {
    return 'Hostname can contain only A-Z, 0-9, _ and -'
  }

  // Check that the hostname is not already taken
  return client.request({uri, method: 'PUT', body: {studio: host}})
    .then(() => true)
    .catch(() => 'Hostname already taken')
}

@@ -3320,13 +3320,8 @@
 tId%7D
-/host
 %60%0A  
@@ -3326,17 +3326,23 @@
   const 
-h
+studioH
 ost = va
@@ -3432,17 +3432,23 @@
 /i.test(
-h
+studioH
 ost)) %7B%0A
@@ -3603,10 +3603,12 @@
 : 'P
-UT
+ATCH
 ', b
@@ -3623,11 +3623,9 @@
 udio
-: h
+H
 ost%7D

Use PATCH-endpoint for setting studio host (#339)