import { existsSync, readFileSync } from "node:fs";
import { resolve } from "node:path";
import { spawnSync } from "node:child_process";

export const readCheckCatalog = catalogPath => JSON.parse(readFileSync(catalogPath, "utf8"));

export const deletedEntrypointToken = value =>
  value.includes("dev-check.sh") ||
  value.includes("dev_check.ps1") ||
  value.includes("conformance-check.sh");

export const findDeletedEntrypointReferences = catalog => {
  const failures = [];
  for (const [profileName, profile] of Object.entries(catalog.profiles ?? {})) {
    for (const [stepIndex, step] of (profile.steps ?? []).entries()) {
      for (const arg of step.argv ?? []) {
        if (typeof arg === "string" && deletedEntrypointToken(arg)) {
          failures.push(`${profileName}.steps[${stepIndex}].argv references deleted entrypoint: ${arg}`);
        }
      }
    }
  }
  return failures;
};

export const assertNoDeletedEntrypointReferences = catalog => {
  const failures = findDeletedEntrypointReferences(catalog);
  if (failures.length > 0) {
    throw new Error(failures.join("\n"));
  }
};

export const hostName = (platform = process.platform) => {
  if (platform === "darwin") return "darwin";
  if (platform === "linux") return "linux";
  if (platform === "win32") return "windows";
  return platform;
};

export const hostMatches = (host, platform = process.platform) => {
  if (!host || host === "any") return true;
  if (host === "non-windows") return hostName(platform) !== "windows";
  return host === hostName(platform);
};

export const expandProfile = (profiles, name, seen = []) => {
  const selected = profiles[name];
  if (!selected) {
    throw new Error(`Unknown profile: ${name}`);
  }
  if (seen.includes(name)) {
    throw new Error(`Profile include cycle: ${[...seen, name].join(" -> ")}`);
  }
  const steps = [];
  for (const included of selected.includes ?? []) {
    steps.push(...expandProfile(profiles, included, [...seen, name]));
  }
  for (const step of selected.steps ?? []) {
    steps.push(step);
  }
  return steps;
};

export const planProfile = ({ catalog, profile, platform = process.platform }) => {
  assertNoDeletedEntrypointReferences(catalog);
  const profiles = catalog.profiles ?? {};
  const selectedSteps = expandProfile(profiles, profile);
  const plannedSteps = selectedSteps.map(step => ({
    name: step.name,
    host: step.host ?? "any",
    argv: step.argv,
    skipped: !hostMatches(step.host, platform),
  }));
  return {
    profile,
    host: hostName(platform),
    stepCount: plannedSteps.length,
    steps: plannedSteps,
  };
};

export const formatPlanList = plan => {
  const lines = [`${plan.profile} (${plan.host})`];
  for (const step of plan.steps) {
    const marker = step.skipped ? "skip" : "run";
    lines.push(`${marker}: ${step.name}: ${step.argv.join(" ")}`);
  }
  return lines;
};

export const ensureWindowSubmodule = repoRoot => {
  const windowMod = resolve(repoRoot, "window/modules/window/moon.mod");
  const windowingMod = resolve(repoRoot, "window/modules/windowing/moon.mod");
  if (existsSync(windowMod) && existsSync(windowingMod)) return;
  console.log("\n==> Initializing window submodule...");
  const result = spawnSync("git", ["submodule", "update", "--init", "window"], {
    cwd: repoRoot,
    stdio: "inherit",
  });
  if (result.status !== 0 || result.error) {
    if (result.error) console.error(result.error.message);
    process.exit(result.status ?? 1);
  }
};

export const defaultCheckEnv = (env = process.env) => ({
  ...env,
  MOUI_SKIA_DISABLE_PREBUILD_SKIA: env.MOUI_SKIA_DISABLE_PREBUILD_SKIA ?? "1",
});