import { RegExpParser, RegExpSyntaxError } from "@eslint-community/regexpp";
import type {
  RegexSyntaxRequest,
  RegexSyntaxResult,
  ReplacementSyntaxResult,
  SyntaxCoverage,
} from "../../../model/syntax";
import type { RegexFlavourId } from "../../../model/flavour";
import type {
  RegexSyntaxProvider,
  ReplacementSyntaxRequest,
} from "../../SyntaxProvider";
import { emptyRecoveredPattern, normalizePattern } from "./normalize";
import { parseEcmaScriptReplacement } from "./replacement";

const parser = new RegExpParser({ ecmaVersion: 2025 });
const coverage: SyntaxCoverage = {
  status: "partial",
  summary:
    "Complete parsing through the regexpp 4.12.2 ECMAScript 2025 grammar; application-owned normalized explanations are tested for the current feature matrix and remain partial for advanced constructs.",
  unsupportedConstructs: [
    "tolerant recovery for malformed patterns",
    "exact consumed-length bounds for Unicode properties of strings",
  ],
};

function flagsText(request: RegexSyntaxRequest): string {
  return [...new Set(request.flags)].join("");
}

export class EcmaScriptSyntaxProvider implements RegexSyntaxProvider {
  readonly id = "regexpp-community";
  readonly version = "4.12.2";
  readonly supportedFlavours = ["ecmascript"] as const;

  async parsePattern(request: RegexSyntaxRequest): Promise<RegexSyntaxResult> {
    if (request.flavour !== "ecmascript") {
      throw new Error(`Unsupported syntax flavour: ${request.flavour}`);
    }
    const flags = flagsText(request);
    try {
      const parsedFlags = parser.parseFlags(flags);
      const pattern = parser.parsePattern(
        request.pattern,
        0,
        request.pattern.length,
        {
          unicode: parsedFlags.unicode,
          unicodeSets: parsedFlags.unicodeSets,
        },
      );
      const normalized = normalizePattern(pattern, request.flags);
      return {
        accepted: true,
        ...normalized,
        diagnostics: [],
        provider: { id: this.id, version: this.version },
        coverage,
      };
    } catch (error) {
      const index =
        error instanceof RegExpSyntaxError
          ? Math.max(0, Math.min(request.pattern.length, error.index))
          : 0;
      const range = {
        startUtf16: index,
        endUtf16: index,
      };
      const message =
        error instanceof Error ? error.message : "Invalid ECMAScript pattern";
      const normalized = emptyRecoveredPattern(
        request.pattern,
        range,
        message,
        request.flags,
      );
      return {
        accepted: false,
        ...normalized,
        diagnostics: [
          {
            id: "ecmascript-syntax-error",
            source: "syntax-provider",
            severity: "error",
            code: "invalid-pattern",
            message,
            range,
            flavour: "ecmascript",
            provenance: "reported",
          },
        ],
        provider: { id: this.id, version: this.version },
        coverage,
      };
    }
  }

  async parseReplacement(
    request: ReplacementSyntaxRequest,
  ): Promise<ReplacementSyntaxResult> {
    if (request.flavour !== "ecmascript") {
      throw new Error(`Unsupported replacement flavour: ${request.flavour}`);
    }
    return parseEcmaScriptReplacement(
      request.replacement,
      request.captureMetadata,
    );
  }

  getCoverage(flavour: RegexFlavourId): SyntaxCoverage {
    return flavour === "ecmascript"
      ? coverage
      : {
          status: "unavailable",
          summary: `The community ECMAScript provider does not parse ${flavour}.`,
          unsupportedConstructs: ["all"],
        };
  }
}
