import { Emitter as T, languages as u } from "monaco-editor";
import "monaco-editor/esm/vs/editor/standalone/browser/standaloneServices.js";
import "monaco-editor/esm/vs/editor/standalone/common/standaloneTheme.js";
import "monaco-editor/esm/vs/platform/hover/browser/hover.js";
import "monaco-editor/esm/vs/editor/common/languages/supports/tokenization.js";
import { i as p } from "./monaco-importer.js";
import { Deferred as _ } from "@arcgis/toolkit/promise";
import { a as d } from "./sql-expr-defaults.js";
import { debounce as N } from "@arcgis/toolkit/function";
import { g as f } from "./sql-expr-service-accessors.js";
/*! All material copyright Esri, All Rights Reserved, unless otherwise specified.
See https://js.arcgis.com/5.0/esri/copyright.txt for details.
v5.0.0-next.87 */
let h = new _();
class k {
  constructor(e) {
    this._defaults = e, this._worker = null, this._client = null, this._configChangeListener = this._defaults.onDidChange(() => this.stopWorker());
  }
  dispose() {
    this._configChangeListener.dispose(), this.stopWorker();
  }
  stopWorker() {
    this._worker && (this._worker.dispose(), this._worker = null, h = new _()), this._client = null;
  }
  /**
   * Wait for the worker to be ready.
   * @returns A promise that resolves when the worker is ready.
   */
  static async waitForWorker() {
    return await h.promise;
  }
  async _getClientProxy() {
    const e = await p();
    if (!this._client) {
      const { languageId: t } = this._defaults;
      this._worker = e.createWebWorker({
        moduleId: "SqlExprWorker",
        label: t,
        createData: { languageId: t },
        host: this._defaults.workerHost
      }), h.resolve(this._worker), this._client = this._worker.getProxy();
    }
    return await this._client;
  }
  async getLanguageServiceWorker(...e) {
    const t = await this._getClientProxy();
    return await this._worker?.withSyncedResources(e), t;
  }
}
const S = [
  "AND",
  "AS",
  "BOTH",
  "CASE",
  "CAST",
  "CURRENT_DATE",
  "CURRENT_TIME",
  "CURRENT_TIMESTAMP",
  "DATE",
  "DAY",
  "DOY",
  "DOW",
  "ELSE",
  "END",
  "ESCAPE",
  "FALSE",
  "FLOAT",
  "FOR",
  "FROM",
  "HOUR",
  "IN",
  "INT",
  "INTEGER",
  "INTERVAL",
  "LEADING",
  "LIKE",
  "MINUTE",
  "MONTH",
  "POSITION",
  "REAL",
  "SECOND",
  "SMALLINT",
  "THEN",
  "TIME",
  "TIMESTAMP",
  "TIMEZONE_HOUR",
  "TIMEZONE_MINUTE",
  "TO",
  "TRAILING",
  "TRIM",
  "TRUE",
  "VARCHAR",
  "WEEK",
  "WHEN",
  "WITH",
  "YEAR",
  "ZONE"
], A = [
  // Logical
  "AND",
  "BETWEEN",
  "LIKE",
  "NOT",
  "OR",
  // Predicates
  "IS",
  "NULL"
], L = [
  // Conversion
  "CAST",
  // Datetime
  "EXTRACT",
  "CURRENT_DATE",
  "CURRENT_TIME",
  "CURRENT_TIMESTAMP",
  "CURRENT_USER",
  // Mathematical
  "ABS",
  "ACOS",
  "ASIN",
  "ATAN",
  "CEILING",
  "COS",
  "FLOOR",
  "LOG",
  "LOG10",
  "MOD",
  "NULLIF",
  "POWER",
  "ROUND",
  "SIGN",
  "SIN",
  "TAN",
  "TRUNCATE",
  // String
  "CHAR_LENGTH",
  "COALESCE",
  "CONCAT",
  "LOWER",
  "POSITION",
  "SUBSTRING",
  "TRIM",
  "UPPER"
], R = {
  comments: {
    lineComment: "--",
    blockComment: ["/*", "*/"]
  },
  brackets: [
    ["{", "}"],
    ["[", "]"],
    ["(", ")"]
  ],
  autoClosingPairs: [
    { open: "{", close: "}" },
    { open: "[", close: "]" },
    { open: "(", close: ")" },
    { open: '"', close: '"' },
    { open: "'", close: "'" }
  ],
  surroundingPairs: [
    { open: "{", close: "}" },
    { open: "[", close: "]" },
    { open: "(", close: ")" },
    { open: '"', close: '"' },
    { open: "'", close: "'" }
  ]
}, M = {
  defaultToken: "",
  tokenPostfix: ".arcgis",
  ignoreCase: !0,
  brackets: [
    { open: "[", close: "]", token: "delimiter.square" },
    { open: "(", close: ")", token: "delimiter.parenthesis" }
  ],
  keywords: S,
  operators: A,
  builtinFunctions: L,
  builtinVariables: [],
  tokenizer: {
    root: [
      { include: "@comments" },
      { include: "@whitespace" },
      { include: "@numbers" },
      { include: "@strings" },
      { include: "@complexIdentifiers" },
      { include: "@scopes" },
      [/[;,.]/, "delimiter"],
      [/[()]/, "@brackets"],
      [
        /[\w@#$]+/,
        {
          cases: {
            "@operators": "operator",
            "@builtinVariables": "predefined",
            "@builtinFunctions": "predefined",
            "@keywords": "keyword",
            "@default": "identifier"
          }
        }
      ],
      [/[<>=!%&+\-*/|~^]/, "operator"]
    ],
    whitespace: [[/\s+/, "white"]],
    comments: [
      [/--+.*/, "comment"],
      [/\/\*/, { token: "comment.quote", next: "@comment" }]
    ],
    comment: [
      [/[^*/]+/, "comment"],
      // Not supporting nested comments, as nested comments seem to not be standard?
      // i.e. http://stackoverflow.com/questions/728172/are-there-multiline-comment-delimiters-in-sql-that-are-vendor-agnostic
      // [/\/\*/, { token: 'comment.quote', next: '@push' }],    // nested comment not allowed :-(
      [/\*\//, { token: "comment.quote", next: "@pop" }],
      [/./, "comment"]
    ],
    numbers: [
      [/0[xX][0-9a-fA-F]*/, "number"],
      [/[$][+-]*\d*(\.\d*)?/, "number"],
      [/((\d+(\.\d*)?)|(\.\d+))([eE][\-+]?\d+)?/, "number"]
    ],
    strings: [
      [/N'/, { token: "string", next: "@string" }],
      [/'/, { token: "string", next: "@string" }]
    ],
    string: [
      [/[^']+/, "string"],
      [/''/, "string"],
      [/'/, { token: "string", next: "@pop" }]
    ],
    complexIdentifiers: [[/"/, { token: "identifier.quote", next: "@quotedIdentifier" }]],
    quotedIdentifier: [
      [/[^"]+/, "identifier"],
      [/""/, "identifier"],
      [/"/, { token: "identifier.quote", next: "@pop" }]
    ],
    scopes: [
      [/(BEGIN|CASE)\b/i, { token: "keyword.block" }],
      [/END\b/i, { token: "keyword.block" }],
      [/WHEN\b/i, { token: "keyword.choice" }],
      [/THEN\b/i, { token: "keyword.choice" }]
    ]
  }
};
class x {
  constructor(e, t) {
    this._worker = e, this._defaults = t;
  }
  async provideCompletionItems(e, t) {
    try {
      const i = await this._worker(e.uri), n = e.getWordUntilPosition(t), r = {
        startLineNumber: t.lineNumber,
        endLineNumber: t.lineNumber,
        startColumn: n.startColumn,
        endColumn: n.endColumn
      }, c = this._defaults.getApiContextForModel(e.uri);
      return await i.doComplete(e.uri.toString(), r, t, c);
    } catch (i) {
      return console.error(i), { suggestions: [] };
    }
  }
}
class b {
  constructor(e, t, {
    defaults: i,
    diagnosticService: n
  }) {
    this._languageId = e, this._worker = t, this._disposables = [], this._modelListeners = /* @__PURE__ */ new Map(), this._diagnosticsService = n, this._defaults = i, p().then((r) => {
      const c = (o) => {
        const s = o.getLanguageId();
        if (s !== this._languageId)
          return;
        const l = N(() => {
          this._doValidate(o, s).catch((I) => {
            throw I;
          });
        }), m = o.onDidChangeContent(l), w = o.onDidChangeAttached(l);
        this._modelListeners.set(o.uri.toString(), [m, w]), this._doValidate(o, s).catch(console.error);
      }, g = (o) => {
        const s = o.uri.toString();
        r.setModelMarkers(o, this._languageId, []);
        const l = this._modelListeners.get(s);
        if (l) {
          for (; l.length; )
            l.pop()?.dispose();
          this._modelListeners.delete(s);
        }
      };
      this._disposables.push(r.onDidCreateModel(c)), this._disposables.push(r.onWillDisposeModel((o) => g(o))), this._disposables.push(
        r.onDidChangeModelLanguage((o) => {
          g(o.model), c(o.model);
        })
      ), this._disposables.push(
        i.onDidChange(() => {
          r.getModels().forEach((o) => {
            o.getLanguageId() === this._languageId && (g(o), c(o));
          });
        })
      ), this._disposables.push(
        i.onModelContextDidChange((o) => {
          r.getModels().forEach((s) => {
            s.getLanguageId() === this._languageId && s.uri.toString() === o && this._doValidate(s, this._languageId).catch(console.error);
          });
        })
      ), this._disposables.push({
        dispose: () => {
          this._modelListeners.forEach((o) => o.forEach((s) => s.dispose())), this._modelListeners.clear();
        }
      }), r.getModels().forEach(c);
    });
  }
  async _doValidate(e, t) {
    const i = await p();
    if (e.isAttachedToEditor())
      try {
        const n = await this._worker(e.uri), r = this._defaults.getApiContextForModel(e.uri), c = await n.doValidation(e.uri.toString(), r);
        this._diagnosticsService.fireDiagnosticsChange(e.uri, c), i.setModelMarkers(e, t, c);
      } catch (n) {
        console.error(n);
      }
  }
}
let E;
async function V(...a) {
  return await k.waitForWorker(), await new Promise((e, t) => {
    if (!E) {
      t(new Error("sql expression worker not registered!"));
      return;
    }
    e(E(...a));
  });
}
class D {
  constructor() {
    this._onDiagnosticsChange = new T();
  }
  /**
   * An event to signal changes to the diagnostics.
   * The event value is the uri string and the diagnostics.
   */
  get onDiagnosticsChange() {
    return this._onDiagnosticsChange.event;
  }
  /**
   * Fires the diagnostics change event.
   * @param uri The uri of the model for which the diagnostics changed.
   * @param diagnostics The diagnostics for the model.
   */
  fireDiagnosticsChange(e, t) {
    this._onDiagnosticsChange.fire({ uri: e, diagnostics: t });
  }
}
const C = new D();
async function B() {
  return await f(), C;
}
function K(a) {
  const e = new k(a), t = async (...n) => await e.getLanguageServiceWorker(...n);
  E = t, u.setMonarchTokensProvider(d.languageId, M), u.setLanguageConfiguration(d.languageId, R);
  const i = new x(t, d);
  u.registerCompletionItemProvider(d.languageId, i), new b(a.languageId, t, {
    defaults: a,
    diagnosticService: C
  });
}
export {
  B as getSqlExprDiagnosticService,
  V as getSqlExprWorker,
  K as setupMode
};
