#!/usr/bin/env bun
/**
 * Pre-publish gate — type-check every workspace package against the
 * monorepo's strict tsconfig.
 *
 * Required before any publish. The Bun test runner is intentionally
 * permissive (it transpiles, doesn't type-check), so without this gate
 * a package can pass `bun test` and still ship code that fails to
 * compile in a downstream consumer's strict TS project.
 *
 *   Usage:
 *     bun run scripts/typecheck-all.ts          # check every package
 *     bun run scripts/typecheck-all.ts core sdk # check only listed
 *
 * Exit code 0 if every package compiles, 1 otherwise.
 */
import { readdirSync, statSync, existsSync } from 'fs';
import { join } from 'path';
import { $ } from 'bun';

const ROOT = join(import.meta.dir, '..');
const PACKAGES_DIR = join(ROOT, 'packages');

const filter = new Set(process.argv.slice(2));

const packages = readdirSync(PACKAGES_DIR).filter((name) => {
  const p = join(PACKAGES_DIR, name);
  if (!statSync(p).isDirectory()) return false;
  if (!existsSync(join(p, 'tsconfig.json'))) return false;
  if (filter.size > 0 && !filter.has(name) && !filter.has(name.replace(/^shade-/, ''))) {
    return false;
  }
  return true;
});

let failures = 0;
const failed: { pkg: string; out: string }[] = [];

for (const pkg of packages) {
  const dir = join(PACKAGES_DIR, pkg);
  const proc = Bun.spawnSync(['bunx', 'tsc', '--noEmit', '-p', 'tsconfig.json'], {
    cwd: dir,
    stdout: 'pipe',
    stderr: 'pipe',
  });
  const stdout = proc.stdout.toString();
  const stderr = proc.stderr.toString();
  const out = (stdout + stderr)
    .split('\n')
    .filter((l) => !/^Resolving|^Resolved|^Saved/.test(l))
    .join('\n')
    .trim();

  if (proc.exitCode === 0 && out.length === 0) {
    console.log(`  ✓ ${pkg}`);
  } else {
    failures++;
    failed.push({ pkg, out });
    console.log(`  ✗ ${pkg}`);
  }
}

console.log();

// Step 2 — consumer-strict smoke. Compiles a tiny "as if I were a
// downstream app" project against our public API surface under the
// consumer-likely tsconfig (`lib: ["DOM"]` + `exactOptionalPropertyTypes`).
// Catches type-bugs that ONLY surface when our internal narrower types
// meet a consumer's standard-library types — the class of bug `tsc`
// inside our own packages does not see (because our packages compile
// against `lib: ["ES2022"]` only).
if (filter.size === 0) {
  console.log('Consumer-strict smoke (lib: DOM, exactOptional, paths→workspace) ...');
  const consumerDir = join(ROOT, 'tests', 'consumer-strict');
  if (existsSync(join(consumerDir, 'tsconfig.json'))) {
    const proc = Bun.spawnSync(['bunx', 'tsc', '--noEmit', '-p', 'tsconfig.json'], {
      cwd: consumerDir,
      stdout: 'pipe',
      stderr: 'pipe',
    });
    const out = (proc.stdout.toString() + proc.stderr.toString())
      .split('\n')
      .filter((l) => !/^Resolving|^Resolved|^Saved/.test(l))
      .join('\n')
      .trim();
    if (proc.exitCode === 0 && out.length === 0) {
      console.log('  ✓ consumer-strict');
    } else {
      failures++;
      failed.push({ pkg: 'consumer-strict', out });
      console.log('  ✗ consumer-strict');
    }
  }
  console.log();
}

if (failures === 0) {
  console.log(`All ${packages.length} packages type-check cleanly.`);
  process.exit(0);
}

console.error(`${failures} of ${packages.length} packages failed:\n`);
for (const f of failed) {
  console.error(`── ${f.pkg} ──`);
  console.error(f.out);
  console.error();
}
process.exit(1);
