function debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
        const later = () => {
            clearTimeout(timeout);
            func(...args);
        };
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
    };
}

class AreaInput {
    constructor(options = {}) {
        this.options = {
            containerId: options.containerId || null,
            id: options.id || this.generateId(),
            name: options.name || options.id || this.generateId(),
            label: options.label || '',
            placeholder: options.placeholder || '',
            defaultValue: options.defaultValue || '',
            required: options.required || false,
            validation: options.validation || null,
            onChange: options.onChange || null,
            className: options.className || '',
            showCharCounter: options.showCharCounter !== undefined ? options.showCharCounter : false,
            maxLength: options.maxLength || null,

            resize: options.resize || 'both',

            autoGrow: options.autoGrow !== undefined ? options.autoGrow : false,

            minHeight: options.minHeight || '100px',
            maxHeight: options.maxHeight || '500px',
            minWidth: options.minWidth || '100%',
            maxWidth: options.maxWidth || '100%',
            rows: options.rows || 5,

            useRichText: options.useRichText || false,
            quillConfig: options.quillConfig || {
                theme: 'snow',
                modules: {
                    toolbar: [
                        [{ 'header': [1, 2, 3, false] }],
                        ['bold', 'italic', 'underline', 'strike'],
                        [{ 'list': 'ordered'}, { 'list': 'bullet' }],
                        [{ 'color': [] }, { 'background': [] }],
                        ['link'],
                        ['clean']
                    ]
                },
                placeholder: options.placeholder || 'Enter text...'
            },

            ...options
        };

        this.container = null;
        this.textareaElement = null;
        this.labelElement = null;
        this.errorElement = null;
        this.charCounterElement = null;
        this.quillEditor = null;
        this.isValid = true;

        this.handleInput = null;
        this.handleBlur = null;
        this.handleTextChange = null;
        this.handleSelectionChange = null;

        this.init();
    }

    generateId() {
        return 'area-input-' + Math.random().toString(36).substr(2, 9);
    }

    init() {
        if (this.options.containerId) {
            this.container = document.getElementById(this.options.containerId);
            if (!this.container) {
                console.error(`AreaInput: Container with ID "${this.options.containerId}" not found`);
                return;
            }
            this.render();
        }
    }

    render() {
        if (!this.container) return;

        const formGroupClass = `form-group area-input-component ${this.options.className}`.trim();

        if (this.options.useRichText) {
            this.renderRichTextEditor(formGroupClass);
        } else {
            this.renderTextarea(formGroupClass);
        }
    }

    renderTextarea(formGroupClass) {

        const styleProps = [];

        if (this.options.autoGrow) {
            styleProps.push(`resize: none`);

            styleProps.push(`overflow-y: auto`);
        } else {
            styleProps.push(`resize: ${this.options.resize}`);
        }

        if (this.options.minHeight) styleProps.push(`min-height: ${this.options.minHeight}`);
        if (this.options.maxHeight) styleProps.push(`max-height: ${this.options.maxHeight}`);
        if (this.options.minWidth) styleProps.push(`min-width: ${this.options.minWidth}`);
        if (this.options.maxWidth) styleProps.push(`max-width: ${this.options.maxWidth}`);
        const styleAttr = styleProps.join('; ');

        this.container.innerHTML = `
            <div class="${formGroupClass}">
                <label for="${this.options.id}">${this.options.label}${this.options.required ? ' *' : ''}</label>
                <div class="area-input-wrapper">
                    <textarea
                        id="${this.options.id}"
                        name="${this.options.name}"
                        placeholder="${this.options.placeholder}"
                        rows="${this.options.rows}"
                        ${this.options.required ? 'required' : ''}
                        ${this.options.maxLength ? `maxlength="${this.options.maxLength}"` : ''}
                        class="area-input-field ${this.options.autoGrow ? 'auto-grow' : ''}"
                        style="${styleAttr}"
                        aria-describedby="${this.options.id}-error ${this.options.id}-counter"
                    >${this.options.defaultValue}</textarea>
                </div>
                ${this.options.showCharCounter ? `<div id="${this.options.id}-counter" class="area-input-char-counter">0 characters</div>` : ''}
                <div id="${this.options.id}-error" class="area-input-error" role="alert" style="display: none;"></div>
            </div>
        `;

        this.textareaElement = this.container.querySelector(`#${this.options.id}`);
        this.labelElement = this.container.querySelector('label');
        this.errorElement = this.container.querySelector('.area-input-error');
        this.charCounterElement = this.container.querySelector('.area-input-char-counter');

        this.attachEventListeners();

        if (this.options.autoGrow && this.textareaElement) {
            this.adjustHeight();
        }

        if (this.charCounterElement) {
            this.updateCharCounter();
        }
    }

    renderRichTextEditor(formGroupClass) {

        if (typeof Quill === 'undefined') {
            console.error('AreaInput: Quill.js is not loaded. Please include Quill.js library.');
            this.renderTextarea(formGroupClass);
            return;
        }

        this.container.innerHTML = `
            <div class="${formGroupClass}">
                <label for="${this.options.id}">${this.options.label}${this.options.required ? ' *' : ''}</label>
                <div id="${this.options.id}" class="area-input-rich-text" style="min-height: ${this.options.minHeight}; max-height: ${this.options.maxHeight};" aria-describedby="${this.options.id}-error ${this.options.id}-counter"></div>
                ${this.options.showCharCounter ? `<div id="${this.options.id}-counter" class="area-input-char-counter">0 characters</div>` : ''}
                <div id="${this.options.id}-error" class="area-input-error" role="alert" style="display: none;"></div>
            </div>
        `;

        this.labelElement = this.container.querySelector('label');
        this.errorElement = this.container.querySelector('.area-input-error');
        this.charCounterElement = this.container.querySelector('.area-input-char-counter');

        const editorElement = this.container.querySelector(`#${this.options.id}`);
        this.quillEditor = new Quill(editorElement, this.options.quillConfig);

        if (this.options.defaultValue) {
            this.quillEditor.root.innerHTML = this.options.defaultValue;
        }

        this.attachRichTextEventListeners();

        if (this.charCounterElement) {
            this.updateCharCounter();
        }
    }

    attachEventListeners() {
        if (!this.textareaElement) return;

        const debouncedOnChange = this.options.onChange ? debounce((value) => {
            this.options.onChange(value, this);
        }, 300) : null;

        this.handleInput = (e) => {

            if (this.errorElement && this.errorElement.style.display !== 'none') {
                this.showError('');
            }

            this.updateCharCounter();

            if (this.options.autoGrow) {
                this.adjustHeight();
            }

            if (debouncedOnChange) {
                debouncedOnChange(e.target.value);
            }
        };

        this.textareaElement.addEventListener('input', this.handleInput);
    }

    adjustHeight() {
        if (!this.textareaElement || !this.options.autoGrow) return;

        this.textareaElement.style.height = 'auto';

        let newHeight = this.textareaElement.scrollHeight;

        const minHeight = parseInt(this.options.minHeight) || 0;
        const maxHeight = parseInt(this.options.maxHeight) || Infinity;

        newHeight = Math.max(minHeight, Math.min(maxHeight, newHeight));

        this.textareaElement.style.height = newHeight + 'px';
    }

    attachRichTextEventListeners() {
        if (!this.quillEditor) return;

        const debouncedOnChange = this.options.onChange ? debounce((value) => {
            this.options.onChange(value, this);
        }, 300) : null;

        this.handleTextChange = () => {

            if (this.errorElement && this.errorElement.style.display !== 'none') {
                this.showError('');
            }

            this.updateCharCounter();
            if (debouncedOnChange) {
                debouncedOnChange(this.getValue());
            }
        };

        this.quillEditor.on('text-change', this.handleTextChange);
    }

    validateInput() {

        const plainText = this.getPlainText();
        const value = this.getValue();
        let isValid = true;
        let errorMessage = '';

        if (this.options.required && !plainText.trim()) {
            isValid = false;
            errorMessage = `${this.options.label} is required`;
        }

        if (isValid && this.options.validation && typeof this.options.validation === 'function') {

            const validationValue = this.quillEditor ? plainText : value;
            const validationResult = this.options.validation(validationValue);
            if (validationResult !== true) {
                isValid = false;
                errorMessage = validationResult || 'Invalid input';
            }
        }

        this.isValid = isValid;
        this.showError(isValid ? '' : errorMessage);

        return isValid;
    }

    showError(message) {
        if (!this.errorElement) return;

        if (message) {
            this.errorElement.textContent = message;
            this.errorElement.style.display = 'block';

            if (this.textareaElement) {
                this.textareaElement.classList.add('error');
            } else if (this.quillEditor) {
                this.quillEditor.root.parentElement.classList.add('error');
            }
        } else {
            this.errorElement.style.display = 'none';

            if (this.textareaElement) {
                this.textareaElement.classList.remove('error');
            } else if (this.quillEditor) {
                this.quillEditor.root.parentElement.classList.remove('error');
            }
        }
    }

    updateCharCounter() {
        if (!this.charCounterElement) return;

        const plainValue = this.getPlainText();
        const length = plainValue.length;

        if (this.options.maxLength) {
            this.charCounterElement.textContent = `${length} / ${this.options.maxLength} characters`;
        } else {
            this.charCounterElement.textContent = `${length} character${length !== 1 ? 's' : ''}`;
        }

        this.charCounterElement.classList.remove('warning', 'error');

        if (this.options.maxLength) {
            const percentUsed = (length / this.options.maxLength) * 100;

            if (percentUsed >= 100) {
                this.charCounterElement.classList.add('error');
            } else if (percentUsed >= 80) {
                this.charCounterElement.classList.add('warning');
            }
        }
    }

    getValue() {
        if (this.quillEditor) {

            return this.quillEditor.root.innerHTML;
        } else if (this.textareaElement) {
            return this.textareaElement.value;
        }
        return '';
    }

    getPlainText() {
        if (this.quillEditor) {

            return this.quillEditor.getText().trim();
        } else if (this.textareaElement) {
            return this.textareaElement.value;
        }
        return '';
    }

    setValue(value) {
        if (this.quillEditor) {
            this.quillEditor.root.innerHTML = value || '';
        } else if (this.textareaElement) {
            this.textareaElement.value = value || '';
        }

    }

    clear() {
        this.setValue('');
    }

    focus() {
        if (this.quillEditor) {
            this.quillEditor.focus();
        } else if (this.textareaElement) {
            this.textareaElement.focus();
        }
    }

    setEnabled(enabled) {
        if (this.quillEditor) {
            this.quillEditor.enable(enabled);
        } else if (this.textareaElement) {
            this.textareaElement.disabled = !enabled;
        }
    }

    validate() {
        return this.validateInput();
    }

    getData() {
        return {
            [this.options.name]: this.getValue()
        };
    }

    setData(data) {
        if (data && data[this.options.name] !== undefined) {
            this.setValue(data[this.options.name]);
        }
    }

    getEditor() {
        return this.quillEditor;
    }

    isRichText() {
        return this.options.useRichText && this.quillEditor !== null;
    }

    destroy() {

        if (this.textareaElement) {
            if (this.handleInput) {
                this.textareaElement.removeEventListener('input', this.handleInput);
            }
            if (this.handleBlur) {
                this.textareaElement.removeEventListener('blur', this.handleBlur);
            }
        }

        if (this.quillEditor) {
            if (this.handleTextChange) {
                this.quillEditor.off('text-change', this.handleTextChange);
            }
            if (this.handleSelectionChange) {
                this.quillEditor.off('selection-change', this.handleSelectionChange);
            }
            this.quillEditor = null;
        }

        if (this.container) {
            this.container.innerHTML = '';
        }

        this.textareaElement = null;
        this.labelElement = null;
        this.errorElement = null;
        this.charCounterElement = null;
        this.container = null;
        this.handleInput = null;
        this.handleBlur = null;
        this.handleTextChange = null;
        this.handleSelectionChange = null;
    }
}

if (typeof module !== 'undefined' && module.exports) {
    module.exports = AreaInput;
}