#!/usr/bin/env node
import { spawn } from "node:child_process";
import { fileURLToPath } from "node:url";

const args = parseArgs(process.argv.slice(2));

if (args.help || args.h) {
  printHelp();
  process.exit(0);
}

const generateScript = fileURLToPath(new URL("./orderly-generate-category-pages.mjs", import.meta.url));
const hydrateScript = fileURLToPath(new URL("./orderly-hydrate-static-pages.mjs", import.meta.url));
const generatorArgs = passthroughGeneratorArgs(process.argv.slice(2));
const hydrateArgs = passthroughHydrateArgs(process.argv.slice(2));

if (args["dry-run"]) {
  await runNode([generateScript, ...generatorArgs]);
  process.exit(0);
}

await runNode([generateScript, ...generatorArgs]);
try {
  await runCommand(args["build-command"] ?? "npm run build", {
    VITE_ORDERLY_CATEGORY_URL_MODE: "path",
    ORDERLY_CATEGORY_URL_MODE: "path"
  });
  if (!args["no-hydrate"]) {
    await runNode([hydrateScript, ...hydrateArgs]);
  }
} finally {
  await runNode([generateScript, "--clean", ...cleanGeneratorArgs(generatorArgs)]);
}

function runNode(args) {
  return run(process.execPath, args);
}

function runCommand(command, env = {}) {
  return run(command, [], { shell: true, env });
}

function run(command, args, options = {}) {
  return new Promise((resolve, reject) => {
    const child = spawn(command, args, {
      env: { ...process.env, ...options.env },
      shell: options.shell ?? false,
      stdio: "inherit"
    });
    child.on("error", reject);
    child.on("exit", (code) => {
      if (code === 0) {
        resolve();
      } else {
        reject(new Error(`${command} ${args.join(" ")} exited with code ${code}.`));
      }
    });
  });
}

function passthroughGeneratorArgs(values) {
  const result = [];
  for (let index = 0; index < values.length; index += 1) {
    const value = values[index];
    const name = value.startsWith("--") ? value.slice(2).split("=", 1)[0] : "";
    if (buildOnlyArgs().has(name) || hydrateOnlyArgs().has(name)) {
      if (!value.includes("=") && values[index + 1] && !values[index + 1].startsWith("--")) {
        index += 1;
      }
      continue;
    }
    result.push(value);
  }
  return result;
}

function passthroughHydrateArgs(values) {
  const result = [];
  for (let index = 0; index < values.length; index += 1) {
    const value = values[index];
    const name = value.startsWith("--") ? value.slice(2).split("=", 1)[0] : "";
    if (buildOnlyArgs().has(name) || generatorOnlyArgs().has(name) || name === "dry-run") {
      if (!value.includes("=") && values[index + 1] && !values[index + 1].startsWith("--")) {
        index += 1;
      }
      continue;
    }
    if (name === "hydrate") {
      continue;
    }
    result.push(value);
  }
  return result;
}

function cleanGeneratorArgs(values) {
  const blocked = new Set(["--dry-run", "--clean"]);
  return values.filter((value) => !blocked.has(value));
}

function buildOnlyArgs() {
  return new Set(["build-command", "hydrate", "no-hydrate"]);
}

function generatorOnlyArgs() {
  return new Set(["template", "categories-dir", "component-tag", "clean"]);
}

function hydrateOnlyArgs() {
  return new Set([
    "dist",
    "dist-categories-dir",
    "shop-query",
    "shop-query-export",
    "base-url",
    "protocol",
    "products-per-category",
    "home-products-per-category",
    "product-page",
    "product-root",
    "image-base-url",
    "skip-products",
    "strict-products",
    "strict-empty-categories",
    "fail-empty-categories"
  ]);
}

function parseArgs(values) {
  const parsed = {};
  for (let index = 0; index < values.length; index += 1) {
    const value = values[index];
    if (!value.startsWith("--")) {
      continue;
    }
    const [name, inlineValue] = value.slice(2).split("=", 2);
    if (inlineValue !== undefined) {
      parsed[name] = inlineValue;
    } else if (values[index + 1] && !values[index + 1].startsWith("--")) {
      parsed[name] = values[index + 1];
      index += 1;
    } else {
      parsed[name] = true;
    }
  }
  return parsed;
}

function printHelp() {
  console.log(`Usage: orderly-build-category-pages [options]

Generate real URL category pages, run the current project's build, then remove generated source pages.

Options:
  --build-command <cmd>    Build command. Defaults to "npm run build".
  --no-hydrate             Skip static homepage/category fallback hydration after build.
  --dry-run                Forward to orderly-generate-category-pages without building.

Category options are forwarded to orderly-generate-category-pages. Hydration options are forwarded
to orderly-hydrate-static-pages.
`);
}
