import { describe, expect, it } from "vitest";
import {
  WORKER_PROTOCOL_VERSION,
  type WorkerRequest,
} from "../execution/worker-protocol";
import type { WorkerLike } from "../execution/WorkerSupervisor";
import type { NormalizedRegexNode } from "../model/syntax";
import { DEFAULT_GENERATION_SETTINGS } from "./generation-limits";
import type {
  GenerationWorkerOperation,
  GenerationWorkerResult,
} from "./generation.types";
import { GenerationSupervisor } from "./GenerationSupervisor";

function root(): NormalizedRegexNode {
  return {
    id: "root",
    kind: "pattern",
    range: { startUtf16: 0, endUtf16: 1 },
    raw: "a",
    explanation: "pattern",
    children: [],
    properties: {
      zeroWidth: false,
      nullable: false,
      minimumLength: 1,
      maximumLength: 1,
      consumesInput: true,
    },
    support: { flavour: "ecmascript", status: "supported", notes: [] },
    provenance: {
      provider: "fixture",
      providerVersion: "1",
      source: "parsed",
    },
  };
}

class ImmediateWorker implements WorkerLike {
  onmessage: ((event: MessageEvent<unknown>) => void) | null = null;
  onerror: ((event: ErrorEvent) => void) | null = null;
  onmessageerror: ((event: MessageEvent<unknown>) => void) | null = null;
  terminated = false;

  postMessage(value: unknown): void {
    const request = value as WorkerRequest<GenerationWorkerOperation>;
    const payload: GenerationWorkerResult = {
      kind: "generate",
      result: {
        generator: {
          id: "regex-tools-ast-cases",
          version: "1",
        },
        flavour: "ecmascript",
        seed: request.payload.request.seed,
        seedHash: "12345678",
        candidates: [],
        coverage: [],
        unsupportedConstructs: [],
        visitedAstNodes: 1,
        candidateBytes: 0,
        candidateAttempts: 0,
        duplicateCandidates: 0,
        candidatesTruncated: false,
        traversalTruncated: false,
        warnings: [],
      },
    };
    queueMicrotask(() =>
      this.onmessage?.(
        new MessageEvent("message", {
          data: {
            protocolVersion: WORKER_PROTOCOL_VERSION,
            requestId: request.requestId,
            generation: request.generation,
            ok: true,
            payload,
          },
        }),
      ),
    );
  }

  terminate(): void {
    this.terminated = true;
  }
}

describe("GenerationSupervisor", () => {
  it("routes the bounded request and returns the matching result", async () => {
    const worker = new ImmediateWorker();
    const supervisor = new GenerationSupervisor(() => worker);
    await expect(
      supervisor.generate(
        {
          flavour: "ecmascript",
          root: root(),
          flags: ["u"],
          seed: "supervisor-seed",
          settings: DEFAULT_GENERATION_SETTINGS,
        },
        100,
      ),
    ).resolves.toEqual(expect.objectContaining({ seed: "supervisor-seed" }));
    supervisor.dispose();
    expect(worker.terminated).toBe(true);
  });
});
