import console from "node:console";
import { existsSync, readFileSync, readdirSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import process from "node:process";

const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
let passed = 0;
let failed = 0;
let warned = 0;
const failures = [];

function check(name, condition, detail = "") {
  if (condition) {
    passed += 1;
    console.log(`  ok   ${name}`);
    return;
  }
  failed += 1;
  failures.push(detail ? `${name}: ${detail}` : name);
  console.error(`  FAIL ${name}${detail ? ` — ${detail}` : ""}`);
}

function warn(name, detail = "") {
  warned += 1;
  console.warn(`  warn ${name}${detail ? ` — ${detail}` : ""}`);
}

function text(path) {
  return readFileSync(join(root, path), "utf8");
}

function has(path, pattern) {
  const value = text(path);
  return typeof pattern === "string" ? value.includes(pattern) : pattern.test(value);
}

const packageDirs = readdirSync(join(root, "packages"), { withFileTypes: true })
  .filter(
    (entry) =>
      entry.isDirectory() && existsSync(join(root, "packages", entry.name, "package.json")),
  )
  .map((entry) => entry.name);
const manifests = packageDirs
  .map((dir) => ({
    dir,
    manifest: JSON.parse(text(`packages/${dir}/package.json`)),
  }))
  .filter(({ manifest }) => manifest.name?.startsWith("@wrnexus/"));

const rootVersion = JSON.parse(text("package.json")).version;
check(
  "root version is compatible with the 0.7 security foundation",
  /^0\.(?:7|8)\./.test(rootVersion),
  rootVersion,
);
const temporaryTypecheckFiles = ["focus-shims.d.ts", "tsconfig.focus.json"].filter((path) =>
  existsSync(join(root, path)),
);
const rootTsconfig = JSON.parse(text("tsconfig.json"));
const rootExcludes = new Set(rootTsconfig.exclude ?? []);
check(
  "root typecheck excludes temporary focused files",
  rootExcludes.has("focus-shims.d.ts") &&
    rootExcludes.has("tsconfig.focus.json") &&
    rootExcludes.has("**/focus-shims.d.ts") &&
    rootExcludes.has("**/tsconfig.focus.json"),
  "Keep the temporary focused typecheck files outside the root TypeScript program.",
);
if (temporaryTypecheckFiles.length > 0) {
  warn(
    "temporary focused typecheck files are local leftovers",
    `${temporaryTypecheckFiles.join(", ")} will be removed by: bun run repair:workspace`,
  );
}
check(
  "lint and formatting ignore temporary focused files",
  has("eslint.config.js", '"focus-shims.d.ts"') &&
    has("eslint.config.js", '"tsconfig.focus.json"') &&
    has(".prettierignore", "focus-shims.d.ts") &&
    has(".prettierignore", "tsconfig.focus.json"),
  "Temporary focused helpers must not create lint or formatting cascades before workspace repair.",
);
check(
  "all framework packages use compatible independent versions",
  manifests.every(({ manifest }) => /^0\.(?:7|8)\.\d+$/.test(manifest.version)),
  [...new Set(manifests.map(({ manifest }) => manifest.version))].join(", "),
);
for (const name of ["security", "cache", "image", "observability", "benchmark"]) {
  check(
    `@wrnexus/${name} package exists`,
    existsSync(join(root, "packages", name, "src", "index.ts")),
  );
}

check(
  "context-aware URL sanitizer is wired into compiler",
  has("packages/compiler/src/codegen.ts", "sanitizeUrlAttribute"),
);
check(
  "URL security checks avoid lint-blocked control-character regexes",
  has("packages/compiler/src/codegen.ts", "stripAsciiControlAndSpace") &&
    has("packages/security/src/url.ts", "hasAsciiControlOrSpace") &&
    has("packages/syntax/src/diagnostics.ts", "stripAsciiControlAndSpace") &&
    !has("packages/compiler/src/codegen.ts", /\\u0000-\\u0020/) &&
    !has("packages/security/src/url.ts", /\\u0000-\\u0020/) &&
    !has("packages/syntax/src/diagnostics.ts", /\\u0000-\\u0020/),
  "Use character-code checks instead of control-character regular expressions.",
);
check(
  "trusted HTML requires an explicit policy",
  has("packages/security/src/trusted-html.ts", "createTrustedHtml"),
);
check(
  "reactive URL updates are sanitized",
  has("packages/csr/src/reactive-runtime.ts", "sanitizeReactiveUrl"),
);
check(
  "SSR store state uses secure serialization",
  has("packages/ssr/src/store-context.ts", "serializeForHtml"),
);
check("prototype pollution keys are rejected", has("packages/security/src/object.ts", "__proto__"));
check(
  "SSRF helper blocks private networks",
  has("packages/security/src/fetch.ts", "isPrivateAddress"),
);
check(
  "CSRF checks Origin and Fetch Metadata",
  has("packages/core/src/csrf.ts", /sec-fetch-site|validateOrigin/),
);
check(
  "security headers include CSP and isolation",
  has("packages/core/src/headers.ts", "Origin-Agent-Cluster"),
);
check(
  "request limits are automatic runtime middleware",
  has("packages/dev-server/src/runtime.ts", "requestHardening"),
);
check(
  "gateway enforces request limits",
  has("packages/dev-server/src/gateway.ts", "GatewayRequestLimits"),
);
check(
  "gateway enforces WebSocket limits",
  has("packages/dev-server/src/gateway.ts", "maxQueuedMessages"),
);
check(
  "realtime enforces per-room security",
  has("packages/core/src/realtime.ts", "RealtimeSecurityOptions"),
);
check(
  "database instrumentation detects N+1 patterns",
  has("packages/db/src/performance.ts", "WRN-DB-DUPLICATE-QUERY"),
);
check(
  "CSS performance audit is available",
  has("packages/styles/src/audit.ts", "WRN-CSS-TRANSITION-ALL"),
);
check(
  "static runtime analysis is exported",
  has("packages/compiler/src/analysis.ts", "analyzeRuntimeRequirements"),
);
check(
  "static pages default to zero framework JavaScript",
  has("packages/dev-server/src/runtime.ts", 'navigation.mode ?? "auto"') &&
    has("packages/dev-server/src/runtime.ts", "scripts.length > 0"),
);
check(
  "Brotli delivery is preferred",
  has("packages/dev-server/src/runtime.ts", "brotliCompressSync"),
);
check(
  "Happy DOM validation events and elements use one DOM type system",
  has("packages/validation/test/validation.test.ts", "type Event as HappyDOMEvent") &&
    has(
      "packages/validation/test/validation.test.ts",
      "type HTMLInputElement as HappyDOMHTMLInputElement",
    ) &&
    has(
      "packages/validation/test/validation.test.ts",
      "type HTMLFormElement as HappyDOMHTMLFormElement",
    ) &&
    has(
      "packages/validation/test/validation.test.ts",
      "function windowEvent(win: Window, type: string, init?: IEventInit): HappyDOMEvent",
    ) &&
    !has(
      "packages/validation/test/validation.test.ts",
      /as unknown as (?:HTMLInputElement|HTMLFormElement|HTMLElement)/,
    ),
  "Use Happy DOM Event and element types together; do not mix them with browser DOM globals.",
);
check(
  "DevToolbar security checks include CSRF",
  has("packages/dev-toolbar/src/rules/security.ts", "security/missing-csrf"),
);
check(
  "DevToolbar performance checks include hydration",
  has("packages/dev-toolbar/src/rules/performance.ts", "performance/hydration-count"),
);
check("release migration includes 0.7.0", has("packages/cli/src/update.ts", 'version: "0.7.0"'));
check(
  "21-point implementation matrix exists",
  existsSync(join(root, "docs", "SECURITY-PERFORMANCE-0.7.md")),
);
check(
  "production audit script exists",
  existsSync(join(root, "scripts", "security-performance-audit.mjs")),
);
check(
  "workspace repair command exists",
  existsSync(join(root, "scripts", "repair-workspace.mjs")) &&
    existsSync(join(root, "scripts", "test-workspace-repair.mjs")) &&
    JSON.parse(text("package.json")).scripts?.["repair:workspace"] ===
      "node scripts/repair-workspace.mjs" &&
    JSON.parse(text("package.json")).scripts?.["check:workspace"] ===
      "node scripts/repair-workspace.mjs --check" &&
    has("scripts/release.ts", "check:workspace"),
);
check(
  "security audit scans Git-tracked release files",
  has("scripts/security-performance-audit.mjs", 'execFileSync("git", ["ls-files", "-z"]') &&
    has("scripts/security-performance-audit.mjs", "SEC-NO-TRACKED-SECRET-FILES"),
);
check("SBOM generator exists", existsSync(join(root, "scripts", "generate-sbom.mjs")));
check(
  "SBOM includes transitive bun.lock packages",
  has("scripts/generate-sbom.mjs", "lock.packages"),
);
check(
  "SBOM parse errors preserve their original cause",
  has("scripts/generate-sbom.mjs", "{ cause: error }") &&
    has("scripts/test-workspace-repair.mjs", 'import process from "node:process"'),
);
check(
  "staged packages are secret-scanned and hashed",
  has("scripts/publish-packages.ts", "validateAndHashStage") &&
    has("scripts/publish-packages.ts", "PACKAGE-INTEGRITY.json"),
);
check("benchmark runner exists", existsSync(join(root, "scripts", "benchmark-framework.mjs")));
check("release gate formats generated UI references", has("scripts/release.ts", "--write"));

console.log(`\n${passed} passed`);
console.log(`${warned} warnings`);
console.log(`${failed} failed`);
if (failed) {
  console.error("\nFailures:");
  for (const failure of failures) console.error(`- ${failure}`);
  process.exitCode = 1;
}
