"use strict";
const codemirror = require("./codemirror.cjs.js");
const graphql = require("graphql");
const forEachState = require("./forEachState.cjs.js");
function hintList(cursor, token, list) {
  const hints = filterAndSortList(list, normalizeText(token.string));
  if (!hints) {
    return;
  }
  const tokenStart = token.type !== null && /"|\w/.test(token.string[0]) ? token.start : token.end;
  return {
    list: hints,
    from: { line: cursor.line, ch: tokenStart },
    to: { line: cursor.line, ch: token.end }
  };
}
function filterAndSortList(list, text) {
  if (!text) {
    return filterNonEmpty(list, (entry) => !entry.isDeprecated);
  }
  const byProximity = list.map((entry) => ({
    proximity: getProximity(normalizeText(entry.text), text),
    entry
  }));
  const conciseMatches = filterNonEmpty(filterNonEmpty(byProximity, (pair) => pair.proximity <= 2), (pair) => !pair.entry.isDeprecated);
  const sortedMatches = conciseMatches.sort((a, b) => (a.entry.isDeprecated ? 1 : 0) - (b.entry.isDeprecated ? 1 : 0) || a.proximity - b.proximity || a.entry.text.length - b.entry.text.length);
  return sortedMatches.map((pair) => pair.entry);
}
function filterNonEmpty(array, predicate) {
  const filtered = array.filter(predicate);
  return filtered.length === 0 ? array : filtered;
}
function normalizeText(text) {
  return text.toLowerCase().replaceAll(/\W/g, "");
}
function getProximity(suggestion, text) {
  let proximity = lexicalDistance(text, suggestion);
  if (suggestion.length > text.length) {
    proximity -= suggestion.length - text.length - 1;
    proximity += suggestion.indexOf(text) === 0 ? 0 : 0.5;
  }
  return proximity;
}
function lexicalDistance(a, b) {
  let i;
  let j;
  const d = [];
  const aLength = a.length;
  const bLength = b.length;
  for (i = 0; i <= aLength; i++) {
    d[i] = [i];
  }
  for (j = 1; j <= bLength; j++) {
    d[0][j] = j;
  }
  for (i = 1; i <= aLength; i++) {
    for (j = 1; j <= bLength; j++) {
      const cost = a[i - 1] === b[j - 1] ? 0 : 1;
      d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
      if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
        d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost);
      }
    }
  }
  return d[aLength][bLength];
}
codemirror.CodeMirror.registerHelper("hint", "graphql-variables", (editor, options) => {
  const cur = editor.getCursor();
  const token = editor.getTokenAt(cur);
  const results = getVariablesHint(cur, token, options);
  if ((results === null || results === void 0 ? void 0 : results.list) && results.list.length > 0) {
    results.from = codemirror.CodeMirror.Pos(results.from.line, results.from.ch);
    results.to = codemirror.CodeMirror.Pos(results.to.line, results.to.ch);
    codemirror.CodeMirror.signal(editor, "hasCompletion", editor, results, token);
  }
  return results;
});
function getVariablesHint(cur, token, options) {
  const state = token.state.kind === "Invalid" ? token.state.prevState : token.state;
  const { kind, step } = state;
  if (kind === "Document" && step === 0) {
    return hintList(cur, token, [{ text: "{" }]);
  }
  const { variableToType } = options;
  if (!variableToType) {
    return;
  }
  const typeInfo = getTypeInfo(variableToType, token.state);
  if (kind === "Document" || kind === "Variable" && step === 0) {
    const variableNames = Object.keys(variableToType);
    return hintList(cur, token, variableNames.map((name) => ({
      text: `"${name}": `,
      type: variableToType[name]
    })));
  }
  if ((kind === "ObjectValue" || kind === "ObjectField" && step === 0) && typeInfo.fields) {
    const inputFields = Object.keys(typeInfo.fields).map((fieldName) => typeInfo.fields[fieldName]);
    return hintList(cur, token, inputFields.map((field) => ({
      text: `"${field.name}": `,
      type: field.type,
      description: field.description
    })));
  }
  if (kind === "StringValue" || kind === "NumberValue" || kind === "BooleanValue" || kind === "NullValue" || kind === "ListValue" && step === 1 || kind === "ObjectField" && step === 2 || kind === "Variable" && step === 2) {
    const namedInputType = typeInfo.type ? graphql.getNamedType(typeInfo.type) : void 0;
    if (namedInputType instanceof graphql.GraphQLInputObjectType) {
      return hintList(cur, token, [{ text: "{" }]);
    }
    if (namedInputType instanceof graphql.GraphQLEnumType) {
      const values = namedInputType.getValues();
      return hintList(cur, token, values.map((value) => ({
        text: `"${value.name}"`,
        type: namedInputType,
        description: value.description
      })));
    }
    if (namedInputType === graphql.GraphQLBoolean) {
      return hintList(cur, token, [
        { text: "true", type: graphql.GraphQLBoolean, description: "Not false." },
        { text: "false", type: graphql.GraphQLBoolean, description: "Not true." }
      ]);
    }
  }
}
function getTypeInfo(variableToType, tokenState) {
  const info = {
    type: null,
    fields: null
  };
  forEachState.forEachState(tokenState, (state) => {
    switch (state.kind) {
      case "Variable": {
        info.type = variableToType[state.name];
        break;
      }
      case "ListValue": {
        const nullableType = info.type ? graphql.getNullableType(info.type) : void 0;
        info.type = nullableType instanceof graphql.GraphQLList ? nullableType.ofType : null;
        break;
      }
      case "ObjectValue": {
        const objectType = info.type ? graphql.getNamedType(info.type) : void 0;
        info.fields = objectType instanceof graphql.GraphQLInputObjectType ? objectType.getFields() : null;
        break;
      }
      case "ObjectField": {
        const objectField = state.name && info.fields ? info.fields[state.name] : null;
        info.type = objectField === null || objectField === void 0 ? void 0 : objectField.type;
        break;
      }
    }
  });
  return info;
}
//# sourceMappingURL=hint.cjs2.js.map
