/**
 * Monaco snippet glue shared by the scaffold services (`spec-transform-scaffold`,
 * `spec-param-scaffold`; docs/architecture/08 → editor augmentation): inserting a
 * `${n:default}` template as a live, Tab-through session, and the completion-side
 * plumbing — the slot a scaffold suggestion replaces and the suggestion item itself.
 * The pure text math (offsets, comma affixing) is `core/spec-snippet`; this file is
 * only what needs Monaco types or the editor handle.
 */

import * as monaco from 'monaco-editor/esm/vs/editor/edcore.main';
import { arrayAffixes } from '@core/spec-snippet';

/** Monaco's snippet contribution — the public entry to insert a `${n:…}` template with tab stops. */
interface SnippetInserter extends monaco.editor.IEditorContribution {
  insert(template: string, opts?: { adjustWhitespace?: boolean }): void;
}

/**
 * Insert `template` at `offset` through Monaco's snippet engine, so its
 * `${n:default}` tab stops become a live, Tab-through session. Places the cursor first
 * (the controller inserts at the selection). `adjustWhitespace: false` keeps the engine
 * from re-basing our explicit indentation to the insertion line's — it otherwise leaves
 * a multi-line block's closing bracket under-indented. No-op if the contribution is
 * absent (it ships in `edcore.main`, so this is just defensive).
 */
export function insertSnippetAt(
  editor: monaco.editor.IStandaloneCodeEditor,
  offset: number,
  template: string,
): void {
  const model = editor.getModel();
  if (!model) return;
  editor.setPosition(model.getPositionAt(offset));
  editor.focus();
  editor.getContribution<SnippetInserter>('snippetController2')?.insert(template, {
    adjustWhitespace: false,
  });
}

/** The array-element slot a scaffold completion fills. */
export interface ScaffoldSlot {
  /** The range the suggestion replaces (the bare partial the user typed). */
  range: monaco.Range;
  /** Comma affixes that keep the surrounding array valid around the inserted entry. */
  lead: string;
  trail: string;
}

/**
 * The completion slot at `position` for an array-element scaffold: the bare alphabetic
 * partial before the cursor, parsed from the line — never `getWordUntilPosition`, whose
 * JSON wordPattern reaches across punctuation (docs/architecture/08 → completion
 * ranges) — plus the comma affixes for the element the suggestion becomes.
 */
export function scaffoldSlotAt(
  model: monaco.editor.ITextModel,
  position: monaco.Position,
  text: string,
  offset: number,
): ScaffoldSlot {
  const before = model.getLineContent(position.lineNumber).slice(0, position.column - 1);
  const partial = /[A-Za-z]*$/.exec(before)?.[0] ?? '';
  const range = new monaco.Range(
    position.lineNumber,
    position.column - partial.length,
    position.lineNumber,
    position.column,
  );
  const { lead, trail } = arrayAffixes(text, offset - partial.length, offset);
  return { range, lead, trail };
}

/**
 * A catalog entry as a snippet completion in `slot` — comma-affixed to keep the array
 * valid, and sorted by catalog index above the schema's generic items.
 */
export function scaffoldSuggestion(
  slot: ScaffoldSlot,
  index: number,
  item: { label: string; detail: string; body: string; documentation?: monaco.IMarkdownString },
): monaco.languages.CompletionItem {
  return {
    label: item.label,
    kind: monaco.languages.CompletionItemKind.Snippet,
    detail: item.detail,
    documentation: item.documentation,
    insertText: `${slot.lead}${item.body}${slot.trail}`,
    insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
    range: slot.range,
    sortText: `0${String(index).padStart(2, '0')}`,
  };
}
