import type { RegexEngineAdapter } from "../../RegexEngineAdapter";
import type { RegexDiagnostic } from "../../../model/diagnostics";
import type { RegexEngineInfo } from "../../../model/flavour";
import type {
  RegexExecutionRequest,
  RegexExecutionResult,
  RegexReplacementRequest,
  RegexReplacementResult,
} from "../../../model/match";
import {
  JavaEngineAdapter,
  JAVA_ENGINE_VERSION,
  JAVA_FLAVOUR_VERSION,
} from "../java/JavaEngineAdapter";
import type { TeaVmJavaRegexModule } from "../java/java-module";

export const SCALA_FLAVOUR_VERSION =
  "Scala/JVM regex syntax via TeaVM java.util.regex 0.15.0";

function scalaDiagnostic(diagnostic: RegexDiagnostic): RegexDiagnostic {
  return {
    ...diagnostic,
    id: diagnostic.id.startsWith("java-")
      ? `scala-${diagnostic.id.slice("java-".length)}`
      : `scala-${diagnostic.id}`,
    message: diagnostic.message
      .replaceAll("TeaVM Java", "Scala/JVM compatibility")
      .replaceAll("Java ", "Scala/JVM "),
    flavour: "scala",
  };
}

function scalaEngineInfo(java: RegexEngineInfo): RegexEngineInfo {
  return {
    ...java,
    flavour: "scala",
    engineName: "Scala/JVM regex via TeaVM java.util.regex",
    engineVersion: `TeaVM ${JAVA_ENGINE_VERSION} compatibility profile`,
    runtimeVersion: [
      "Scala/JVM pattern and replacement semantics",
      "no Scala runtime",
      java.runtimeVersion,
    ]
      .filter(Boolean)
      .join(" · "),
  };
}

function validateScalaRequest(request: RegexExecutionRequest): void {
  if (request.flavour !== "scala") {
    throw new Error(`Scala adapter cannot execute ${request.flavour}.`);
  }
  if (
    request.flavourVersion !== undefined &&
    request.flavourVersion !== SCALA_FLAVOUR_VERSION
  ) {
    throw new RangeError(
      `Scala flavour version ${JSON.stringify(request.flavourVersion)} is unsupported by this compatibility profile.`,
    );
  }
}

export class ScalaEngineAdapter implements RegexEngineAdapter {
  readonly flavour = "scala" as const;
  readonly #java: JavaEngineAdapter;
  #info: RegexEngineInfo | undefined;

  constructor(module: TeaVmJavaRegexModule, runtimeVersion?: string) {
    this.#java = new JavaEngineAdapter(module, runtimeVersion);
  }

  async load(): Promise<RegexEngineInfo> {
    this.#info ??= scalaEngineInfo(await this.#java.load());
    return this.#info;
  }

  async execute(request: RegexExecutionRequest): Promise<RegexExecutionResult> {
    validateScalaRequest(request);
    const info = await this.load();
    const result = await this.#java.execute({
      ...request,
      flavour: "java",
      flavourVersion: JAVA_FLAVOUR_VERSION,
    });
    return {
      ...result,
      engine: info,
      diagnostics: result.diagnostics.map(scalaDiagnostic),
    };
  }

  async replace(
    request: RegexReplacementRequest,
  ): Promise<RegexReplacementResult> {
    validateScalaRequest(request);
    const info = await this.load();
    const result = await this.#java.replace({
      ...request,
      flavour: "java",
      flavourVersion: JAVA_FLAVOUR_VERSION,
    });
    return {
      ...result,
      execution: {
        ...result.execution,
        engine: info,
        diagnostics: result.execution.diagnostics.map(scalaDiagnostic),
      },
    };
  }

  terminate(): void {
    this.#java.terminate();
  }
}
