// https://observablehq.com/@keystroke/obs-run
const { watch, ...args } = getArgs();

if (watch) {
  let etag = await getEtag(args);
  let worker = startWorker(args);
  let updated = new Date();
  while (true) {
    await delay(new Date() - updated > watch.recent ? watch.slow : watch.fast);
    const latest = await getEtag(args);
    if (latest && etag !== latest) {
      console.log(`$ SIGTERM -> p:${worker.pid}`);
      worker.kill("SIGTERM");
      worker.close();
      worker = startWorker(args);
      etag = latest;
      updated = new Date();
    }
  }
}

import(`https://obs.run/${notebook}`);

function getArgs() {
  let watch = Deno.args.filter(a => a.startsWith("--watch"))[0]
    || Deno.env.get("WATCH").filter(i => i).map(i => `--watch=${i}`)[0];
  let reload = Deno.args.filter(a => a.startsWith("--reload"))[0]
    || [Deno.env.get("RELOAD")].filter(i => i).map(i => i === "*" ? "--reload" : `--reload=${i}`)[0]
    || "--reload=https://api.observablehq.com";
  let permissions = Deno.args
    .filter(a => a === "-A" || a.startsWith("--"))
    .filter(a => a !== watch && a !== reload)
    .join(" ")
      || Deno.env.get("PERMISSIONS")
      || "";
  const notebook = Deno.args.filter(a => !a.startsWith("-"))[0]
    || Deno.env.get("NOTEBOOK");
  if (watch) {
    let parts = watch.split("=", 2)[1]?.split(';', 3) || [];
    watch = {
      slow: Number(parts[0]) || 31000,
      fast: Number(parts[1]) || 3000,
      recent: Number(parts[2]) || 1000 * 60 * 5,
    };
  }
  if (reload != "--reload" && !reload.includes("https://api.observablehq.com"))
    reload += ",https://api.observablehq.com";
  if (reload != "--reload" && !reload.includes("https://obs.run"))
    reload += ",https://obs.run";
  if (permissions)
    permissions = permissions.split(" ");
  return { watch, reload, permissions, notebook };
}

async function getEtag({ notebook }) {
  const url = `https://api.observablehq.com/${notebook}.js?v=3`;
  try {
    const response = await fetch(url);
    if (!response.ok) console.warn(`${response.status} ${url}`, await response.text());
    return response.headers.get("etag");
  } catch (error) {
    console.warn(url, error);
    return null;
  }
}

function startWorker({ reload, permissions, notebook }) {
  const cmd = [
    "deno",
    "run",
    reload,
    ...permissions,
    `https://obs.run/${notebook}`,
  ];
  console.log(`$ ${cmd.join(" ")}`);
  return Deno.run({ cmd });
}

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}