import {
  PORTABLE_BRIDGE_ABI_VERSION,
  PortableEngineAdapter,
  type PortableEngineConfiguration,
  type PortableRegexBridge,
} from "../portable/PortableEngineAdapter";
import type {
  RegexExecutionRequest,
  RegexExecutionResult,
  RegexReplacementRequest,
  RegexReplacementResult,
} from "../../../model/match";

export const PERL_FLAVOUR_VERSION = "Perl 5.28.1 via WebPerl 0.09-beta";
export const PERL_ENGINE_VERSION = "5.28.1";
export const WEBPERL_VERSION = "0.09-beta";

function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

function configuration(runtimeIdentity?: string): PortableEngineConfiguration {
  return {
    flavour: "perl",
    flavourLabel: "Perl (legacy WebPerl)",
    expectedFlavourVersion: PERL_FLAVOUR_VERSION,
    engineName: "Perl regular expressions (legacy)",
    engineVersion: PERL_ENGINE_VERSION,
    runtimeVersion: [
      `WebPerl ${WEBPERL_VERSION} · legacy beta`,
      runtimeIdentity,
    ]
      .filter(Boolean)
      .join(" · "),
    offsetUnit: "code-point",
    supportedFlags: "gimsxnadlu",
    rejectLoneSurrogates: true,
    capabilities: {
      compilation: true,
      matching: true,
      replacement: true,
      namedCaptures: true,
      captureHistory: false,
      actualTrace: false,
      benchmark: false,
    },
    validateIdentity: (envelope) => {
      const identity = envelope.identity;
      if (
        envelope.abi !== PORTABLE_BRIDGE_ABI_VERSION ||
        envelope.ok !== true ||
        !isRecord(identity) ||
        identity.bridge !== "regex-tools-webperl-json" ||
        identity.bridgeVersion !== PORTABLE_BRIDGE_ABI_VERSION ||
        identity.engine !== "Perl regular expressions" ||
        identity.engineVersion !== PERL_ENGINE_VERSION ||
        identity.perlVersion !== "v5.28.1" ||
        identity.webPerlVersion !== WEBPERL_VERSION ||
        identity.offsetUnit !== "code-point" ||
        identity.replacementGrammar !== "literal + $$ + $n + ${name}" ||
        identity.replacementTransport !== "fixed MEMFS binary output file" ||
        identity.replacementParityOracle !== "fixed native s/// fixtures" ||
        identity.legacy !== true ||
        identity.beta !== true ||
        identity.selfTest !== true
      ) {
        throw new Error(
          "The bundled legacy WebPerl runtime failed its identity self-test.",
        );
      }
    },
  };
}

function annotateCaptureNames(
  result: RegexExecutionResult,
  request: RegexExecutionRequest,
): RegexExecutionResult {
  const names = new Map(
    request.captureMetadata.flatMap((capture) =>
      capture.name ? [[capture.number, capture.name] as const] : [],
    ),
  );
  if (names.size === 0 || result.matches.length === 0) return result;
  return {
    ...result,
    matches: result.matches.map((match) => ({
      ...match,
      captures: match.captures.map((capture) => {
        const groupName = capture.groupName ?? names.get(capture.groupNumber);
        return groupName ? { ...capture, groupName } : capture;
      }),
    })),
  };
}

export class PerlEngineAdapter extends PortableEngineAdapter {
  constructor(bridge: PortableRegexBridge, runtimeIdentity?: string) {
    super(bridge, configuration(runtimeIdentity));
  }

  override async execute(
    request: RegexExecutionRequest,
  ): Promise<RegexExecutionResult> {
    return annotateCaptureNames(await super.execute(request), request);
  }

  override async replace(
    request: RegexReplacementRequest,
  ): Promise<RegexReplacementResult> {
    const result = await super.replace(request);
    return {
      ...result,
      execution: annotateCaptureNames(result.execution, request),
    };
  }
}
