/** Ensures runtime registry access stays inside plugin boundary and activation rules. */
import { spawnSync } from "node:child_process";
import fs, { readFileSync } from "node:fs";
import { dirname, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import { expectNoFsSyncDuring } from "../test-utils/fs-scan-assertions.js";
import { listGitTrackedFiles, toRepoRelativePath } from "../test-utils/repo-files.js";

const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
const allowedRuntimeResolverRefs = new Set([
  "src/commands/doctor.e2e-harness.ts",
  "src/infra/outbound/channel-bootstrap.runtime.ts",
  "src/plugins/capability-provider-runtime.ts",
  "src/plugins/loader-runtime-registry.ts",
  "src/plugins/loader.ts",
]);

function listSourceFiles(dir: string): string[] {
  const externalFiles = listExternalSourceFiles(dir);
  if (externalFiles) {
    return externalFiles;
  }
  return listSourceFilesByDirectory(dir);
}

function listExternalSourceFiles(dir: string): string[] | null {
  return listGitSourceFiles(dir) ?? listFindSourceFiles(dir);
}

function listGitSourceFiles(dir: string): string[] | null {
  const relativeRoot = toRepoRelativePath(repoRoot, dir) || ".";
  const files = listGitTrackedFiles({ repoRoot, pathspecs: relativeRoot });
  if (!files) {
    return null;
  }
  return files
    .map((file) => resolve(repoRoot, file))
    .filter((filePath) => fs.existsSync(filePath))
    .filter(isProductionTypeScriptFile)
    .toSorted();
}

function listFindSourceFiles(dir: string): string[] | null {
  const result = spawnSync("find", [dir, "-type", "f", "-name", "*.ts"], {
    cwd: repoRoot,
    encoding: "utf8",
    maxBuffer: 1024 * 1024 * 4,
    stdio: ["ignore", "pipe", "ignore"],
  });
  if (result.status !== 0) {
    return null;
  }
  return result.stdout
    .split("\n")
    .map((line) => line.trim())
    .filter((line) => line.length > 0)
    .map((file) => resolve(repoRoot, file))
    .filter(isProductionTypeScriptFile)
    .toSorted();
}

function listSourceFilesByDirectory(dir: string): string[] {
  const files: string[] = [];
  for (const entry of fs.readdirSync(dir)) {
    if (entry === "node_modules" || entry === "dist") {
      continue;
    }
    const path = resolve(dir, entry);
    const stat = fs.statSync(path);
    if (stat.isDirectory()) {
      files.push(...listSourceFilesByDirectory(path));
      continue;
    }
    if (!isProductionTypeScriptFile(path)) {
      continue;
    }
    files.push(path);
  }
  return files;
}

function isProductionTypeScriptFile(path: string): boolean {
  return (
    path.endsWith(".ts") &&
    !path.endsWith(".test.ts") &&
    !path.endsWith(".test.tsx") &&
    !/\.test-(?:fixtures|harness|helpers|mocks|setup|support|utils)\.tsx?$/u.test(path)
  );
}

describe("runtime plugin registry boundary", () => {
  it("lists source files without scanning src in-process", () => {
    expectNoFsSyncDuring(() => {
      const files = listSourceFiles(resolve(repoRoot, "src"));

      expect(files.length).toBeGreaterThan(0);
      expect(files.every(isProductionTypeScriptFile)).toBe(true);
    }, ["readdirSync", "statSync"]);
  });

  it("keeps runtime registry resolution behind the loader boundary", () => {
    const offenders = listSourceFiles(resolve(repoRoot, "src"))
      .map((path) => ({
        path,
        relativePath: relative(repoRoot, path),
        source: readFileSync(path, "utf8"),
      }))
      .filter(
        (file) =>
          !allowedRuntimeResolverRefs.has(file.relativePath) &&
          file.source.includes("resolveRuntimePluginRegistry"),
      )
      .map((file) => file.relativePath);

    expect(offenders).toStrictEqual([]);
  });
});
