import { findMaxBy, compareBy, numberComparator } from "../../../../base/common/arrays.mjs";
import { RunOnceScheduler } from "../../../../base/common/async.mjs";
import { Emitter, Event } from "../../../../base/common/event.mjs";
import { Disposable } from "../../../../base/common/lifecycle.mjs";
import { Position } from "../../../common/core/position.mjs";
import { Range } from "../../../common/core/range.mjs";
import { SnippetParser } from "../../snippet/browser/snippetParser.mjs";
import { SnippetSession } from "../../snippet/browser/snippetSession.mjs";
import { SuggestController } from "../../suggest/browser/suggestController.mjs";
import { minimizeInlineCompletion } from "./inlineCompletionsModel.mjs";
import { normalizedInlineCompletionsEquals } from "./inlineCompletionToGhostText.mjs";
class SuggestWidgetInlineCompletionProvider extends Disposable {
  constructor(editor, suggestControllerPreselector) {
    super();
    this.editor = editor;
    this.suggestControllerPreselector = suggestControllerPreselector;
    this.isSuggestWidgetVisible = false;
    this.isShiftKeyPressed = false;
    this._isActive = false;
    this._currentSuggestItemInfo = void 0;
    this.onDidChangeEmitter = new Emitter();
    this.onDidChange = this.onDidChangeEmitter.event;
    this.setInactiveDelayed = this._register(new RunOnceScheduler(() => {
      if (!this.isSuggestWidgetVisible) {
        if (this._isActive) {
          this._isActive = false;
          this.onDidChangeEmitter.fire();
        }
      }
    }, 100));
    this._register(editor.onKeyDown((e) => {
      if (e.shiftKey && !this.isShiftKeyPressed) {
        this.isShiftKeyPressed = true;
        this.update(this._isActive);
      }
    }));
    this._register(editor.onKeyUp((e) => {
      if (e.shiftKey && this.isShiftKeyPressed) {
        this.isShiftKeyPressed = false;
        this.update(this._isActive);
      }
    }));
    const suggestController = SuggestController.get(this.editor);
    if (suggestController) {
      this._register(suggestController.registerSelector({
        priority: 100,
        select: (model, pos, suggestItems) => {
          const textModel = this.editor.getModel();
          const normalizedItemToPreselect = minimizeInlineCompletion(textModel, this.suggestControllerPreselector());
          if (!normalizedItemToPreselect) {
            return -1;
          }
          const position = Position.lift(pos);
          const candidates = suggestItems.map((suggestItem, index) => {
            const inlineSuggestItem = suggestionToSuggestItemInfo(suggestController, position, suggestItem, this.isShiftKeyPressed);
            const normalizedSuggestItem = minimizeInlineCompletion(textModel, inlineSuggestItem === null || inlineSuggestItem === void 0 ? void 0 : inlineSuggestItem.normalizedInlineCompletion);
            if (!normalizedSuggestItem) {
              return void 0;
            }
            const valid = rangeStartsWith(normalizedItemToPreselect.range, normalizedSuggestItem.range) && normalizedItemToPreselect.text.startsWith(normalizedSuggestItem.text);
            return { index, valid, prefixLength: normalizedSuggestItem.text.length, suggestItem };
          }).filter((item) => item && item.valid);
          const result = findMaxBy(candidates, compareBy((s) => s.prefixLength, numberComparator));
          return result ? result.index : -1;
        }
      }));
      let isBoundToSuggestWidget = false;
      const bindToSuggestWidget = () => {
        if (isBoundToSuggestWidget) {
          return;
        }
        isBoundToSuggestWidget = true;
        this._register(suggestController.widget.value.onDidShow(() => {
          this.isSuggestWidgetVisible = true;
          this.update(true);
        }));
        this._register(suggestController.widget.value.onDidHide(() => {
          this.isSuggestWidgetVisible = false;
          this.setInactiveDelayed.schedule();
          this.update(this._isActive);
        }));
        this._register(suggestController.widget.value.onDidFocus(() => {
          this.isSuggestWidgetVisible = true;
          this.update(true);
        }));
      };
      this._register(Event.once(suggestController.model.onDidTrigger)((e) => {
        bindToSuggestWidget();
      }));
    }
    this.update(this._isActive);
  }
  /**
   * Returns undefined if the suggest widget is not active.
  */
  get state() {
    if (!this._isActive) {
      return void 0;
    }
    return { selectedItem: this._currentSuggestItemInfo };
  }
  update(newActive) {
    const newInlineCompletion = this.getSuggestItemInfo();
    let shouldFire = false;
    if (!suggestItemInfoEquals(this._currentSuggestItemInfo, newInlineCompletion)) {
      this._currentSuggestItemInfo = newInlineCompletion;
      shouldFire = true;
    }
    if (this._isActive !== newActive) {
      this._isActive = newActive;
      shouldFire = true;
    }
    if (shouldFire) {
      this.onDidChangeEmitter.fire();
    }
  }
  getSuggestItemInfo() {
    const suggestController = SuggestController.get(this.editor);
    if (!suggestController) {
      return void 0;
    }
    if (!this.isSuggestWidgetVisible) {
      return void 0;
    }
    const focusedItem = suggestController.widget.value.getFocusedItem();
    if (!focusedItem) {
      return void 0;
    }
    return suggestionToSuggestItemInfo(suggestController, this.editor.getPosition(), focusedItem.item, this.isShiftKeyPressed);
  }
  stopForceRenderingAbove() {
    const suggestController = SuggestController.get(this.editor);
    if (suggestController) {
      suggestController.stopForceRenderingAbove();
    }
  }
  forceRenderingAbove() {
    const suggestController = SuggestController.get(this.editor);
    if (suggestController) {
      suggestController.forceRenderingAbove();
    }
  }
}
function rangeStartsWith(rangeToTest, prefix) {
  return prefix.startLineNumber === rangeToTest.startLineNumber && prefix.startColumn === rangeToTest.startColumn && (prefix.endLineNumber < rangeToTest.endLineNumber || prefix.endLineNumber === rangeToTest.endLineNumber && prefix.endColumn <= rangeToTest.endColumn);
}
function suggestItemInfoEquals(a, b) {
  if (a === b) {
    return true;
  }
  if (!a || !b) {
    return false;
  }
  return a.completionItemKind === b.completionItemKind && a.isSnippetText === b.isSnippetText && normalizedInlineCompletionsEquals(a.normalizedInlineCompletion, b.normalizedInlineCompletion);
}
function suggestionToSuggestItemInfo(suggestController, position, item, toggleMode) {
  if (Array.isArray(item.completion.additionalTextEdits) && item.completion.additionalTextEdits.length > 0) {
    return {
      completionItemKind: item.completion.kind,
      isSnippetText: false,
      normalizedInlineCompletion: {
        // Dummy element, so that space is reserved, but no text is shown
        range: Range.fromPositions(position, position),
        text: ""
      }
    };
  }
  let { insertText } = item.completion;
  let isSnippetText = false;
  if (item.completion.insertTextRules & 4) {
    const snippet = new SnippetParser().parse(insertText);
    const model = suggestController.editor.getModel();
    if (snippet.children.length > 100) {
      return void 0;
    }
    SnippetSession.adjustWhitespace(model, position, snippet, true, true);
    insertText = snippet.toString();
    isSnippetText = true;
  }
  const info = suggestController.getOverwriteInfo(item, toggleMode);
  return {
    isSnippetText,
    completionItemKind: item.completion.kind,
    normalizedInlineCompletion: {
      text: insertText,
      range: Range.fromPositions(position.delta(0, -info.overwriteBefore), position.delta(0, Math.max(info.overwriteAfter, 0)))
    }
  };
}
export {
  SuggestWidgetInlineCompletionProvider,
  rangeStartsWith
};
