const fetch = require("node-fetch")
const fs = require("fs")
const Zip = require("zip-stream");
const child_process = require("child_process");

async function zip(api, {id, output}) {
  const resp = await fetch(`${api.host}/api/website/${id}/zip`, {
    headers: {"Authorization": "Bearer " + api.token},
  })
  if(resp.status != 200) throw resp.statusText

  const stream = output == "-" || !output ? process.stdout : fs.createWriteStream(output)
  resp.body.pipe(stream)
}

async function unzip(api, {filename, website, owner}) {
  const resp = await fetch(`${api.host}/api/website/unzip?empty=1&${
    website ? "&website=" + website : ""
  }${
    owner ? "&owner=" + owner : ""
  }`, {
    method: "POST",
    headers: {"Authorization": "Bearer " + api.token, "Content-Type": "application/zip"},
    body: filename == "-" || !filename ? process.stdin : fs.createReadStream(filename)
  })
  if(resp.status != 200) {
    if(resp.headers.get('content-type') == "application/json") throw (await resp.json()).error
    throw await resp.text()
  }
  return await resp.json()
}

async function downloadUrl(url) {
  // console.error(">>", url)
  const resp = await fetch(url)
  if(resp.status != 200) {
    if(resp.headers.get('content-type') == "application/json") throw (await resp.json()).error
    throw await resp.text()
  }
  return resp
}

async function downloadWebsite(api, {id, zipoutput}) {
  const {data: [site]} = await api.website.list({query: {select: "render", filter: {_id: id}}})
  if(!site) throw `website "${id}" is not found`
  if(!site.render) throw `website "${id}" has no render`
  const path = `site/${site.render}`
  const downloadHash = {}
  const {data: bucketName} = await api.api.BUCKET_NAME()
  const {data: bucketUrl} = await api.api.BUCKET_URL()

  const storeRegexp = /(?:https?:)?\/\/([^/"')]+)\.storage\.googleapis\.com\/([^,\s"')?]+)(?:\?[^,\s"')]*)?/g

  const {data: files} = await api.siterender.getFiles({id: site.render})
  // console.log(files)
  let zip
  if(zipoutput) {
    zip = new Zip(); // {zlib: {level: 1}}
    zip.on("error", function(error) {
      // console.error({error, stack: error.stack}, `Archiving ${id} error: ${error}`);
      zip.destroy()
      zip = null
      throw error;
    });
    zip.pipe(zipoutput == "-" || !zipoutput ? process.stdout : fs.createWriteStream(zipoutput))
  }

  async function saveFile(zip, name, body) {
    if(zip) {
      await new Promise((resolve, reject) => zip.entry(
        body, {name}, (err, res) => err ? reject(err) : resolve(res))
      )
    } else {
      const dirs = name.split("/").slice(0, -1);
      dirs.length && child_process.execSync(`mkdir -p ${dirs.join("/")}`)
      typeof body == "string" ? fs.writeFileSync(name, body) : body.pipe(fs.createWriteStream(name))
    }
  }

  async function download(key, fileurl, contentType) {
    let body = await downloadUrl(fileurl)
    downloadHash[key] = "done"
    if(["text/html", "text/css", "application/javascript"].includes(contentType)) {
      body = await body.text()
      // key.endsWith("block.js") && console.log("!!!!!!!11block.js")
      body = body
        .replace(/\\u002f/gi, "/")
        .replace(storeRegexp, (str, bucket, name) => {
          key.endsWith("block.js") && console.log(bucket, name)
          if(bucket == bucketName && name.indexOf(path + "/") == 0) name = name.replace(path + "/", "");
          downloadHash[id + "/"+name] = downloadHash[id + "/"+name] || {
            url: `http://${bucket}.storage.googleapis.com/${name}`,
            contentType: (name.endsWith(".html") || name.endsWith(".js") || name.endsWith(".css")) && "text/css"
          }
          // console.error(key, `${open}${relationPath}/${name}${close}`, downloadHash[id + "/"+name])
          return "/" + name
        })
      "text/html" == contentType && (
        body = body.replace(/<\/body>/,
          `<script>window.urlByRef = function(ref) {return ref && "/" + ("" + ref).split("/").slice(2).join("/")}</script>
</body>`))

      await saveFile(zip, key, body)
    } else {
      await saveFile(zip, key, body.body)
    }
  }

  for(let {name, contentType} of files) {
    if(zip === null) return;
    const fileurl = `http:${bucketUrl}/site/${site.render}/${name}`;
    [contentType] = contentType.split(";")
    contentType == "text/html" && !name.endsWith(".html") && (name  += ".html")
    await download(id + "/" + name, fileurl, contentType.split(";")[0])
  }

  let found = true
  while(found) {
    found = false
    for(const name in downloadHash) {
      const {url} = downloadHash[name]
      if(zip !== null && url) {
        downloadHash[name] = "done"
        found = true
        await download(name, url, downloadHash[name].contentType).catch(error => console.error(error))
      }
    }
  }

  zip && await zip.finish();
}

module.exports = {zip, unzip, downloadWebsite}
