import { execFileSync } from "node:child_process";

const dryRun = process.argv.includes("--dry-run") || process.env.DRY_RUN === "true";

// Read package.json to get name, version, and whether to also publish to JSR.
const pkg = JSON.parse(await Bun.file("package.json").text());
const { name, version } = pkg;
// Publish to JSR alongside npm, unless the package opts out with "jsr": false
// (the web-component packages do, since JSR forbids the global type augmentation
// custom elements need). Same predicate as package.ts so the manifest it
// generates and the publish here stay in lockstep; the release-script clause is
// always true here (this file is the release script).
const publishJsr = Boolean(pkg.scripts?.release) && pkg.jsr !== false;

// Whether this exact version already exists on each registry. The two are
// checked independently so a package can be onboarded onto JSR even when its
// current version is already on npm.
function npmHasVersion(): boolean {
	try {
		const published = execFileSync("npm", ["view", name, "version"], {
			encoding: "utf8",
			stdio: ["pipe", "pipe", "pipe"],
		}).trim();
		return published === version;
	} catch {
		return false; // not published yet
	}
}

async function jsrHasVersion(): Promise<boolean> {
	try {
		const res = await fetch(`https://jsr.io/${name}/meta.json`);
		if (!res.ok) return false;
		const meta = (await res.json()) as { versions?: Record<string, unknown> };
		return Boolean(meta.versions?.[version]);
	} catch {
		return false;
	}
}

// Skip the registry checks in dry-run mode so we always exercise the build +
// publish manifest, even when the version is already published.
const npmDone = !dryRun && npmHasVersion();
const jsrDone = !dryRun && publishJsr && (await jsrHasVersion());

if (!dryRun && npmDone && (!publishJsr || jsrDone)) {
	console.log(`⏭️  ${name}@${version} already published, skipping`);
	process.exit(0);
}

console.log(`📦 Building ${name}@${version}...`);
execFileSync("bun", ["run", "build"], { stdio: "inherit" });

// npm
if (dryRun) {
	// `npm publish --dry-run` still hits the registry to check for version
	// conflicts and errors out when the version is already published (which
	// is the common case on PRs of main). `npm pack --dry-run` exercises the
	// same packaging path without any registry roundtrip.
	console.log(`🧪 Packing ${name}@${version} (dry-run)...`);
	execFileSync("npm", ["pack", "--dry-run"], { stdio: "inherit", cwd: "dist" });
} else if (npmDone) {
	console.log(`⏭️  ${name}@${version} already on npm, skipping`);
} else {
	console.log(`🚀 Publishing ${name}@${version} to npm...`);
	// Use npm for publishing to support OIDC trusted publishing
	execFileSync("npm", ["publish", "--access", "public"], { stdio: "inherit", cwd: "dist" });
}

// JSR (jsr.json is generated by the build for packages with a release script).
if (publishJsr) {
	// Call `deno publish` directly (what `jsr publish` wraps) using the deno from
	// the nix devshell, so parallel package releases don't race on a runtime
	// jsr/deno download into a shared bunx cache. These unstable flags mirror what
	// the jsr CLI passes. jsr.json and the generated dist leave the tree dirty, so
	// allow it. OIDC trusted publishing kicks in automatically under GitHub
	// Actions; locally it falls back to an interactive browser login.
	const args = [
		"publish",
		"--unstable-bare-node-builtins",
		"--unstable-sloppy-imports",
		"--unstable-byonm",
		"--no-check",
		"--allow-dirty",
	];
	// Only libs publish to JSR now (web components opt out), and their built
	// .d.ts has explicit types, so slow types shouldn't appear. Keep this as a
	// safety valve the release workflow can toggle without a code change.
	if (process.env.JSR_ALLOW_SLOW_TYPES === "true") args.push("--allow-slow-types");
	if (dryRun) {
		console.log(`🧪 Publishing ${name}@${version} to JSR (dry-run)...`);
		args.push("--dry-run");
		execFileSync("deno", args, { stdio: "inherit" });
	} else if (jsrDone) {
		console.log(`⏭️  ${name}@${version} already on JSR, skipping`);
	} else {
		console.log(`🚀 Publishing ${name}@${version} to JSR...`);
		execFileSync("deno", args, { stdio: "inherit" });
	}
}
