import {
  WorkerSupervisor,
  type WorkerFactory,
} from "../execution/WorkerSupervisor";
import type {
  AnalysisEngineIdentity,
  AnalysisWorkerOperation,
  AnalysisWorkerResult,
  BenchmarkSubject,
  BenchmarkWorkerSample,
  GrowthProbeSubject,
  GrowthWorkerSample,
} from "./analysis.types";

export interface AnalysisWorkerClient {
  identity(timeoutMs: number): Promise<AnalysisEngineIdentity>;
  benchmarkSample(
    request: BenchmarkSubject,
    timeoutMs: number,
  ): Promise<BenchmarkWorkerSample>;
  growthProbe(
    request: GrowthProbeSubject,
    timeoutMs: number,
  ): Promise<GrowthWorkerSample>;
  cancel(): void;
  dispose(): void;
}

const createAnalysisWorker: WorkerFactory = () =>
  new Worker(new URL("../../workers/analysis.worker.ts", import.meta.url), {
    type: "module",
    name: "regex-tools-ecmascript-analysis",
  });

export class AnalysisSupervisor implements AnalysisWorkerClient {
  readonly #supervisor: WorkerSupervisor<
    AnalysisWorkerOperation,
    AnalysisWorkerResult
  >;

  constructor(workerFactory: WorkerFactory = createAnalysisWorker) {
    this.#supervisor = new WorkerSupervisor(
      "ECMAScript analysis worker",
      workerFactory,
    );
  }

  async identity(timeoutMs: number): Promise<AnalysisEngineIdentity> {
    const response = await this.#supervisor.run(
      { kind: "identity" },
      timeoutMs,
    );
    if (response.kind !== "identity") {
      throw new Error("Analysis worker returned the wrong identity response.");
    }
    return response.result;
  }

  async benchmarkSample(
    request: BenchmarkSubject,
    timeoutMs: number,
  ): Promise<BenchmarkWorkerSample> {
    const response = await this.#supervisor.run(
      { kind: "benchmark-sample", request },
      timeoutMs,
    );
    if (response.kind !== "benchmark-sample") {
      throw new Error("Analysis worker returned the wrong benchmark response.");
    }
    return response.result;
  }

  async growthProbe(
    request: GrowthProbeSubject,
    timeoutMs: number,
  ): Promise<GrowthWorkerSample> {
    const response = await this.#supervisor.run(
      { kind: "growth-probe", request },
      timeoutMs,
    );
    if (response.kind !== "growth-probe") {
      throw new Error("Analysis worker returned the wrong growth response.");
    }
    return response.result;
  }

  cancel(): void {
    this.#supervisor.cancel();
  }

  dispose(): void {
    this.#supervisor.dispose();
  }
}
