/**
 * Monaco Editor wiki-link autocomplete provider.
 *
 * Registers a completion provider for markdown that triggers on `[`
 * and provides file suggestions when the user types `[[`.
 */

import type { FileNode } from '@/types/api';

interface FlatFile {
  name: string;
  path: string;
}

const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico']);

function isImage(name: string): boolean {
  const dot = name.lastIndexOf('.');
  return dot >= 0 && IMAGE_EXTENSIONS.has(name.substring(dot).toLowerCase());
}

function flattenFiles(nodes: FileNode[]): FlatFile[] {
  const out: FlatFile[] = [];
  for (const n of nodes) {
    if (n.isDirectory) {
      if (n.children) out.push(...flattenFiles(n.children));
    } else {
      out.push({ path: n.path, name: n.name });
    }
  }
  return out;
}

/**
 * Register a wiki-link `[[` autocomplete provider for Monaco's markdown mode.
 *
 * @param monaco - The Monaco namespace (from onMount callback)
 * @param getFileTree - Callback returning the current file tree
 * @returns IDisposable to unregister the provider
 */
export function registerWikiLinkCompletion(
  monaco: typeof import('monaco-editor'),
  getFileTree: () => FileNode[],
): import('monaco-editor').IDisposable {
  return monaco.languages.registerCompletionItemProvider('markdown', {
    triggerCharacters: ['['],
    provideCompletionItems(model, position) {
      // Check that we're in a `[[` context
      const lineContent = model.getLineContent(position.lineNumber);
      const textBefore = lineContent.substring(0, position.column - 1);

      // Must end with `[[` (possibly with partial text after it)
      const wikiStart = textBefore.lastIndexOf('[[');
      if (wikiStart < 0) return { suggestions: [] };

      // Make sure there's no `]]` between wikiStart and cursor (not already closed)
      const between = textBefore.substring(wikiStart + 2);
      if (between.includes(']]')) return { suggestions: [] };

      // Check if this is an image embed (![[)
      const isEmbed = wikiStart > 0 && textBefore[wikiStart - 1] === '!';

      // The partial query the user has typed after `[[`
      const query = between.toLowerCase();

      // Build the range from after `[[` to current cursor (or past any auto-closed `]]`)
      const startCol = wikiStart + 3; // +2 for `[[`, +1 for 1-based
      const textAfter = lineContent.substring(position.column - 1);
      // If Monaco auto-closed brackets, there may be `]]` right after cursor — consume it
      const closingMatch = textAfter.match(/^\]{1,2}/);
      const extraClose = closingMatch ? closingMatch[0].length : 0;
      const range = new monaco.Range(
        position.lineNumber,
        startCol,
        position.lineNumber,
        position.column + extraClose,
      );

      const files = flattenFiles(getFileTree());
      const suggestions: import('monaco-editor').languages.CompletionItem[] = [];

      for (const file of files) {
        const fileIsImage = isImage(file.name);

        // If user typed `![[`, prefer images; if `[[`, prefer docs
        // But show all files regardless — just sort differently
        const displayName = file.name.endsWith('.md')
          ? file.name.slice(0, -3)
          : file.name;

        // Filter by query
        if (query && !displayName.toLowerCase().includes(query) && !file.path.toLowerCase().includes(query)) {
          continue;
        }

        // For wiki-links, we insert just the name (hook resolves the path)
        const insertName = file.name.endsWith('.md')
          ? file.name.slice(0, -3)
          : file.name;

        suggestions.push({
          label: displayName,
          kind: fileIsImage
            ? monaco.languages.CompletionItemKind.File
            : monaco.languages.CompletionItemKind.Reference,
          detail: file.path,
          insertText: insertName + ']]',
          range,
          // Sort: prioritize images for ![[, docs for [[
          sortText: (isEmbed ? (fileIsImage ? '0' : '1') : (fileIsImage ? '1' : '0')) + displayName,
        });
      }

      return { suggestions };
    },
  });
}
