import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { runVerify, verifyResult } from "./verify";

describe("verifyResult", () => {
  it("passes when every outcome passes", () => {
    expect(
      verifyResult([
        { name: "a", ok: true },
        { name: "b", ok: true },
      ]).exitCode,
    ).toBe(0);
  });

  it("fails when any outcome fails", () => {
    expect(
      verifyResult([
        { name: "a", ok: true },
        { name: "b", ok: false },
      ]).exitCode,
    ).toBe(1);
  });

  it("passes for an empty set", () => {
    expect(verifyResult([]).exitCode).toBe(0);
  });
});

describe("runVerify", () => {
  let tmp: string;

  beforeEach(() => {
    tmp = fs.mkdtempSync(path.join(os.tmpdir(), "verify-"));
  });

  afterEach(() => {
    fs.rmSync(tmp, { recursive: true, force: true });
  });

  it("returns an exit code when no modules or apps exist", async () => {
    fs.writeFileSync(
      path.join(tmp, "package.json"),
      JSON.stringify({ name: "x", version: "0.0.0" }),
    );

    const result = await runVerify({
      modulesRoot: "modules",
      appsRoot: "apps",
      cwd: tmp,
      installedVersion: "0.0.0",
    });

    expect(typeof result.exitCode).toBe("number");
  });

  it("fails when a component breaks the structure naming rule", async () => {
    // Component files must be PascalCase; "bad-name" breaks that rule.
    // Both required app dirs exist, so naming is the only possible failure.
    const appRoot = path.join(tmp, "apps", "my-app");
    const componentsDir = path.join(appRoot, "frontend", "src", "components");
    fs.mkdirSync(componentsDir, { recursive: true });
    fs.mkdirSync(path.join(appRoot, "backend"), { recursive: true });
    fs.writeFileSync(path.join(componentsDir, "bad-name.tsx"), "export const X = () => null;\n");

    const result = await runVerify({
      modulesRoot: "modules",
      appsRoot: "apps",
      cwd: tmp,
      installedVersion: "0.0.0",
    });

    expect(result.exitCode).toBe(1);
  });

  it("fails when an app component has no co-located test", async () => {
    // PascalCase name keeps the structure naming rule happy, so the only
    // possible failure is the missing co-located component test.
    const appRoot = path.join(tmp, "apps", "my-app");
    const componentsDir = path.join(appRoot, "frontend", "src", "components");
    fs.mkdirSync(componentsDir, { recursive: true });
    fs.mkdirSync(path.join(appRoot, "backend"), { recursive: true });
    fs.writeFileSync(path.join(componentsDir, "Card.tsx"), "export const Card = () => null;\n");

    const result = await runVerify({
      modulesRoot: "modules",
      appsRoot: "apps",
      cwd: tmp,
      installedVersion: "0.0.0",
    });

    expect(result.exitCode).toBe(1);
  });
});
