import { describe, expect, it } from "vitest";
import {
  addTrackerError,
  buildServiceTrackers,
  finalizeServiceResult,
  recordGraphError,
  recordTaskOutcome,
  summarizeDbBackend,
} from "./results.mjs";

describe("runner results", () => {
  it("tracks task outcomes and graph errors", () => {
    const trackers = buildServiceTrackers(
      [
        {
          skipped: false,
          config: {
            name: "api",
            testkit: {
              database: {
                selectedBackend: "local",
              },
            },
          },
          suites: [
            {
              name: "health",
              type: "integration",
              framework: "k6",
              files: ["tests/health.js"],
              orderIndex: 0,
            },
          ],
        },
      ],
      1000
    );

    recordTaskOutcome(
      trackers,
      {
        serviceName: "api",
        suiteKey: "integration:health",
        file: "tests/health.js",
      },
      {
        failed: true,
        durationMs: 250,
        error: "boom",
      },
      1250
    );

    const tracker = trackers.get("api");
    addTrackerError(tracker, "worker failed");
    addTrackerError(tracker, "worker failed");
    recordGraphError(trackers, { targetNames: ["api"] }, "graph failed", 1300);

    const result = finalizeServiceResult(tracker, 1000, 1500);
    expect(result.failed).toBe(true);
    expect(result.failedSuiteCount).toBe(1);
    expect(result.totalFileCount).toBe(1);
    expect(result.failedFileCount).toBe(1);
    expect(result.passedFileCount).toBe(0);
    expect(result.errors).toEqual(["worker failed", "graph failed"]);
    expect(result.suites[0].framework).toBe("default");
    expect(result.suites[0].failedFileCount).toBe(1);
    expect(result.suites[0].files).toEqual([
      {
        path: "tests/health.js",
        failed: true,
        status: "failed",
        durationMs: 250,
        error: "boom",
        reason: null,
      },
    ]);
  });

  it("reports service duration as wall-clock time, not accumulated file time", () => {
    const trackers = buildServiceTrackers(
      [
        {
          skipped: false,
          config: {
            name: "frontend",
            testkit: {
              database: {
                selectedBackend: null,
              },
            },
          },
          suites: [
            {
              name: "browser",
              type: "e2e",
              framework: "playwright",
              files: ["tests/a.pw.testkit.ts", "tests/b.pw.testkit.ts"],
              orderIndex: 0,
            },
          ],
        },
      ],
      1000
    );

    recordTaskOutcome(
      trackers,
      {
        serviceName: "frontend",
        suiteKey: "e2e:browser",
        file: "tests/a.pw.testkit.ts",
      },
      {
        failed: false,
        durationMs: 30_000,
        startedAt: 1000,
        finishedAt: 8000,
        error: null,
      },
      8000
    );
    recordTaskOutcome(
      trackers,
      {
        serviceName: "frontend",
        suiteKey: "e2e:browser",
        file: "tests/b.pw.testkit.ts",
      },
      {
        failed: false,
        durationMs: 45_000,
        startedAt: 3000,
        finishedAt: 9000,
        error: null,
      },
      9000
    );

    const result = finalizeServiceResult(trackers.get("frontend"), 1000, 10_000);

    expect(result.durationMs).toBe(8000);
    expect(result.totalTaskDurationMs).toBe(75_000);
    expect(result.suites[0].durationMs).toBe(75_000);
  });

  it("handles epoch-zero task timestamps when calculating wall-clock duration", () => {
    const trackers = buildServiceTrackers(
      [
        {
          skipped: false,
          config: {
            name: "frontend",
            testkit: {
              database: {
                selectedBackend: null,
              },
            },
          },
          suites: [
            {
              name: "browser",
              type: "e2e",
              framework: "playwright",
              files: ["tests/a.pw.testkit.ts"],
              orderIndex: 0,
            },
          ],
        },
      ],
      0
    );

    recordTaskOutcome(
      trackers,
      {
        serviceName: "frontend",
        suiteKey: "e2e:browser",
        file: "tests/a.pw.testkit.ts",
      },
      {
        failed: false,
        durationMs: 3_000,
        startedAt: 0,
        finishedAt: 8_000,
        error: null,
      },
      8_000
    );

    const result = finalizeServiceResult(trackers.get("frontend"), 0, 10_000);

    expect(result.durationMs).toBe(8_000);
    expect(result.totalTaskDurationMs).toBe(3_000);
  });

  it("counts config-skipped files and runtime-skipped files separately from passed work", () => {
    const trackers = buildServiceTrackers(
      [
        {
          skipped: false,
          config: {
            name: "api",
            testkit: {
              database: {
                selectedBackend: null,
              },
            },
          },
          suites: [
            {
              name: "billing",
              type: "integration",
              framework: "k6",
              files: ["tests/billing-live.int.testkit.ts"],
              skippedFiles: [
                {
                  path: "tests/billing-stubbed.int.testkit.ts",
                  reason: "Billing is stubbed",
                },
              ],
              totalFileCount: 2,
              orderIndex: 0,
            },
            {
              name: "frontend-smoke",
              type: "e2e",
              framework: "playwright",
              files: ["tests/frontend-smoke.pw.testkit.ts"],
              orderIndex: 1,
            },
          ],
        },
      ],
      1000
    );

    recordTaskOutcome(
      trackers,
      {
        serviceName: "api",
        suiteKey: "integration:billing",
        file: "tests/billing-live.int.testkit.ts",
      },
      {
        failed: false,
        durationMs: 100,
        error: null,
      },
      1100
    );
    recordTaskOutcome(
      trackers,
      {
        serviceName: "api",
        suiteKey: "e2e:frontend-smoke",
        file: "tests/frontend-smoke.pw.testkit.ts",
      },
      {
        failed: false,
        status: "skipped",
        reason: "Playwright test.skip()",
        durationMs: 50,
        error: null,
      },
      1150
    );

    const result = finalizeServiceResult(trackers.get("api"), 1000, 1200);

    expect(result.failed).toBe(false);
    expect(result.completedSuiteCount).toBe(2);
    expect(result.skippedSuiteCount).toBe(1);
    expect(result.totalFileCount).toBe(3);
    expect(result.completedFileCount).toBe(1);
    expect(result.passedFileCount).toBe(1);
    expect(result.failedFileCount).toBe(0);
    expect(result.skippedFileCount).toBe(2);
    expect(result.notRunFileCount).toBe(0);
    expect(result.suites[0].files).toEqual([
      {
        path: "tests/billing-live.int.testkit.ts",
        failed: false,
        status: "passed",
        durationMs: 100,
        error: null,
        reason: null,
      },
      {
        path: "tests/billing-stubbed.int.testkit.ts",
        failed: false,
        status: "skipped",
        durationMs: 0,
        error: null,
        reason: "Billing is stubbed",
      },
    ]);
    expect(result.suites[1].files).toEqual([
      {
        path: "tests/frontend-smoke.pw.testkit.ts",
        failed: false,
        status: "skipped",
        durationMs: 50,
        error: null,
        reason: "Playwright test.skip()",
      },
    ]);
  });

  it("summarizes mixed db backends", () => {
    expect(
      summarizeDbBackend([{ dbBackend: "local" }, { dbBackend: "neon" }])
    ).toBe("mixed");
  });
});
