import { StateField } from '../../../../../@codemirror_state@6.5.2/node_modules/@codemirror/state/dist/index.js';
import { Decoration, EditorView } from '../../../../../@codemirror_view@6.28.6/node_modules/@codemirror/view/dist/index.js';
import { addSuggestions, acceptSuggestion, clearSuggestion } from './effects.js';
import { codeiumConfig } from './config.js';

const ghostMark = Decoration.mark({ class: "cm-ghostText" });
/**
 * Note that the completion _text_ is not actually a decoration!
 * The text is very real, and actually inserted into the editor.
 * The completion decoration is just a decoration that matches
 * the same range as the completion text, and changes how it looks.
 */
const completionDecoration = StateField.define({
    create(_state) {
        return null;
    },
    update(state, transaction) {
        const config = transaction.state.facet(codeiumConfig);
        for (const effect of transaction.effects) {
            if (effect.is(addSuggestions)) {
                const { changeSpecs, index } = effect.value;
                // NOTE: here we're adjusting the decoration range
                // to refer to locations in the document _after_ we've
                // inserted the text.
                const ranges = changeSpecs[index].map((suggestionRange) => {
                    const range = ghostMark.range(suggestionRange.absoluteStartPos, suggestionRange.absoluteEndPos);
                    return range;
                });
                const widgetPos = ranges.at(-1)?.to;
                const decorations = Decoration.set([
                    ...ranges,
                    ...(widgetPos !== undefined &&
                        changeSpecs.length > 1 &&
                        config.widgetClass
                        ? [
                            Decoration.widget({
                                widget: new config.widgetClass(index, changeSpecs.length),
                                side: 1,
                            }).range(widgetPos),
                        ]
                        : []),
                ]);
                return {
                    index,
                    decorations,
                    changeSpecs,
                    reverseChangeSet: effect.value.reverseChangeSet,
                };
            }
            if (effect.is(acceptSuggestion)) {
                return null;
            }
            if (effect.is(clearSuggestion)) {
                return null;
            }
        }
        if (state) {
            // If we ever have a state that is being updated,
            // map it through the new changes to avoid the potential
            // of a mismatch between it and the new document and new
            // document length
            return {
                ...state,
                decorations: state.decorations.map(transaction.changes),
                reverseChangeSet: state.reverseChangeSet.map(transaction.changes),
            };
        }
        return state;
    },
    provide: (field) => EditorView.decorations.from(field, (value) => {
        return value?.decorations || Decoration.none;
    }),
});

export { completionDecoration };
