/* COPYRIGHT Esri - https://js.arcgis.com/5.1/LICENSE.txt */
import { g as v, f as F, m as M, L as S, E as L } from "./language-defaults-base.js";
import { i as h, a as A, b as k, c as E, d as I, e as C, f as P } from "./utils.js";
import { supportedLocales as j, fetchT9nStringsBundle as $ } from "@arcgis/toolkit/intl";
import { g as T } from "./runtime.js";
import { CompletionItemKind as w, InsertTextFormat as u, InsertTextMode as f } from "vscode-languageserver-types";
import { getDeclaredPixelMembers as B } from "@arcgis/core/applications/Components/arcadeEditorUtils.js";
import "@arcgis/toolkit/type";
async function K(e) {
  const { apiVersion: i, bundles: t, hiddenApiItems: o } = e.toEditorProfileDefinition(), s = await b(e.dictionaryVariables.filter(P));
  return { apiVersion: i, bundles: t, variables: s, hiddenApiItems: o?.map((a) => a.toLowerCase()) };
}
async function b(e, i = w.Variable) {
  return await Promise.all(
    e.map(async (t) => {
      switch (t.type) {
        case "dictionary":
          return await O(t, i);
        case "pixel":
          return await z(t, i);
        // Voxel is equivalent to features in regards to the languageservice.
        // They both have equivalent fields, but some voxel fields are different types (arrays)
        // than what is reported in .fields, so in the sidebar we treat a bit differently.
        // In autocomplete we don't differentiate types, so we can use the same logic as features.
        case "voxel":
        case "feature":
          return await q(t, i);
        default:
          return _(t, i);
      }
    })
  );
}
function _(e, i) {
  const { name: t, type: o } = e, s = e.getDescription(), a = i === w.Field ? v(t, !1) : t;
  return {
    name: t,
    description: s,
    type: o,
    completion: {
      label: t,
      detail: t,
      insertText: a,
      insertTextMode: f.asIs,
      insertTextFormat: u.PlainText,
      kind: i,
      documentation: { kind: "markdown", value: s }
    }
  };
}
async function q(e, i) {
  const t = await e.loadSource(), { name: o } = e, s = e.getDescription(), a = {
    label: o,
    detail: o,
    insertText: o,
    insertTextMode: f.asIs,
    insertTextFormat: u.PlainText,
    kind: i
  }, n = {
    name: o,
    description: s,
    type: "dictionary",
    properties: [],
    completion: a
  };
  if (!t)
    return n;
  const [r, c] = F(t.fields, s, !0);
  return n.properties = r, a.documentation = { kind: "markdown", value: c }, n;
}
async function z(e, i) {
  const t = await e.loadSource(), o = e.toProfileVariableDefinition();
  if (o.type !== "pixel")
    throw new Error("Incorrect definition passed to pixel variable");
  const { name: s } = e, a = e.getDescription(), n = {
    label: s,
    detail: s,
    insertText: s,
    insertTextMode: f.asIs,
    insertTextFormat: u.PlainText,
    kind: i
  }, r = {
    name: s,
    description: a,
    type: "dictionary",
    properties: [],
    completion: n
  };
  if (!t || !o.definition)
    return r;
  const c = await B(t), [d, l] = M(c, a);
  return r.properties = d, n.documentation = { kind: "markdown", value: l }, r;
}
async function O(e, i) {
  const { name: t, dictionaryVariables: o } = e, s = e.getDescription(), a = o.reduce((n, r) => {
    n !== "" && (n += `  
  
`), n += `**${r.name}**  
${r.type}`;
    const c = r.getDescription();
    return c && (n += `  
${c}`), n;
  }, s);
  return {
    name: t,
    description: s,
    type: "dictionary",
    // TODO(editor-profile): remove filter once we have a arcade-specific editor profile type that doesn't include SQL-layer-specific variables.
    properties: await b(o.filter(P), w.Field),
    completion: {
      label: t,
      detail: t,
      insertText: t,
      insertTextMode: f.asIs,
      insertTextFormat: u.PlainText,
      kind: i,
      documentation: { kind: "markdown", value: a }
    }
  };
}
async function y(e = "en") {
  const i = x.get(e);
  if (i)
    return i;
  if (!j.has(e))
    return await y("en");
  try {
    const t = await fetch(T(`./assets/arcade-language/profiles/arcade-profiles.t9n.${e}.json`));
    return t.ok ? Q(e, await t.json()) : e === "en" ? null : await y("en");
  } catch {
    return null;
  }
}
const x = /* @__PURE__ */ new Map();
function Q(e, i) {
  const t = /* @__PURE__ */ new Map();
  return i.forEach((o) => {
    t.set(o.id, o);
  }), x.set(e, t), t;
}
async function U(e, i = "en") {
  return (await y(i))?.get(e) ?? null;
}
function G(e) {
  return !!e && typeof e == "object" && "additionalVariables" in e && Array.isArray(e.additionalVariables);
}
function H(e) {
  return e?.map(m);
}
function m(e) {
  switch (e.type) {
    case "dictionary":
      return {
        ...e,
        type: e.type,
        properties: H(e.properties)
      };
    case "array":
      return {
        ...e,
        type: e.type,
        elementType: { type: "number", name: "number" }
      };
    default:
      return { ...e, type: e.type };
  }
}
async function J(e, i = "en") {
  if (!h(e))
    return;
  const t = await U(e.id, i);
  if (!t)
    return;
  const o = e.definitions, s = new Map(
    Object.entries(o).map(([n, r]) => [n.toLowerCase(), r])
  ), a = {
    bundles: [...t.bundles],
    variables: [],
    hiddenApiItems: e.hiddenApiItems?.map((n) => n.toLowerCase())
  };
  return t.variables.forEach((n) => {
    if (e.disabledVariables?.includes(n.name))
      return;
    const r = s.get(n.name.toLowerCase());
    switch (n.type) {
      case "dictionary":
        return a.variables.push(
          m({
            ...n,
            type: n.type,
            properties: Array.isArray(r) ? [...r] : n.properties
          })
        );
      case "feature":
        return C(r) ? a.variables.push({ ...n, type: "feature", definition: r }) : void 0;
      case "pixel":
        return I(r) ? a.variables.push({ ...n, type: n.type, definition: r }) : void 0;
      case "voxel":
        return E(r) ? a.variables.push({ ...n, type: n.type, definition: r }) : void 0;
      case "featureSet":
        return k(r) ? a.variables.push({ ...n, type: n.type, definition: r }) : void 0;
      case "featureSetCollection":
        return A(r) ? a.variables.push({ ...n, type: n.type, definition: r }) : void 0;
      default:
        return a.variables.push(m(n));
    }
  }), G(e) && a.variables.push(...e.additionalVariables ?? []), a;
}
const N = [
  "catalog-footprint",
  "catalog",
  "csv",
  "feature",
  "geojson",
  "knowledge-graph-sublayer",
  "oriented-imagery",
  "subtype-group",
  "subtype-sublayer",
  "wfs"
];
function R(e) {
  return !!e && typeof e == "object" && "type" in e && typeof e.type == "string" && N.includes(e.type);
}
const W = {
  filterFsCollectionLayersFromMap: (e) => (
    // only some layers are supported in arcade feature sets, one example: https://devtopia.esri.com/WebGIS/arcgis-web-components/issues/10715
    R(e)
  )
};
class X extends S {
  /**
   * Per-model update chaining to serialize profile updates.
   *
   * Why this is necessary:
   * `setProfileForModel` does multiple async steps (t9n load, predefined profile conversion,
   * profile -> API conversion, model disposal + re-init). If callers invoke it twice quickly,
   * the *earlier* call can complete *after* the later call and overwrite the newer API context,
   * because a new profile could convert faster than an old one (e.g. if the newer one is a simple
   * editor profile but the older one is a complex predefined profile that takes a while to convert).
   *
   * We prevent that race by chaining a promise per model key so updates for the same model run
   * strictly in call order.
   */
  #e = /* @__PURE__ */ new Map();
  constructor() {
    super("arcade", {
      apiPath: "./assets/arcade-language/api",
      apiPrefix: "arcade-api.t9n."
    });
  }
  /**
   * Create an EditorProfile for the given model id using the given definition and locale.
   * The EditorProfile is used by the the Arcade Language service as well by the coding components.
   * The definition can be a pre-defined profile or an editor profile definition.
   * If the locale is not provided then the 'en' locale is used.
   * @param modelId The model id for which to create the context.
   * @param definition The definition to use for the model context.
   * @param locale The locale to use for the model context.
   * @returns The EditorProfile for the model.
   */
  async setProfileForModel(i, t, o = { locale: "en" }) {
    const s = this._getApiKey(i), n = (this.#e.get(s) || Promise.resolve()).then(async () => {
      const c = t, d = h(c) ? await J(c, o.locale) : c, l = await $(
        o.locale,
        T("./assets/editor-profile/t9n"),
        "messages."
      );
      if (!l)
        throw new Error(`Failed to load the language bundle for ${o.locale}`);
      this.disposeForModel(i);
      const g = new L(
        d,
        l,
        o.locale,
        W
      );
      this._modelToProfileMap.set(s, g);
      const D = await K(g);
      this.updateApiContextForModel(i, {
        locale: o.locale,
        profile: D,
        snippets: o.snippets
      });
    }), r = n.finally(() => {
      this.#e.get(s) === r && this.#e.delete(s);
    });
    return this.#e.set(s, r), await n;
  }
}
const p = new X(), re = {
  setProfileForModel: p.setProfileForModel.bind(p),
  getApiLibraryForModel: p.getApiLibraryForModel.bind(p)
};
export {
  re as a,
  p as b
};
