import { cp, mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
import { dirname, join, relative, resolve } from "node:path";

const root = resolve(new URL("..", import.meta.url).pathname);
const runtimeDir = process.env.FOUNDRY_RUNTIME_DIR ? resolve(root, process.env.FOUNDRY_RUNTIME_DIR) : join(root, "runtime-data");
const seedDir = resolve(process.env.FOUNDRY_RUNTIME_SEED_DIR || join(root, "runtime-seed"));
const manifestPath = join(seedDir, "seed-manifest.json");

function isInside(parent, target) {
  const path = relative(parent, target);
  return path && !path.startsWith("..") && !path.includes("../");
}

async function exists(path) {
  try { await stat(path); return true; } catch (error) { if (error?.code === "ENOENT") return false; throw error; }
}

async function copySeedFile(file) {
  const source = resolve(seedDir, file);
  const target = resolve(runtimeDir, file);
  if (!isInside(seedDir, source) || !isInside(runtimeDir, target)) throw new Error("runtime_seed_path_invalid");
  if (!await exists(source)) throw new Error(`runtime_seed_file_missing:${file}`);
  await mkdir(dirname(target), { recursive: true });
  await cp(source, target, { force: true, preserveTimestamps: true });
}

async function isGeneratedFallback() {
  const profilePath = join(runtimeDir, "design-profiles", "default.json");
  if (!await exists(profilePath)) return true;
  try {
    const profile = JSON.parse(await readFile(profilePath, "utf8"));
    // The server fallback has the same default media paths as the canonical
    // profile, but it has never been persisted as a real Design Profile
    // layout. `savedAt` is written by the baseline/profile save path. A
    // subsequently edited user profile must never be replaced implicitly.
    return profile?.id === "default"
      && profile?.name === "NODE.DC Default"
      && profile?.status === "draft"
      && !String(profile?.layout?.savedAt || "").trim()
      && String(profile?.timestamps?.createdAt || "") === String(profile?.timestamps?.updatedAt || "");
  } catch {
    return false;
  }
}

const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
if (!/^\d+\.\d+\.\d+$/.test(String(manifest.version || ""))) throw new Error("runtime_seed_version_invalid");
if (!Array.isArray(manifest.files) || !manifest.files.every((file) => typeof file === "string" && file.length > 0)) {
  throw new Error("runtime_seed_manifest_invalid");
}

const marker = join(runtimeDir, "runtime-seed", `${manifest.version}.json`);
if (!await exists(marker)) {
  const shouldApply = await isGeneratedFallback();
  if (shouldApply) for (const file of manifest.files) await copySeedFile(file);
  await mkdir(dirname(marker), { recursive: true });
  const temporaryMarker = `${marker}.tmp`;
  await writeFile(temporaryMarker, `${JSON.stringify({ version: manifest.version, appliedAt: new Date().toISOString(), applied: shouldApply }, null, 2)}\n`, "utf8");
  await rename(temporaryMarker, marker);
  console.log(`Foundry runtime seed ${manifest.version}: ${shouldApply ? "applied" : "preserved existing runtime"}`);
}
