import { renderIcons } from './icons.js';

/**
 * Format processor name: convert snake_case to Title Case
 * @param {string} name - Processor name in snake_case
 * @returns {string} Formatted name in Title Case
 */
export function formatProcessorName(name) {
    return name
        .split('_')
        .map(word => word.charAt(0).toUpperCase() + word.slice(1))
        .join(' ');
}

/**
 * Debounce function execution
 * @param {Function} func - Function to debounce
 * @param {number} wait - Delay in milliseconds
 * @returns {Function} Debounced function
 */
export function debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
        const later = () => {
            clearTimeout(timeout);
            func(...args);
        };
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
    };
}

/**
 * Debouncer class for managing multiple debounced operations
 */
export class Debouncer {
    constructor() {
        this.timers = new Map();
    }

    debounce(key, fn, delay = 300) {
        if (this.timers.has(key)) {
            clearTimeout(this.timers.get(key));
        }
        const timer = setTimeout(() => {
            this.timers.delete(key);
            fn();
        }, delay);
        this.timers.set(key, timer);
    }

    cancel(key) {
        if (this.timers.has(key)) {
            clearTimeout(this.timers.get(key));
            this.timers.delete(key);
        }
    }

    cancelAll() {
        this.timers.forEach(clearTimeout);
        this.timers.clear();
    }
}

/**
 * Request Guard: prevents concurrent execution of the same operation
 */
export class RequestGuard {
    constructor() {
        this.locks = new Set();
    }

    isLocked(key) {
        return this.locks.has(key);
    }

    async runExclusive(key, fn) {
        if (this.isLocked(key)) {
            return;
        }
        this.locks.add(key);
        try {
            return await fn();
        } finally {
            this.locks.delete(key);
        }
    }
}

/**
 * Button State Manager: manages button states (loading, disabled, normal)
 */
export class ButtonState {
    static set(element, { state = 'normal', icon = '', text = '', disabled = false }) {
        if (!element) return;

        switch (state) {
            case 'loading':
                element.disabled = true;
                element.innerHTML = `<span data-icon="${icon || 'loader'}"></span> ${text || 'Загрузка...'}`;
                break;
            case 'disabled':
                element.disabled = true;
                element.innerHTML = icon ? `<span data-icon="${icon}"></span> ${text}` : text;
                break;
            case 'normal':
            default:
                element.disabled = !!disabled;
                element.innerHTML = icon ? `<span data-icon="${icon}"></span> ${text}` : text;
                break;
        }

        renderIcons(element);
    }
}

/**
 * Set button to disabled state during request
 * @param {HTMLElement} button - Button element
 * @param {boolean} loading - Whether to disable button
 * @param {Object} originalState - Original button state to restore
 */
export function setButtonLoading(button, loading, originalState = {}) {
    if (!button) return originalState;

    if (loading) {
        if (!originalState.disabled) {
            originalState.disabled = button.disabled;
        }
        button.disabled = true;
    } else {
        button.disabled = originalState.disabled || false;
    }

    return originalState;
}

/**
 * Create parameter control HTML for processor settings
 * @param {Object} param - Parameter configuration
 * @param {string} processorId - Processor ID
 * @param {string} idPrefix - Prefix for element IDs
 * @returns {string} HTML string for parameter control
 */
export function createParameterControl(param, processorId, idPrefix = 'param') {
    const paramId = `${idPrefix}_${processorId}_${param.name}`;
    const value = param.value;
    const opts = param.options || {};

    switch (param.type) {
        case 'slider':
        case 'range':
            return `
                <div class="chart-setting" data-param="${param.name}">
                    <label class="chart-setting__label">
                        ${param.label}
                        <span class="chart-setting__value">${value}</span>
                    </label>
                    <input
                        type="range"
                        class="chart-setting__slider"
                        id="${paramId}"
                        min="${opts.min ?? 0}"
                        max="${opts.max ?? 100}"
                        step="${opts.step ?? 1}"
                        value="${value}"
                    >
                </div>
            `;

        case 'toggle':
        case 'boolean':
            return `
                <div class="chart-setting" data-param="${param.name}">
                    <label class="chart-setting__toggle">
                        <input type="checkbox" id="${paramId}" ${value ? 'checked' : ''}>
                        <span class="chart-setting__toggle-slider"></span>
                        <span class="chart-setting__label">${param.label}</span>
                    </label>
                </div>
            `;

        case 'select':
        case 'dropdown': {
            let choices = [];
            if (Array.isArray(opts)) choices = opts;
            else if (Array.isArray(opts.choices)) choices = opts.choices;

            const optionsHtml = choices.map(option =>
                `<option value="${option}" ${option === value ? 'selected' : ''}>${option}</option>`
            ).join('');

            return `
                <div class="chart-setting" data-param="${param.name}">
                    <label class="chart-setting__label">${param.label}</label>
                    <select class="chart-setting__select" id="${paramId}">
                        ${optionsHtml}
                    </select>
                </div>
            `;
        }

        case 'button':
            const buttonText = param.label || 'Нажать';
            const actionDesc = opts.action ? `title="${opts.action}"` : '';
            return `
                <div class="chart-setting" data-param="${param.name}">
                    <button
                        type="button"
                        class="chart-setting__button"
                        id="${paramId}"
                        data-processor="${processorId}"
                        data-param="${param.name}"
                        ${actionDesc}
                    >
                        ${buttonText}
                    </button>
                </div>
            `;

        default:
            return `
                <div class="chart-setting" data-param="${param.name}">
                    <label class="chart-setting__label">${param.label}</label>
                    <input
                        type="text"
                        class="chart-setting__input"
                        id="${paramId}"
                        value="${value ?? ''}"
                    >
                </div>
            `;
    }
}

/**
 * Safe clone object avoiding circular references
 * @param {*} obj - Object to clone
 * @param {WeakSet} seen - Set of already seen objects
 * @returns {*} Cloned object
 */
export function safeClone(obj, seen = new WeakSet()) {
    if (obj === null || typeof obj !== 'object') return obj;
    if (seen.has(obj)) return '[Циклическая ссылка]';

    seen.add(obj);

    if (Array.isArray(obj)) {
        return obj.map(item => safeClone(item, seen));
    }

    const cloned = {};
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            try {
                cloned[key] = safeClone(obj[key], seen);
            } catch (e) {
                cloned[key] = `[Ошибка: ${e.message}]`;
            }
        }
    }
    return cloned;
}

/**
 * Download JSON data as a file
 * @param {Object} data - Data to download
 * @param {string} filename - Filename for download
 */
export function downloadJSON(data, filename) {
    const jsonString = JSON.stringify(data, null, 2);
    const blob = new Blob([jsonString], { type: 'application/json' });
    const url = URL.createObjectURL(blob);

    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);

    URL.revokeObjectURL(url);
}

/**
 * Escape HTML to prevent XSS attacks
 * @param {string} unsafe - Unsafe string
 * @returns {string} Escaped string
 */
export function escapeHtml(unsafe) {
    if (typeof unsafe !== 'string') return '';
    return unsafe
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
}

/**
 * Format bytes to human readable format
 * @param {number} bytes - Number of bytes
 * @returns {string} Formatted string
 */
export function formatBytes(bytes) {
    if (bytes === 0) return '0 байт';

    const k = 1024;
    const sizes = ['байт', 'КБ', 'МБ', 'ГБ'];
    const i = Math.min(sizes.length - 1, Math.floor(Math.log(bytes) / Math.log(k)));

    return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
}

/**
 * Show a confirmation dialog with custom options
 * @param {Object} options - Dialog options
 * @param {string} options.title - Dialog title
 * @param {string} options.message - Dialog message
 * @param {Array<Object>} options.buttons - Array of button configurations
 * @returns {Promise<string>} Resolves with the button value that was clicked
 */
export function showConfirmDialog({ title, message, buttons }) {
    return new Promise((resolve) => {
        // Create modal structure
        const modal = document.createElement('div');
        modal.className = 'modal modal--active';

        const buttonsHtml = buttons.map(btn =>
            `<button class="btn ${btn.class || 'btn--secondary'}" data-value="${btn.value}">${btn.text}</button>`
        ).join('');

        modal.innerHTML = `
            <div class="modal__backdrop" style="background-color: rgba(0, 0, 0, 0.85);"></div>
            <div class="modal__content" style="background-color: #1e293b;">
                <div class="modal__header">
                    <h3 class="modal__title">${escapeHtml(title)}</h3>
                </div>
                <div class="modal__body" style="padding: var(--space-6);">
                    <p style="margin: 0; color: var(--color-text-secondary);">${escapeHtml(message)}</p>
                </div>
                <div style="padding: var(--space-6); padding-top: 0; display: flex; gap: var(--space-3); justify-content: flex-end;">
                    ${buttonsHtml}
                </div>
            </div>
        `;

        document.body.appendChild(modal);

        // Handle button clicks
        const handleClick = (e) => {
            const button = e.target.closest('[data-value]');
            if (button) {
                const value = button.dataset.value;
                cleanup();
                resolve(value);
            }
        };

        // Handle backdrop click
        const handleBackdrop = (e) => {
            if (e.target.classList.contains('modal__backdrop')) {
                cleanup();
                resolve(null);
            }
        };

        // Handle escape key
        const handleEscape = (e) => {
            if (e.key === 'Escape') {
                cleanup();
                resolve(null);
            }
        };

        const cleanup = () => {
            modal.removeEventListener('click', handleClick);
            modal.removeEventListener('click', handleBackdrop);
            document.removeEventListener('keydown', handleEscape);
            modal.remove();
        };

        modal.addEventListener('click', handleClick);
        modal.addEventListener('click', handleBackdrop);
        document.addEventListener('keydown', handleEscape);
    });
}
