import { findNodeForProperty as v, findNodeForKeyword as y } from "../utils/grammar-utils.js";
import { streamAst as m } from "../utils/ast-utils.js";
import { tokenToRange as x } from "../utils/cst-utils.js";
import { interruptAndCheck as d, isOperationCancelled as w } from "../utils/promise-utils.js";
import { diagnosticData as u } from "./validation-registry.js";
import { c as l } from "../../../../../../../_virtual/cancellation.js";
class R {
  constructor(e) {
    this.validationRegistry = e.validation.ValidationRegistry, this.metadata = e.LanguageMetaData;
  }
  async validateDocument(e, i = {}, s = l.CancellationToken.None) {
    const n = e.parseResult, r = [];
    if (await d(s), (!i.categories || i.categories.includes("built-in")) && (this.processLexingErrors(n, r, i), i.stopAfterLexingErrors && r.some((t) => {
      var a;
      return ((a = t.data) === null || a === void 0 ? void 0 : a.code) === c.LexingError;
    }) || (this.processParsingErrors(n, r, i), i.stopAfterParsingErrors && r.some((t) => {
      var a;
      return ((a = t.data) === null || a === void 0 ? void 0 : a.code) === c.ParsingError;
    })) || (this.processLinkingErrors(e, r, i), i.stopAfterLinkingErrors && r.some((t) => {
      var a;
      return ((a = t.data) === null || a === void 0 ? void 0 : a.code) === c.LinkingError;
    }))))
      return r;
    try {
      r.push(...await this.validateAst(n.value, i, s));
    } catch (t) {
      if (w(t))
        throw t;
      console.error("An error occurred during validation:", t);
    }
    return await d(s), r;
  }
  processLexingErrors(e, i, s) {
    var n, r, t;
    const a = [...e.lexerErrors, ...(r = (n = e.lexerReport) === null || n === void 0 ? void 0 : n.diagnostics) !== null && r !== void 0 ? r : []];
    for (const g of a) {
      const h = (t = g.severity) !== null && t !== void 0 ? t : "error", p = {
        severity: f(h),
        range: {
          start: {
            line: g.line - 1,
            character: g.column - 1
          },
          end: {
            line: g.line - 1,
            character: g.column + g.length - 1
          }
        },
        message: g.message,
        data: k(h),
        source: this.getSource()
      };
      i.push(p);
    }
  }
  processParsingErrors(e, i, s) {
    for (const n of e.parserErrors) {
      let r;
      if (isNaN(n.token.startOffset)) {
        if ("previousToken" in n) {
          const t = n.previousToken;
          if (isNaN(t.startOffset)) {
            const a = { line: 0, character: 0 };
            r = { start: a, end: a };
          } else {
            const a = { line: t.endLine - 1, character: t.endColumn };
            r = { start: a, end: a };
          }
        }
      } else
        r = x(n.token);
      if (r) {
        const t = {
          severity: f("error"),
          range: r,
          message: n.message,
          data: u(c.ParsingError),
          source: this.getSource()
        };
        i.push(t);
      }
    }
  }
  processLinkingErrors(e, i, s) {
    for (const n of e.references) {
      const r = n.error;
      if (r) {
        const t = {
          node: r.container,
          property: r.property,
          index: r.index,
          data: {
            code: c.LinkingError,
            containerType: r.container.$type,
            property: r.property,
            refText: r.reference.$refText
          }
        };
        i.push(this.toDiagnostic("error", r.message, t));
      }
    }
  }
  async validateAst(e, i, s = l.CancellationToken.None) {
    const n = [], r = (t, a, g) => {
      n.push(this.toDiagnostic(t, a, g));
    };
    return await this.validateAstBefore(e, i, r, s), await this.validateAstNodes(e, i, r, s), await this.validateAstAfter(e, i, r, s), n;
  }
  async validateAstBefore(e, i, s, n = l.CancellationToken.None) {
    var r;
    const t = this.validationRegistry.checksBefore;
    for (const a of t)
      await d(n), await a(e, s, (r = i.categories) !== null && r !== void 0 ? r : [], n);
  }
  async validateAstNodes(e, i, s, n = l.CancellationToken.None) {
    await Promise.all(m(e).map(async (r) => {
      await d(n);
      const t = this.validationRegistry.getChecks(r.$type, i.categories);
      for (const a of t)
        await a(r, s, n);
    }));
  }
  async validateAstAfter(e, i, s, n = l.CancellationToken.None) {
    var r;
    const t = this.validationRegistry.checksAfter;
    for (const a of t)
      await d(n), await a(e, s, (r = i.categories) !== null && r !== void 0 ? r : [], n);
  }
  toDiagnostic(e, i, s) {
    return {
      message: i,
      range: E(s),
      severity: f(e),
      code: s.code,
      codeDescription: s.codeDescription,
      tags: s.tags,
      relatedInformation: s.relatedInformation,
      data: s.data,
      source: this.getSource()
    };
  }
  getSource() {
    return this.metadata.languageId;
  }
}
function E(o) {
  if (o.range)
    return o.range;
  let e;
  return typeof o.property == "string" ? e = v(o.node.$cstNode, o.property, o.index) : typeof o.keyword == "string" && (e = y(o.node.$cstNode, o.keyword, o.index)), e ?? (e = o.node.$cstNode), e ? e.range : {
    start: { line: 0, character: 0 },
    end: { line: 0, character: 0 }
  };
}
function f(o) {
  switch (o) {
    case "error":
      return 1;
    case "warning":
      return 2;
    case "info":
      return 3;
    case "hint":
      return 4;
    default:
      throw new Error("Invalid diagnostic severity: " + o);
  }
}
function k(o) {
  switch (o) {
    case "error":
      return u(c.LexingError);
    case "warning":
      return u(c.LexingWarning);
    case "info":
      return u(c.LexingInfo);
    case "hint":
      return u(c.LexingHint);
    default:
      throw new Error("Invalid diagnostic severity: " + o);
  }
}
var c;
(function(o) {
  o.LexingError = "lexing-error", o.LexingWarning = "lexing-warning", o.LexingInfo = "lexing-info", o.LexingHint = "lexing-hint", o.ParsingError = "parsing-error", o.LinkingError = "linking-error";
})(c || (c = {}));
export {
  R as DefaultDocumentValidator,
  c as DocumentValidator,
  E as getDiagnosticRange,
  k as toDiagnosticData,
  f as toDiagnosticSeverity
};
