import type { Logger } from '../common';
import { collection } from '../common';
import type { Selection } from '../pm';
import { inputRules } from '../pm';
import { triggerInputRule } from './input-rule';
import { suggestionKeymap } from './keymap';
import {
  openSuggestion,
  pluginSuggestion,
  type ReplacementContent,
  removeSuggestMark,
  replaceSuggestMarkWith,
} from './plugin-suggestion';
import { suggestionsMark } from './suggestions-mark';

export * from './input-rule';
export * from './keymap';
export * from './plugin-suggestion';
export * from './suggestions-mark';

export type SuggestionConfig = {
  providerId?: string;
  markName: string;
  trigger: string;
  markClassName: string;
  logger?: Logger;
  requireTriggerBoundary?: boolean;
  installKeymap?: boolean;
  /** End the suggestion when the query contains whitespace (slash menu
   * behavior); providers with multi-word queries (wiki links) leave it off. */
  endOnWhitespace?: boolean;
  /** End the suggestion as soon as text follows the trigger. Use for
   * trigger-only providers that have no query. */
  endOnQuery?: boolean;
};

export function setupSuggestions(config: SuggestionConfig) {
  const marks = {
    [config.markName]: suggestionsMark({
      markName: config.markName,
      className: config.markClassName,
      trigger: config.trigger,
    }),
  };

  const plugin = {
    inputRules: inputRules({ rules: [triggerInputRule(config)] }),
    ...(config.installKeymap === false
      ? {}
      : { keybindings: suggestionKeymap() }),
    suggestion: pluginSuggestion(config),
  };

  return collection({
    id: `suggestions-${config.providerId ?? config.markName}`,
    marks,
    plugin,
    command: {
      replaceSuggestMarkWith: ({
        content,
        focus,
      }: {
        content?: ReplacementContent;
        focus?: boolean;
      }) => {
        return replaceSuggestMarkWith({
          markName: config.markName,
          content,
          focus,
        });
      },
      removeSuggestMark: (selection: Selection) => {
        return removeSuggestMark({
          markName: config.markName,
          selection,
        });
      },
      openSuggestion: () => {
        return openSuggestion({
          markName: config.markName,
          trigger: config.trigger,
        });
      },
    },
    markdown: {
      marks: {
        [config.markName]: {
          toMarkdown: {
            open: () => '',
            close: () => '',
          },
        },
      },
    },
  });
}
