// const ZERO_WIDTH_SPACE = '&#8203;';
const ZERO_WIDTH_SPACE = '&#x200B;';
class EditableInputModel {

    constructor(value = '') {
        this.value = value;
    }

    setValue(newValue) {
        this.value = newValue;
    }

    getValue() {
        return this.value;
    }
}

class EditableInputView {
    constructor(model, controller) {
        this.model = model;
        this.controller = controller;
    }

    render(element) {
        this.element = element;
        this.element.innerHTML = this.generateInputHtml();
        this.editableDiv = this.element.querySelector('.editableInputContent');
        this.attachEventListeners();
    }

    generateInputHtml() {
        let contentHtml = this.model.getValue() ? this.renderContent(this.parseContent(this.model.getValue())) : ZERO_WIDTH_SPACE;
        return `
            <div contenteditable="true" class="editableInputContent form-control" data-value="${this.model.getValue()}">
                ${contentHtml}
            </div>
        `;
    }

    attachEventListeners() {
        this.editableDiv.addEventListener('input', (event) => {
            this.controller.handleInput(this.editableDiv);
            if (this.controller.additionalCallback) {
                this.controller.additionalCallback(this.model.getValue());
            }
        });
    }

    updateUI() {
        let content = this.model.getValue();
        if (content) {
            this.editableDiv.innerHTML = this.renderContent(this.parseContent(content));
        } else {
            this.editableDiv.innerHTML = ZERO_WIDTH_SPACE; // Insert a zero-width space
        }

        this.editableDiv.setAttribute('data-value', content);
    }

    renderContent(contentArray) {
        let content = '';
        contentArray.forEach((item) => {

            if (item.startsWith('{{') && item.endsWith('}}')) {
                content += `<span class="variable">${item}</span>`;
            } else {
                content += `<span class="text">${item}</span>`;
            }
        });
        return content;
    }

    parseContent(inputString) {
        let returnStringArray = [];
        let regex = /{{.*?}}/g;
        let lastIndex = 0;

        inputString.replace(regex, (match, index) => {
            // Add the string before the match
            if (index > lastIndex) {
                returnStringArray.push(inputString.slice(lastIndex, index));
            }

            // Add the matched string including the delimiters
            returnStringArray.push(match);

            // Update lastIndex for the next iteration
            lastIndex = index + match.length;
        });

        // Add any remaining string after the last match
        if (lastIndex < inputString.length) {
            returnStringArray.push(inputString.slice(lastIndex));
        }

        return returnStringArray;
    }
}


class EditableInputController {
    constructor(model) {
        this.model = model;
        this.view = new EditableInputView(this.model, this);
    }

    init(element, callback) {
        this.view.render(element);
        this.additionalCallback = callback;
    }


    getCurrentCursor(element) {
        const selection = window.getSelection();
        if (selection.rangeCount === 0) return null;

        const range = selection.getRangeAt(0);
        const preCaretRange = range.cloneRange();
        preCaretRange.selectNodeContents(element);
        preCaretRange.setEnd(range.endContainer, range.endOffset);
        return preCaretRange.toString().trim().length;
    }

    handleInput(editableDiv) {
        // Check and remove zero-width space if it's the only content
        if (editableDiv.innerHTML === '&#8203;') {
            editableDiv.innerHTML = '';
        }

        let cursor = this.getCurrentCursor(editableDiv);
        let mergedString = Array.from(editableDiv.childNodes)
            .filter(function (node) {
                // Keep only SPAN elements and text nodes
                return node.nodeName.toUpperCase() === 'SPAN' || node.nodeType === 3;
            })
            .map(node => {
                return node.nodeType === 3 ? node.data.trim() : node.innerText.trim();
            })
            .join('');

        var result = mergedString.replace(/[\u200B-\u200D\uFEFF]/g, '');

        let content = this.parseContent(result);

        this.model.setValue(content.join(''));
        // Optionally, update the view if needed
        this.view.updateUI();

        this.restoreCursor(editableDiv, cursor);
    }

    parseContent(inputString) {
        let returnStringArray = [];
        let regex = /{{.*?}}/g;
        let lastIndex = 0;

        inputString.replace(regex, (match, index) => {
            // Add the string before the match
            if (index > lastIndex) {
                returnStringArray.push(inputString.slice(lastIndex, index));
            }

            // Add the matched string including the delimiters
            returnStringArray.push(match);

            // Update lastIndex for the next iteration
            lastIndex = index + match.length;
        });

        // Add any remaining string after the last match
        if (lastIndex < inputString.length) {
            returnStringArray.push(inputString.slice(lastIndex));
        }

        return returnStringArray;
    }

    restoreCursor(element, position) {
        const selection = window.getSelection();
        const range = document.createRange();
        range.setStart(element, 0);
        range.collapse(true);

        const nodeStack = [element];
        let node, foundStart = false, stop = false;
        let charIndex = 0;

        while (!stop && (node = nodeStack.pop())) {
            if (node.nodeType === 3) {
                const nextCharIndex = charIndex + node.length;
                if (!foundStart && position >= charIndex && position <= nextCharIndex) {
                    range.setStart(node, position - charIndex);
                    foundStart = true;
                }
                if (foundStart && position >= charIndex && position <= nextCharIndex) {
                    range.setEnd(node, position - charIndex);
                    stop = true;
                }
                charIndex = nextCharIndex;
            } else {
                let i = node.childNodes.length;
                while (i--) {
                    nodeStack.push(node.childNodes[i]);
                }
            }
        }

        selection.removeAllRanges();
        selection.addRange(range);
    }

    updateUI() {
        this.view.updateUI();
    }
}

class EditableInput {
    constructor(options = {}) {
        // Initialize the model with the value
        this.model = new EditableInputModel(options.value || '');
        this.controller = new EditableInputController(this.model);

        // Additional properties as required
        this.name = options.name || '';
        this.id = options.id || '';
        this.class = options.class || 'form-control';
        this.placeholder = options.placeholder || '';
    }

    init(element, callback) {
        // Initialize the controller with the DOM element
        this.controller.init(element, callback);
    }

    getValue() {
        // Delegate to the model
        return this.model.getValue();
    }

    setValue(newValue) {
        // Delegate to the model and update the view
        this.model.setValue(newValue);
        this.controller.updateUI();
    }

    // Any additional methods required for interaction with this component
}

export default EditableInput;

