import { afterAll, describe, expect, it, vi } from "vitest";

// ESLint Node API init is slow on first lint (~5-8s). Increase timeout
// so the first test in this file doesn't flake under load.
vi.setConfig({ testTimeout: 30_000 });
import { ESLint } from "eslint";
import fs from "node:fs";
import path from "node:path";
import crypto from "node:crypto";

const ROOT = path.resolve(import.meta.dirname, "../..");
const eslint = new ESLint({ cwd: ROOT });

/** Probe files created during this test run, cleaned up in afterAll. */
const probeFiles: string[] = [];

function uid(): string {
  return crypto.randomBytes(4).toString("hex");
}

function cleanupProbe(absPath: string): void {
  try {
    fs.unlinkSync(absPath);
  } catch { /* file may not exist */ }
  let dir = path.dirname(absPath);
  const srcDir = path.join(ROOT, "src");
  while (dir.length > srcDir.length) {
    try { fs.rmdirSync(dir); } catch { break; }
    dir = path.dirname(dir);
  }
}

afterAll(() => {
  for (const f of probeFiles) cleanupProbe(f);
});

async function lintProbe(
  sourcePath: string,
  sourceContent: string,
  targetPath?: string,
  targetContent?: string,
): Promise<string> {
  const absSrc = path.join(ROOT, sourcePath);
  const absTarget = targetPath ? path.join(ROOT, targetPath) : null;

  fs.mkdirSync(path.dirname(absSrc), { recursive: true });
  fs.writeFileSync(absSrc, sourceContent, "utf8");
  probeFiles.push(absSrc);

  if (absTarget && targetContent) {
    fs.mkdirSync(path.dirname(absTarget), { recursive: true });
    fs.writeFileSync(absTarget, targetContent, "utf8");
    probeFiles.push(absTarget);
  }

  const results = await eslint.lintFiles([absSrc]);
  const messages = results.flatMap((r) => r.messages);

  if (messages.length === 0) return "PASS";
  return JSON.stringify(messages.map((m) => ({ ruleId: m.ruleId, message: m.message })));
}

const TARGET_CONTENT = "export const something = 1;\n";

describe("boundaries rules", () => {
  it("features/ cannot import from routes/", async () => {
    const id = uid();
    const result = await lintProbe(
      `src/features/online-board/__bnd_probe_${id}__.ts`,
      `import { something } from "../../routes/__probe_${id}__/__target_probe__";\nexport const x = something;\n`,
      `src/routes/__probe_${id}__/__target_probe__.ts`,
      TARGET_CONTENT,
    );
    expect(result).toContain("boundaries/element-types");
  });

  it("features/ cannot import from mf/", async () => {
    const id = uid();
    const result = await lintProbe(
      `src/features/online-board/__bnd_probe_${id}__.ts`,
      `import { something } from "../../mf/expose/__target_probe_${id}__";\nexport const x = something;\n`,
      `src/mf/expose/__target_probe_${id}__.ts`,
      TARGET_CONTENT,
    );
    expect(result).toContain("boundaries/element-types");
  });

  it("ui/ cannot import from features/", async () => {
    const id = uid();
    const result = await lintProbe(
      `src/ui/__probe_${id}__/__bnd_probe__.ts`,
      'import { something } from "../../features/online-board/index";\nexport const x = something;\n',
    );
    expect(result).toContain("boundaries/element-types");
  });

  it("shared/ cannot import from features/", async () => {
    const id = uid();
    const result = await lintProbe(
      `src/shared/__probe_${id}__/__bnd_probe__.ts`,
      'import { something } from "../../features/online-board/index";\nexport const x = something;\n',
    );
    expect(result).toContain("boundaries/element-types");
  });

  it("observability/ cannot import from features/", async () => {
    const id = uid();
    const result = await lintProbe(
      `src/observability/logger/__bnd_probe_${id}__.ts`,
      'import { something } from "../../features/online-board/index";\nexport const x = something;\n',
    );
    expect(result).toContain("boundaries/element-types");
  });

  it("features/ CAN import from env/ (allowed direction)", async () => {
    const id = uid();
    const result = await lintProbe(
      `src/features/online-board/__bnd_probe_${id}__.ts`,
      'import { getEnv } from "../../env/index";\nexport const x = getEnv;\n',
    );
    expect(result).not.toContain("boundaries/element-types");
  });
});
