/**
 * LaTeX Completion Provider for Monaco Editor.
 * Provides autocompletion for LaTeX commands, environments, and packages.
 */
import type * as Monaco from 'monaco-editor';
import {
    LATEX_COMMANDS,
    LATEX_ENVIRONMENTS,
    LATEX_PACKAGES,
    type LaTeXCommand
} from './commands/data';

export class LaTeXCompletionProvider implements Monaco.languages.CompletionItemProvider {
    triggerCharacters = ['\\', '{', '['];

    constructor(private monaco: typeof Monaco) {}

    provideCompletionItems(
        model: Monaco.editor.ITextModel,
        position: Monaco.Position,
        context: Monaco.languages.CompletionContext,
        token: Monaco.CancellationToken
    ): Monaco.languages.ProviderResult<Monaco.languages.CompletionList> {
        const textUntilPosition = model.getValueInRange({
            startLineNumber: position.lineNumber,
            startColumn: 1,
            endLineNumber: position.lineNumber,
            endColumn: position.column,
        });

        const suggestions: Monaco.languages.CompletionItem[] = [];

        // Check if we're after a backslash (command completion)
        const commandMatch = textUntilPosition.match(/\\([\w]*)$/);
        if (commandMatch) {
            // Calculate range for just the command name (after backslash)
            const commandRange = {
                startLineNumber: position.lineNumber,
                startColumn: position.column - commandMatch[1].length,
                endLineNumber: position.lineNumber,
                endColumn: position.column,
            };
            suggestions.push(...this.getCommandCompletions(commandRange));
        }

        // Check if we're inside \begin{ or \end{ (environment completion)
        const envMatch = textUntilPosition.match(/\\(begin|end)\{([\w*]*)$/);
        if (envMatch) {
            const envRange = {
                startLineNumber: position.lineNumber,
                startColumn: position.column - envMatch[2].length,
                endLineNumber: position.lineNumber,
                endColumn: position.column,
            };
            suggestions.push(...this.getEnvironmentCompletions(envRange));
        }

        // Check if we're inside \usepackage{ (package completion)
        const pkgMatch = textUntilPosition.match(/\\usepackage(?:\[.*?\])?\{([\w,\s]*)$/);
        if (pkgMatch) {
            const pkgRange = {
                startLineNumber: position.lineNumber,
                startColumn: position.column - pkgMatch[1].length,
                endLineNumber: position.lineNumber,
                endColumn: position.column,
            };
            suggestions.push(...this.getPackageCompletions(pkgRange));
        }

        return { suggestions };
    }

    private getCommandCompletions(range: Monaco.IRange): Monaco.languages.CompletionItem[] {
        return LATEX_COMMANDS.map((cmd, index) => ({
            label: `\\${cmd.name}`,
            kind: this.monaco.languages.CompletionItemKind.Function,
            // insertText without backslash since we only replace the command name
            insertText: cmd.snippet || cmd.name,
            insertTextRules: cmd.snippet
                ? this.monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
                : undefined,
            detail: cmd.signature ? `\\${cmd.name}${cmd.signature}` : cmd.category,
            documentation: this.formatDocumentation(cmd),
            range,
            sortText: String(index).padStart(4, '0'),
            // Filter based on what user typed after backslash
            filterText: cmd.name,
        }));
    }

    private getEnvironmentCompletions(range: Monaco.IRange): Monaco.languages.CompletionItem[] {
        return LATEX_ENVIRONMENTS.map((env, index) => ({
            label: env.name,
            kind: this.monaco.languages.CompletionItemKind.Snippet,
            insertText: env.name,
            detail: env.package ? `Requires: ${env.package}` : 'Base LaTeX',
            documentation: {
                value: env.description,
            },
            range,
            sortText: String(index).padStart(4, '0'),
        }));
    }

    private getPackageCompletions(range: Monaco.IRange): Monaco.languages.CompletionItem[] {
        return LATEX_PACKAGES.map((pkg, index) => ({
            label: pkg.name,
            kind: this.monaco.languages.CompletionItemKind.Module,
            insertText: pkg.name,
            documentation: pkg.description,
            range,
            sortText: String(index).padStart(4, '0'),
        }));
    }

    private formatDocumentation(cmd: LaTeXCommand): Monaco.IMarkdownString {
        let doc = `**${cmd.description}**\n\n`;

        if (cmd.package) {
            doc += `*Requires:* \`\\usepackage{${cmd.package}}\`\n\n`;
        }

        if (cmd.example) {
            doc += `**Example:**\n\`\`\`latex\n${cmd.example}\n\`\`\``;
        }

        return { value: doc };
    }
}

/**
 * Creates a completion provider for LaTeX.
 */
export function createCompletionProvider(monaco: typeof Monaco): LaTeXCompletionProvider {
    return new LaTeXCompletionProvider(monaco);
}
