import { describe, expect, it } from "vitest";
import {
  applyShard,
  buildGraphDirName,
  buildRuntimeGraphs,
  buildTaskQueue,
  claimNextBatch,
  collectSuites,
  resolveRuntimeConfigs,
} from "./planning.mjs";

function makeConfig(name, extras = {}) {
  return {
    name,
    suites: extras.suites || {},
    testkit: {
      dependsOn: extras.dependsOn || [],
    },
    ...extras,
  };
}

describe("runner-planning", () => {
  it("orders runtime configs by dependency and detects cycles", () => {
    const api = makeConfig("api");
    const frontend = makeConfig("frontend", { dependsOn: ["api"] });
    const configMap = new Map([
      ["api", api],
      ["frontend", frontend],
    ]);

    expect(resolveRuntimeConfigs(frontend, configMap).map((config) => config.name)).toEqual([
      "api",
      "frontend",
    ]);

    api.testkit.dependsOn = ["frontend"];
    expect(() => resolveRuntimeConfigs(frontend, configMap)).toThrow("Dependency cycle");
  });

  it("collects suites with aliases, weights, and framework filters", () => {
    const config = makeConfig("api", {
      suites: {
        integration: [
          { name: "health", files: ["health.js"] },
        ],
        e2e: [
          { name: "auth", framework: "playwright", files: ["auth.spec.js", "signup.spec.js"] },
        ],
      },
    });

    expect(collectSuites(config, "int", [], "all")[0]).toMatchObject({
      name: "health",
      type: "integration",
      framework: "k6",
      weight: 1,
    });

    expect(collectSuites(config, "all", [], "playwright")[0]).toMatchObject({
      name: "auth",
      framework: "playwright",
      weight: 2,
    });
  });

  it("applies shards, builds graphs, queues tasks, and claims batches", () => {
    const api = makeConfig("api");
    const frontend = makeConfig("frontend");
    const plans = [
      {
        config: api,
        skipped: false,
        runtimeConfigs: [api],
        runtimeNames: ["api"],
        runtimeKey: "api",
        suites: [
          {
            name: "health",
            type: "integration",
            framework: "k6",
            files: ["a.js", "b.js"],
            orderIndex: 0,
            weight: 2,
            maxFileConcurrency: 2,
          },
        ],
      },
      {
        config: frontend,
        skipped: false,
        runtimeConfigs: [api, frontend],
        runtimeNames: ["api", "frontend"],
        runtimeKey: "api|frontend",
        suites: [
          {
            name: "auth",
            type: "e2e",
            framework: "playwright",
            files: ["auth.spec.js", "signup.spec.js"],
            orderIndex: 0,
            weight: 2,
            maxFileConcurrency: 1,
          },
        ],
      },
    ];

    expect(applyShard(["a", "b", "c", "d"], { index: 2, total: 2 })).toEqual(["b", "d"]);

    const graphs = buildRuntimeGraphs(plans);
    expect(graphs).toHaveLength(1);
    expect(plans[0].assignedGraphKey).toBe("api|frontend");
    expect(buildGraphDirName(["api", "frontend"])).toBe("api__frontend");

    const queue = buildTaskQueue(plans, graphs, { files: {} });
    expect(queue).toHaveLength(4);

    const firstBatch = claimNextBatch(queue, "api|frontend");
    expect(firstBatch.tasks).toHaveLength(2);
    expect(firstBatch.framework).toBe("playwright");

    const secondBatch = claimNextBatch(queue, "api|frontend");
    expect(secondBatch.tasks).toHaveLength(2);
    expect(secondBatch.framework).toBe("k6");
  });
});
