#!/usr/bin/env node
import { readFile, readdir, stat } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";

const workspaceRoot = path.resolve(
  process.env.RUNX_BOUNDARY_WORKSPACE_ROOT ?? fileURLToPath(new URL("..", import.meta.url)),
);
const boundaryGuardPath = path.resolve(fileURLToPath(import.meta.url));

const sourceExtensions = new Set([".ts", ".tsx", ".mts", ".cts"]);
const activeTypeScriptJavaScriptExtensions = new Set([
  ".ts",
  ".tsx",
  ".mts",
  ".cts",
  ".js",
  ".jsx",
  ".mjs",
  ".cjs",
]);
const activeCredentialContractExtensions = new Set([
  ".ts",
  ".tsx",
  ".mts",
  ".cts",
  ".js",
  ".jsx",
  ".mjs",
  ".cjs",
  ".rs",
  ".json",
]);
const ignoredDirectoryNames = new Set([
  ".git",
  ".turbo",
  "node_modules",
  "dist",
  ".build",
  "coverage",
  "target",
]);
const hostedConnectBrokerageScanRoots = ["packages", "plugins", "scripts", "tests"];
const hostedCredentialContractScanRoots = [
  "packages",
  "plugins",
  "scripts",
  "tests",
  "fixtures/contracts",
  "schemas",
  "crates/runx-contracts/src",
  "crates/runx-contracts/tests",
  "crates/runx-runtime/src",
  "crates/runx-core/src",
];
const literalName = (...parts) => parts.join("");
const literalPattern = (...parts) => new RegExp(literalName(...parts));
const privateProviderGatewayUpstreamPattern = new RegExp("nan" + "go", "i");
const legacyRunxConnectPrivateUpstreamEnvPattern = new RegExp(`RUNX_CONNECT_${"NAN"}${"GO"}`);
const hostedOAuthAuthModePattern = /["']?auth_mode["']?\s*[:=]\s*["']oauth(?:_bearer)?["']/;
const legacyProviderReferenceValuePattern = new RegExp("\\bco" + "nn_[A-Za-z0-9_:-]+");
const forbiddenHostedConnectBrokerageTerms = [
  { name: "private provider gateway upstream", pattern: privateProviderGatewayUpstreamPattern },
  { name: literalName("oauth", "_required"), pattern: literalPattern("oauth", "_required") },
  { name: literalName("authorize", "_url"), pattern: literalPattern("authorize", "_url") },
  { name: literalName("Connect", "Session"), pattern: literalPattern("Connect", "Session") },
  { name: literalName("Hosted", "Provider", "Reference"), pattern: literalPattern("Hosted", "Provider", "Reference") },
  { name: literalName("connect", "-http"), pattern: literalPattern("connect", "-http") },
  { name: literalName("create", "Http", "Connect", "Service"), pattern: literalPattern("create", "Http", "Connect", "Service") },
  { name: "legacy private provider gateway env", pattern: legacyRunxConnectPrivateUpstreamEnvPattern },
  {
    name: literalName("RUNX_CONNECT_PROVIDER", "_GATEWAY"),
    pattern: literalPattern("RUNX_CONNECT_PROVIDER", "_GATEWAY"),
  },
];
const forbiddenHostedCredentialContractTerms = [
  { name: "hosted OAuth auth_mode", pattern: hostedOAuthAuthModePattern },
  { name: "legacy conn_ provider reference value", pattern: legacyProviderReferenceValuePattern },
  { name: literalName("opaque", "_connection"), pattern: literalPattern("opaque", "_connection") },
  { name: literalName("redact", "_connect", "_text"), pattern: literalPattern("redact", "_connect", "_text") },
  { name: literalName("credential_delivery", ".broker", "_response"), pattern: literalPattern("credential_delivery", "\\.broker", "_response") },
  { name: literalName("credential_delivery", "_broker", "_response"), pattern: literalPattern("credential_delivery", "_broker", "_response") },
  { name: literalName("credential-delivery", "-broker", "-response"), pattern: literalPattern("credential-delivery", "-broker", "-response") },
  { name: literalName("CredentialDelivery", "Broker", "Response"), pattern: literalPattern("CredentialDelivery", "Broker", "Response") },
];
const retiredCorePackageName = ["@runxhq", "core"].join("/");
const forbiddenPureNodeImports = new Set([
  "fs",
  "fs/promises",
  "node:fs",
  "node:fs/promises",
  "path",
  "node:path",
  "child_process",
  "node:child_process",
  "http",
  "node:http",
  "https",
  "node:https",
  "net",
  "node:net",
  "tls",
  "node:tls",
  "dgram",
  "node:dgram",
  "dns",
  "node:dns",
  "worker_threads",
  "node:worker_threads",
]);
const sunsetTsPackageNames = new Set(["runtime-local", "adapters"]);
const sunsetTsPackageImportPrefixes = ["@runxhq/runtime-local", "@runxhq/adapters"];
const forbiddenCompatibilityPackageNames = new Set([
  "@runxhq/runtime-local-v2",
  "@runxhq/adapters-v2",
  "@runxhq/runtime-local-shim",
  "@runxhq/adapters-shim",
  "@runxhq/runtime-local-compat",
  "@runxhq/adapters-compat",
  "@runxhq/runtime-local-compatibility",
  "@runxhq/adapters-compatibility",
  "runtime-local-v2",
  "adapters-v2",
  "runtime-local-shim",
  "adapters-shim",
  "runtime-local-compat",
  "adapters-compat",
  "runtime-local-compatibility",
  "adapters-compatibility",
]);
const forbiddenCompatibilityPackageDirectoryNames = new Set([
  "runtime-local-v2",
  "adapters-v2",
  "runtime-local-shim",
  "adapters-shim",
  "runtime-local-compat",
  "adapters-compat",
  "runtime-local-compatibility",
  "adapters-compatibility",
]);
const packageDependencyFields = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
const aliasConfigFiles = ["tsconfig.base.json", "vitest.workspace-aliases.ts"];
const forbiddenPackageImports = {
  "runtime-local": {
    prefixes: [
      "@runxhq/adapters",
      "@runxhq/cli",
      "@runxhq/host-adapters",
      "@runxhq/langchain",
    ],
    reason: "@runxhq/runtime-local must not depend on downstream adapters, CLI, or host packages.",
  },
  adapters: {
    prefixes: [
      "@runxhq/cli",
      "@runxhq/host-adapters",
      "@runxhq/langchain",
    ],
    reason: "@runxhq/adapters must stay below host, CLI, and framework packages.",
  },
  "host-adapters": {
    prefixes: [
      "@runxhq/adapters",
      "@runxhq/cli",
      "@runxhq/langchain",
    ],
    reason: "@runxhq/host-adapters must not depend on adapters, CLI, or framework packages.",
  },
  langchain: {
    prefixes: [
      "@runxhq/adapters",
      "@runxhq/cli",
      "@runxhq/host-adapters",
    ],
    reason: "@runxhq/langchain must not depend on adapters, CLI, or host packages.",
  },
};
const pureCoreDomains = ["parser", "policy", "state-machine"];
const relativeRuntimeDomainPattern = /(^|\/)(runner-local|harness|sdk|mcp)(\/|$)/;
const staticSpecifierPattern =
  /\b(?:import|export)\s+(?:type\s+)?(?:[^'";]*?\s+from\s+)?["']([^"']+)["']|import\s*\(\s*["']([^"']+)["']\s*\)/g;
const findings = [];
const packageManifestCache = new Map();
const workspacePackageNames = await readWorkspacePackageNames();

await checkRetiredCorePackageDeleted();
await checkForbiddenCompatibilityPackages();
await checkForbiddenCompatibilityAliases();
await checkForbiddenHostedConnectBrokerage();
await checkForbiddenHostedCredentialContracts();
for (const filePath of await findSourceFiles(workspaceRoot)) {
  await checkSourceFile(filePath);
}

if (findings.length > 0) {
  console.error("Boundary check failed:");
  for (const finding of findings) {
    console.error(`- ${finding}`);
  }
  process.exit(1);
}

console.log("Boundary check passed.");

async function checkRetiredCorePackageDeleted() {
  const corePackagePath = path.join(workspaceRoot, "packages", "core");
  if (await statIfExists(corePackagePath)) {
    findings.push(`packages/core still exists; ${retiredCorePackageName} is retired and must not be restored.`);
  }
  for (const sunsetPath of ["packages/runtime-local/package.json", "packages/adapters/package.json"]) {
    if (await readJsonIfExists(path.join(workspaceRoot, sunsetPath))) {
      findings.push(`${sunsetPath} still exists; local execution is Rust-owned.`);
    }
  }
}

async function checkForbiddenCompatibilityPackages() {
  const packagesDir = path.join(workspaceRoot, "packages");
  const manifestPaths = [path.join(workspaceRoot, "package.json")];

  for (const entry of await readdir(packagesDir, { withFileTypes: true })) {
    if (!entry.isDirectory()) {
      continue;
    }

    if (forbiddenCompatibilityPackageDirectoryNames.has(entry.name)) {
      findings.push(`packages/${entry.name} uses a compatibility package directory; runtime-local/adapters shims are not allowed.`);
    }
    if (sunsetTsPackageNames.has(entry.name)) {
      findings.push(`packages/${entry.name} is a sunset TypeScript executor package and must be deleted.`);
    }

    manifestPaths.push(path.join(packagesDir, entry.name, "package.json"));
  }

  for (const manifestPath of manifestPaths) {
    const manifest = await readJsonIfExists(manifestPath);
    if (!manifest) {
      continue;
    }

    const rel = toPosix(path.relative(workspaceRoot, manifestPath));
    if (isForbiddenCompatibilityPackageName(manifest.name)) {
      findings.push(`${rel} names ${manifest.name}; runtime-local/adapters compatibility packages are not allowed.`);
    }
    if (manifest.name === "@runxhq/runtime-local" || manifest.name === "@runxhq/adapters") {
      findings.push(`${rel} names sunset TypeScript executor package ${manifest.name}.`);
    }

    for (const field of packageDependencyFields) {
      const dependencies = manifest[field];
      if (!dependencies || typeof dependencies !== "object" || Array.isArray(dependencies)) {
        continue;
      }

      for (const dependencyName of Object.keys(dependencies)) {
        if (isForbiddenCompatibilityPackageName(dependencyName)) {
          findings.push(`${rel} declares ${dependencyName} in ${field}; runtime-local/adapters compatibility packages are not allowed.`);
        }
      }
    }
  }
}

async function checkForbiddenCompatibilityAliases() {
  for (const relativePath of aliasConfigFiles) {
    const absolutePath = path.join(workspaceRoot, relativePath);
    const source = await readFile(absolutePath, "utf8");

    if (relativePath.endsWith(".json")) {
      checkJsonAliasConfig(relativePath, JSON.parse(source));
      continue;
    }

    checkTextAliasConfig(relativePath, source);
  }
}

function checkJsonAliasConfig(rel, config) {
  const paths = config?.compilerOptions?.paths;
  if (!paths || typeof paths !== "object" || Array.isArray(paths)) {
    return;
  }

  for (const [alias, targets] of Object.entries(paths)) {
    checkAliasToken(rel, alias);
    const targetList = Array.isArray(targets) ? targets : [targets];
    for (const target of targetList) {
      if (typeof target === "string") {
        checkAliasToken(rel, target);
      }
    }
  }
}

function checkTextAliasConfig(rel, source) {
  for (const token of extractStringLiterals(source)) {
    checkAliasToken(rel, token);
  }
}

function checkAliasToken(rel, token) {
  const normalized = toPosix(token);
  const packageName = packageSpecifierName(normalized.replace(/\/\*$/, ""));
  if (isForbiddenCompatibilityPackageName(packageName)) {
    findings.push(`${rel} aliases ${token}; runtime-local/adapters compatibility aliases are not allowed.`);
    return;
  }

  for (const segment of normalized.split(/[\/\\]/)) {
    if (forbiddenCompatibilityPackageDirectoryNames.has(segment)) {
      findings.push(`${rel} aliases ${token}; runtime-local/adapters compatibility aliases are not allowed.`);
      return;
    }
  }
}

async function checkForbiddenHostedConnectBrokerage() {
  for (const rootName of hostedConnectBrokerageScanRoots) {
    const rootPath = path.join(workspaceRoot, rootName);
    const entry = await statIfExists(rootPath);
    if (!entry?.isDirectory()) {
      continue;
    }

    for (const filePath of await findActiveTypeScriptJavaScriptFiles(rootPath)) {
      const rel = toPosix(path.relative(workspaceRoot, filePath));
      checkForbiddenHostedConnectBrokerageInText(rel, rel, "path");
      const source = await readFile(filePath, "utf8");
      checkForbiddenHostedConnectBrokerageInSource(rel, source);
    }
  }
}

function checkForbiddenHostedConnectBrokerageInSource(rel, source) {
  const lines = source.split(/\r?\n/);
  for (const [index, line] of lines.entries()) {
    checkForbiddenHostedConnectBrokerageInText(rel, line, `line ${index + 1}`);
  }
}

function checkForbiddenHostedConnectBrokerageInText(rel, text, location) {
  for (const term of forbiddenHostedConnectBrokerageTerms) {
    if (term.pattern.test(text)) {
      findings.push(`${rel} contains forbidden hosted connect/OAuth brokerage term ${term.name} in ${location}.`);
    }
  }
}

async function checkForbiddenHostedCredentialContracts() {
  for (const rootName of hostedCredentialContractScanRoots) {
    const rootPath = path.join(workspaceRoot, rootName);
    const entry = await statIfExists(rootPath);
    if (!entry?.isDirectory()) {
      continue;
    }

    for (const filePath of await findActiveCredentialContractFiles(rootPath)) {
      const rel = toPosix(path.relative(workspaceRoot, filePath));
      const source = await readFile(filePath, "utf8");
      const lines = source.split(/\r?\n/);
      for (const [index, line] of lines.entries()) {
        for (const term of forbiddenHostedCredentialContractTerms) {
          if (term.pattern.test(line)) {
            findings.push(`${rel} contains forbidden hosted OAuth credential contract term ${term.name} in line ${index + 1}.`);
          }
        }
      }
    }
  }
}

async function checkSourceFile(filePath) {
  const source = await readFile(filePath, "utf8");
  const specifiers = extractSpecifiers(source);
  const rel = toPosix(path.relative(workspaceRoot, filePath));
  const packageSource = getPackageSource(rel);

  for (const specifier of specifiers) {
    if (specifierMatchesPackageName(specifier, retiredCorePackageName)) {
      findings.push(`${rel} imports ${specifier}; ${retiredCorePackageName} is retired and must not be restored.`);
    }

    if (packageSource) {
      if (!checkSurvivingTsPackageImport(rel, packageSource.packageName, specifier)) {
        checkForbiddenPackageImport(rel, packageSource.packageName, specifier);
      }
      await checkDeclaredWorkspaceImport(rel, packageSource.packageName, specifier);
    }

    checkForbiddenCompatibilityImport(rel, specifier);

    if (packageSource?.packageName === "core") {
      checkCoreImport(rel, packageSource.domain, specifier);
    }

    if (rel.startsWith("packages/") && isCloudSpecifier(specifier)) {
      findings.push(`${rel} imports cloud code; oss must not depend on cloud.`);
    }
  }

}

function checkCoreImport(rel, domain, specifier) {
  if (specifier.startsWith(".") && relativeRuntimeDomainPattern.test(toPosix(path.normalize(path.join(path.dirname(rel), specifier))))) {
    findings.push(`${rel} imports ${specifier}; core cannot reach removed runtime-local domains by relative path.`);
  }

  if (pureCoreDomains.includes(domain)) {
    if (forbiddenPureNodeImports.has(specifier)) {
      findings.push(`${rel} imports ${specifier}; ${domain} must remain pure and deterministic.`);
    }
    if (specifierTargetsDomain(rel, specifier, "executor") || specifierTargetsDomain(rel, specifier, "tool-catalogs")) {
      findings.push(`${rel} imports ${specifier}; ${domain} cannot depend on execution or catalog boundaries.`);
    }
  }

  if (domain === "executor") {
    if (specifierTargetsDomain(rel, specifier, "adapters")) {
      findings.push(`${rel} imports ${specifier}; executor must stay protocol-agnostic and avoid concrete adapters.`);
    }
  }

  if (domain === "parser" && specifierTargetsDomain(rel, specifier, "adapters")) {
    findings.push(`${rel} imports ${specifier}; parser cannot depend on concrete adapters.`);
  }
}

function checkSurvivingTsPackageImport(rel, packageName, specifier) {
  if (sunsetTsPackageNames.has(packageName)) {
    return false;
  }

  if (sunsetTsPackageImportPrefixes.some((prefix) => specifierMatchesPackageName(specifier, prefix))) {
    findings.push(`${rel} imports ${specifier}; surviving TypeScript packages must not depend on sunset @runxhq/runtime-local or @runxhq/adapters packages.`);
    return true;
  }

  return false;
}

function checkForbiddenPackageImport(rel, packageName, specifier) {
  const rule = forbiddenPackageImports[packageName];
  if (!rule) {
    return;
  }
  if (rule.prefixes.some((prefix) => specifierMatchesPackageName(specifier, prefix))) {
    findings.push(`${rel} imports ${specifier}; ${rule.reason}`);
  }
}

function checkForbiddenCompatibilityImport(rel, specifier) {
  const packageName = packageSpecifierName(specifier);
  if (isForbiddenCompatibilityPackageName(packageName)) {
    findings.push(`${rel} imports ${specifier}; runtime-local/adapters compatibility packages are not allowed.`);
  }
}

async function checkDeclaredWorkspaceImport(rel, packageName, specifier) {
  const dependencyName = workspaceDependencyName(specifier);
  if (!dependencyName || !workspacePackageNames.has(dependencyName)) {
    return;
  }

  const manifest = await readPackageManifest(packageName);
  if (!manifest || manifest.name === dependencyName) {
    return;
  }
  if (packageName === "cli" && isNativeCliArtifactManifest(manifest)) {
    return;
  }

  const declared = {
    ...manifest.dependencies,
    ...manifest.devDependencies,
    ...manifest.peerDependencies,
    ...manifest.optionalDependencies,
  };
  if (!Object.hasOwn(declared, dependencyName)) {
    findings.push(`${rel} imports ${specifier}; ${manifest.name} must declare ${dependencyName} in package.json.`);
  }
}

function isNativeCliArtifactManifest(manifest) {
  const bin = typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.runx;
  const files = Array.isArray(manifest.files) ? manifest.files : [];
  const includesFileOrDirectory = (entry) => files.includes(entry) || files.some((file) => file.startsWith(`${entry}/`));
  return manifest.name === "@runxhq/cli"
    && bin === "./bin/runx"
    && includesFileOrDirectory("bin")
    && includesFileOrDirectory("native")
    && !files.includes("src")
    && !files.includes("dist")
    && !files.includes("tools");
}

function extractSpecifiers(source) {
  const specifiers = [];
  let match;
  while ((match = staticSpecifierPattern.exec(source)) !== null) {
    specifiers.push(match[1] ?? match[2]);
  }
  return specifiers;
}

function extractStringLiterals(source) {
  const literals = [];
  const stringLiteralPattern = /["']([^"']+)["']/g;
  let match;
  while ((match = stringLiteralPattern.exec(source)) !== null) {
    literals.push(match[1]);
  }
  return literals;
}

function getPackageSource(rel) {
  const parts = rel.split("/");
  if (parts[0] !== "packages" || parts[2] !== "src") {
    return undefined;
  }
  return {
    packageName: parts[1],
    domain: parts[3] ?? "",
  };
}

function workspaceDependencyName(specifier) {
  const match = /^(@runxhq\/[^/]+)/.exec(specifier);
  return match?.[1];
}

function packageSpecifierName(specifier) {
  if (specifier.startsWith(".")) {
    return undefined;
  }

  const parts = specifier.split("/");
  if (specifier.startsWith("@")) {
    return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : specifier;
  }

  return parts[0];
}

function specifierMatchesPackageName(specifier, packageName) {
  return specifier === packageName || specifier.startsWith(`${packageName}/`);
}

function isForbiddenCompatibilityPackageName(packageName) {
  return typeof packageName === "string" && forbiddenCompatibilityPackageNames.has(packageName);
}

async function readWorkspacePackageNames() {
  const packagesDir = path.join(workspaceRoot, "packages");
  const names = new Set();
  for (const entry of await readdir(packagesDir, { withFileTypes: true })) {
    if (!entry.isDirectory()) {
      continue;
    }
    const manifest = await readPackageManifest(entry.name);
    if (manifest?.name) {
      names.add(manifest.name);
    }
  }
  return names;
}

async function readPackageManifest(packageName) {
  if (packageManifestCache.has(packageName)) {
    return packageManifestCache.get(packageName);
  }
  const manifestPath = path.join(workspaceRoot, "packages", packageName, "package.json");
  const manifest = await readJsonIfExists(manifestPath);
  packageManifestCache.set(packageName, manifest);
  return manifest;
}

function specifierTargetsDomain(rel, specifier, domain) {
  if (specifier === `@runxhq/${domain}` || specifier.startsWith(`@runxhq/${domain}/`)) {
    return true;
  }
  if (!specifier.startsWith(".")) {
    return false;
  }
  const target = toPosix(path.normalize(path.join(path.dirname(rel), specifier)));
  return target.split("/").includes(domain);
}

function isCloudSpecifier(specifier) {
  return specifier === "cloud" || specifier.startsWith("cloud/") || specifier.includes("/cloud/");
}

async function findSourceFiles(root) {
  const files = [];
  await walk(root, files);
  return files;
}

async function findActiveTypeScriptJavaScriptFiles(root) {
  const files = [];
  await walkActiveTypeScriptJavaScript(root, files);
  return files;
}

async function findActiveCredentialContractFiles(root) {
  const files = [];
  await walkActiveCredentialContract(root, files);
  return files;
}

async function walkActiveTypeScriptJavaScript(directory, files) {
  for (const entry of await readdir(directory, { withFileTypes: true })) {
    if (entry.isDirectory()) {
      if (!isIgnoredDirectoryName(entry.name)) {
        await walkActiveTypeScriptJavaScript(path.join(directory, entry.name), files);
      }
      continue;
    }
    if (!entry.isFile() || !activeTypeScriptJavaScriptExtensions.has(path.extname(entry.name))) {
      continue;
    }
    const filePath = path.join(directory, entry.name);
    if (path.resolve(filePath) === boundaryGuardPath) {
      continue;
    }
    files.push(filePath);
  }
}

async function walkActiveCredentialContract(directory, files) {
  for (const entry of await readdir(directory, { withFileTypes: true })) {
    if (entry.isDirectory()) {
      if (!isIgnoredDirectoryName(entry.name)) {
        await walkActiveCredentialContract(path.join(directory, entry.name), files);
      }
      continue;
    }
    if (!entry.isFile() || !activeCredentialContractExtensions.has(path.extname(entry.name))) {
      continue;
    }
    const filePath = path.join(directory, entry.name);
    if (path.resolve(filePath) === boundaryGuardPath) {
      continue;
    }
    files.push(filePath);
  }
}

async function walk(directory, files) {
  for (const entry of await readdir(directory, { withFileTypes: true })) {
    if (entry.isDirectory()) {
      if (!isIgnoredDirectoryName(entry.name)) {
        await walk(path.join(directory, entry.name), files);
      }
      continue;
    }
    if (!entry.isFile() || !sourceExtensions.has(path.extname(entry.name)) || isTestFile(entry.name)) {
      continue;
    }
    files.push(path.join(directory, entry.name));
  }
}

async function statIfExists(filePath) {
  try {
    return await stat(filePath);
  } catch (error) {
    if (isNotFound(error)) {
      return undefined;
    }
    throw error;
  }
}

async function readJsonIfExists(filePath) {
  let contents;
  try {
    contents = await readFile(filePath, "utf8");
  } catch (error) {
    if (isNotFound(error)) {
      return undefined;
    }
    throw error;
  }
  return JSON.parse(contents);
}

function isNotFound(error) {
  return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
}

function isTestFile(fileName) {
  return /\.(test|spec)\.(ts|tsx|mts|cts)$/.test(fileName);
}

function isIgnoredDirectoryName(name) {
  return ignoredDirectoryNames.has(name) || name.startsWith("target-");
}

function toPosix(input) {
  return input.split(path.sep).join("/");
}
