/**
 * StyleDetectionEngine - Analyzes elements for nested styled children with position preservation
 * 
 * Purpose: Extract styled nested elements as formatting options AND preserve their positions
 */

// Node type constants for environments that don't have them
const NODE_TYPES = {
    ELEMENT_NODE: 1,
    TEXT_NODE: 3
};

export class StyleDetectionEngine {
    constructor() {
        this.styleNameMappings = this.initializeStyleMappings();
    }

    /**
     * Analyze element for nested styled elements AND their positions (CLASSES.md line 26-29)
     * Returns both detected styles and structured content that preserves positions
     * Enhanced with smart default formatting options when not present
     * 
     * @param {HTMLElement} element - The .insertr element to analyze
     * @returns {Object} - {styles: Map, structure: Array}
     */
    detectStylesAndStructure(element) {
        const styleMap = new Map();
        const contentStructure = [];
        
        // Parse the element's content while preserving structure
        this.parseContentStructure(element, styleMap, contentStructure);
        
        // Add smart default formatting options if not already present
        this.addSmartDefaults(styleMap, element);
        
        return {
            styles: styleMap,
            structure: contentStructure
        };
    }

    /**
     * Parse content structure while collecting style information
     * Creates a structure array that preserves text and styled element positions
     * 
     * @param {HTMLElement} element - Element to parse
     * @param {Map} styleMap - Map to collect style information
     * @param {Array} structure - Array to build content structure
     */
    parseContentStructure(element, styleMap, structure) {
        for (const node of element.childNodes) {
            if (node.nodeType === (typeof Node !== 'undefined' ? Node.TEXT_NODE : NODE_TYPES.TEXT_NODE)) {
                // Plain text node
                const text = node.textContent;
                if (text.trim()) { // Only add non-whitespace text
                    structure.push({
                        type: 'text',
                        content: text
                    });
                }
            } else if (node.nodeType === (typeof Node !== 'undefined' ? Node.ELEMENT_NODE : NODE_TYPES.ELEMENT_NODE)) {
                // Element node - analyze for styling and extract editable properties
                const styleInfo = this.analyzeElement(node);

                if (styleInfo) {
                    // Styled element - add to both style map and structure
                    styleMap.set(styleInfo.id, styleInfo);

                    // Extract all editable properties for this element
                    const editableProperties = this.extractEditableProperties(node);

                    structure.push({
                        type: 'styled',
                        styleId: styleInfo.id,
                        properties: editableProperties,
                        element: node
                    });
                } else {
                    // Unstyled element - treat as text
                    structure.push({
                        type: 'text',
                        content: node.textContent
                    });
                }
            }
        }
    }

    /**
     * Extract all editable properties from an element
     * Handles multi-property elements like links (href + content), images (src + alt), etc.
     * 
     * @param {HTMLElement} element - Element to extract properties from
     * @returns {Object} - Object with editable properties
     */
    extractEditableProperties(element) {
        const tagName = element.tagName.toLowerCase();
        const properties = {};

        // Always include text content as the primary editable property
        properties.content = element.textContent;

        // Add element-specific editable properties
        switch (tagName) {
            case 'a':
                properties.href = element.href || '';
                properties.title = element.title || '';
                if (element.getAttribute('target')) {
                    properties.target = element.getAttribute('target');
                }
                break;

            case 'img':
                properties.src = element.src || '';
                properties.alt = element.alt || '';
                properties.title = element.title || '';
                break;

            case 'button':
                properties.content = element.textContent;
                if (element.type) {
                    properties.type = element.type;
                }
                if (element.disabled !== undefined) {
                    properties.disabled = element.disabled;
                }
                break;

            case 'input':
                properties.value = element.value || '';
                properties.placeholder = element.placeholder || '';
                if (element.type) {
                    properties.type = element.type;
                }
                break;

            default:
                // For other elements, content is the main editable property
                break;
        }

        return properties;
    }

    /**
     * Analyze individual element for style information
     * 
     * @param {HTMLElement} element - Child element to analyze
     * @returns {Object|null} - Style information object or null if not styled
     */
    analyzeElement(element) {
        const tagName = element.tagName.toLowerCase();
        const classes = Array.from(element.classList);
        const attributes = this.extractElementAttributes(element);

        // Skip elements without styling (no classes or special attributes)
        if (classes.length === 0 && !this.hasSignificantAttributes(attributes)) {
            return null;
        }

        // Generate unique style ID and human-readable name
        const styleId = this.generateStyleId(tagName, classes, attributes);
        const styleName = this.generateStyleName(tagName, classes, attributes);

        return {
            id: styleId,
            name: styleName,
            tagName: tagName,
            classes: classes,
            attributes: attributes,
            template: this.extractTemplate(element),
            textContent: element.textContent,
            element: element // Keep reference for advanced operations
        };
    }

    /**
     * Extract all attributes from element (except standard ones)
     * 
     * @param {HTMLElement} element - Element to extract attributes from
     * @returns {Object} - Attributes object
     */
    extractElementAttributes(element) {
        const attributes = {};
        const skipAttributes = new Set(['class', 'id']); // These are handled separately

        for (const attr of element.attributes) {
            if (!skipAttributes.has(attr.name)) {
                attributes[attr.name] = attr.value;
            }
        }

        // Include ID if present (it's significant for styling)
        if (element.id) {
            attributes.id = element.id;
        }

        return attributes;
    }

    /**
     * Check if element has significant attributes beyond basic content
     * 
     * @param {Object} attributes - Attributes object
     * @returns {boolean} - True if has significant styling attributes
     */
    hasSignificantAttributes(attributes) {
        // Consider data-*, aria-*, href, rel, target, etc. as significant
        return Object.keys(attributes).some(key =>
            key.startsWith('data-') ||
            key.startsWith('aria-') ||
            key === 'href' ||
            key === 'rel' ||
            key === 'target' ||
            key === 'id'
        );
    }

    /**
     * Generate unique style ID for internal tracking
     * 
     * @param {string} tagName - Element tag name
     * @param {Array} classes - Array of CSS classes
     * @param {Object} attributes - Element attributes
     * @returns {string} - Unique style identifier
     */
    generateStyleId(tagName, classes, attributes) {
        const classString = classes.length > 0 ? '_' + classes.join('_') : '';
        const attrString = attributes.id ? '_' + attributes.id : '';
        return `${tagName}${classString}${attrString}`;
    }

    /**
     * Generate human-readable style name for editor UI
     * Based on mappings from demo examples and common patterns
     * 
     * @param {string} tagName - Element tag name  
     * @param {Array} classes - Array of CSS classes
     * @param {Object} attributes - Element attributes
     * @returns {string} - Human-readable style name
     */
    generateStyleName(tagName, classes, attributes) {
        // Check for predefined mappings first
        const key = this.createMappingKey(tagName, classes[0]);
        if (this.styleNameMappings.has(key)) {
            return this.styleNameMappings.get(key);
        }

        // Generate name from class names
        if (classes.length > 0) {
            return this.classesToDisplayName(classes);
        }

        // Generate name from tag + attributes
        if (attributes.id) {
            return this.tagToDisplayName(tagName) + ' (' + attributes.id + ')';
        }

        // Fallback to tag name
        return this.tagToDisplayName(tagName);
    }

    /**
     * Initialize style name mappings based on demo examples
     * Maps common class patterns to user-friendly names
     */
    initializeStyleMappings() {
        const mappings = new Map();

        // From demo examples in simple/index.html
        mappings.set('strong.emph', 'Emphasis');
        mappings.set('strong.brand', 'Brand');
        mappings.set('strong.highlight-price', 'Highlight Price');
        mappings.set('a.fancy', 'Fancy Link');
        mappings.set('span.highlight', 'Highlight');
        mappings.set('span.brand-color', 'Brand Color');
        mappings.set('button.btn', 'Button Style');
        mappings.set('em.emph', 'Emphasis Italic');
        mappings.set('blockquote.testimonial', 'Testimonial');
        mappings.set('i.icon-home', 'Home Icon');
        mappings.set('i.icon-info', 'Info Icon');

        // Common patterns
        mappings.set('strong.highlight', 'Highlight Bold');
        mappings.set('span.brand', 'Brand Style');
        mappings.set('a.button', 'Button Link');
        mappings.set('span.tag', 'Tag');
        mappings.set('span.badge', 'Badge');

        return mappings;
    }

    /**
     * Create mapping key for style name lookup
     */
    createMappingKey(tagName, firstClass) {
        return firstClass ? `${tagName}.${firstClass}` : tagName;
    }

    /**
     * Convert CSS classes to display name
     * Handles multi-word classes and common patterns
     */
    classesToDisplayName(classes) {
        return classes[0]
            .split('-')
            .map(word => word.charAt(0).toUpperCase() + word.slice(1))
            .join(' ');
    }

    /**
     * Convert tag name to display name
     */
    tagToDisplayName(tagName) {
        const tagMappings = {
            'strong': 'Bold',
            'em': 'Italic',
            'span': 'Style',
            'a': 'Link',
            'button': 'Button',
            'i': 'Icon',
            'code': 'Code',
            'blockquote': 'Quote'
        };

        return tagMappings[tagName] || tagName.charAt(0).toUpperCase() + tagName.slice(1);
    }

    /**
     * Extract template from element for style application
     * Creates a template with multiple insertion points for complex elements
     * 
     * @param {HTMLElement} element - Element to create template from
     * @returns {Object} - Template object with placeholders for all editable properties
     */
    extractTemplate(element) {
        const tagName = element.tagName.toLowerCase();
        const clone = element.cloneNode(false); // Clone without children

        // Create template with placeholders for different element types
        const template = {
            tagName: tagName,
            attributes: {},
            editableProperties: []
        };

        // Copy all attributes
        for (const attr of element.attributes) {
            template.attributes[attr.name] = attr.value;
        }

        // Define editable properties and their placeholders
        switch (tagName) {
            case 'a':
                template.editableProperties = ['content', 'href', 'title'];
                template.attributes.href = '{{HREF}}';
                if (template.attributes.title) {
                    template.attributes.title = '{{TITLE}}';
                }
                clone.textContent = '{{CONTENT}}';
                break;

            case 'img':
                template.editableProperties = ['src', 'alt', 'title'];
                template.attributes.src = '{{SRC}}';
                template.attributes.alt = '{{ALT}}';
                if (template.attributes.title) {
                    template.attributes.title = '{{TITLE}}';
                }
                break;

            case 'button':
                template.editableProperties = ['content'];
                clone.textContent = '{{CONTENT}}';
                break;

            case 'input':
                template.editableProperties = ['value', 'placeholder'];
                if (template.attributes.value !== undefined) {
                    template.attributes.value = '{{VALUE}}';
                }
                if (template.attributes.placeholder) {
                    template.attributes.placeholder = '{{PLACEHOLDER}}';
                }
                break;

            default:
                // Default: only content is editable
                template.editableProperties = ['content'];
                clone.textContent = '{{CONTENT}}';
                break;
        }

        // Store both the structured template and the HTML template for backward compatibility
        template.html = clone.outerHTML;

        return template;
    }

    /**
     * Create element from style template with new properties
     * Handles multi-property elements like links (href + content)
     * 
     * @param {Object} styleInfo - Style information object
     * @param {Object|string} properties - Properties to insert into template (or just text for backward compatibility)
     * @returns {HTMLElement} - New element with applied style and properties
     */
    createElementFromTemplate(styleInfo, properties) {
        const element = document.createElement(styleInfo.tagName);

        // Apply classes
        styleInfo.classes.forEach(cls => element.classList.add(cls));

        // Apply base attributes (non-editable ones)
        Object.entries(styleInfo.attributes).forEach(([key, value]) => {
            // Skip attributes that will be set from properties
            if (!this.isEditableAttribute(key, styleInfo.tagName)) {
                element.setAttribute(key, value);
            }
        });

        // Handle properties - support both object and string (backward compatibility)
        if (typeof properties === 'string') {
            // Legacy support: treat as text content
            element.textContent = properties;
        } else if (typeof properties === 'object') {
            // New multi-property support
            this.applyPropertiesToElement(element, properties, styleInfo.tagName);
        }

        return element;
    }

    /**
     * Check if an attribute is editable for a given element type
     * 
     * @param {string} attributeName - Name of the attribute
     * @param {string} tagName - Element tag name
     * @returns {boolean} - True if attribute is editable
     */
    isEditableAttribute(attributeName, tagName) {
        const editableAttributes = {
            'a': ['href', 'title', 'target'],
            'img': ['src', 'alt', 'title'],
            'input': ['value', 'placeholder'],
            'button': []
        };

        return (editableAttributes[tagName] || []).includes(attributeName);
    }

    /**
     * Apply properties to element based on element type
     * 
     * @param {HTMLElement} element - Element to apply properties to
     * @param {Object} properties - Properties to apply
     * @param {string} tagName - Element tag name
     */
    applyPropertiesToElement(element, properties, tagName) {
        switch (tagName) {
            case 'a':
                if (properties.content !== undefined) {
                    element.textContent = properties.content;
                }
                if (properties.href !== undefined) {
                    element.href = properties.href;
                }
                if (properties.title !== undefined) {
                    element.title = properties.title;
                }
                if (properties.target !== undefined) {
                    element.target = properties.target;
                }
                break;

            case 'img':
                if (properties.src !== undefined) {
                    element.src = properties.src;
                }
                if (properties.alt !== undefined) {
                    element.alt = properties.alt;
                }
                if (properties.title !== undefined) {
                    element.title = properties.title;
                }
                break;

            case 'button':
                if (properties.content !== undefined) {
                    element.textContent = properties.content;
                }
                if (properties.type !== undefined) {
                    element.type = properties.type;
                }
                if (properties.disabled !== undefined) {
                    element.disabled = properties.disabled;
                }
                break;

            case 'input':
                if (properties.value !== undefined) {
                    element.value = properties.value;
                }
                if (properties.placeholder !== undefined) {
                    element.placeholder = properties.placeholder;
                }
                if (properties.type !== undefined) {
                    element.type = properties.type;
                }
                break;

            default:
                // Default: set text content
                if (properties.content !== undefined) {
                    element.textContent = properties.content;
                }
                break;
        }
    }

    /**
     * Get plain text content from element with preserved spacing
     * Useful for extracting text for editing while maintaining structure
     * 
     * @param {HTMLElement} element - Element to extract text from
     * @returns {string} - Plain text with preserved spacing
     */
    extractPlainText(element) {
        // Use textContent but preserve some structure for multi-element content
        return element.textContent;
    }

    /**
     * Check if element has complex nested structure
     * Used to determine editing strategy
     * 
     * @param {HTMLElement} element - Element to check
     * @returns {boolean} - True if element has nested styled children
     */
    hasNestedStyledElements(element) {
        return element.children.length > 0;
    }

    /**
     * Reconstruct HTML from structure array and updated properties
     * 
     * @param {Array} structure - Content structure array
     * @param {Map} styles - Style definitions
     * @param {Object} updatedProperties - Updated properties for structure pieces
     * @returns {string} - Reconstructed HTML
     */
    reconstructHTML(structure, styles, updatedProperties = {}) {
        let html = '';

        structure.forEach((piece, index) => {
            if (piece.type === 'text') {
                // Check if any styles are applied to this text piece
                const applied = updatedProperties[index];
                if (applied && applied.styleId && styles.has(applied.styleId)) {
                    const styleInfo = styles.get(applied.styleId);
                    const styledElement = this.createElementFromTemplate(styleInfo, applied.properties || applied.content);
                    html += styledElement.outerHTML;
                } else {
                    // Use updated content or original content
                    const content = applied?.content || piece.content;
                    html += content;
                }
            } else if (piece.type === 'styled') {
                // Use updated properties or original properties
                const properties = updatedProperties[index]?.properties || piece.properties;

                if (styles.has(piece.styleId)) {
                    const styleInfo = styles.get(piece.styleId);
                    const styledElement = this.createElementFromTemplate(styleInfo, properties);
                    html += styledElement.outerHTML;
                } else {
                    // Fallback if style is missing - just use content
                    const content = properties?.content || properties;
                    html += content;
                }
            }
        });

        return html;
    }

    /**
     * Add smart default formatting options when not already present
     * Provides essential formatting (bold, italic, link) if developer hasn't defined them
     * 
     * @param {Map} styleMap - Existing detected styles
     * @param {HTMLElement} element - The element being analyzed
     */
    addSmartDefaults(styleMap, element) {
        // Only add defaults for content elements that benefit from text formatting
        const contentTags = new Set(['p', 'div', 'section', 'article', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'td', 'th', 'blockquote']);
        
        if (!contentTags.has(element.tagName.toLowerCase())) {
            return; // Skip for non-content elements
        }
        
        // Check what's already available
        const hasStrong = this.hasStyleTag(styleMap, 'strong');
        const hasEm = this.hasStyleTag(styleMap, 'em');
        const hasB = this.hasStyleTag(styleMap, 'b');
        const hasI = this.hasStyleTag(styleMap, 'i');
        const hasA = this.hasStyleTag(styleMap, 'a');
        
        // Add Bold if not present (prefer <strong> for semantics)
        if (!hasStrong && !hasB) {
            styleMap.set('default-strong', {
                name: 'Bold',
                tagName: 'strong',
                classes: [],
                attributes: {},
                element: null, // Virtual element - no DOM reference
                isDefault: true,
                description: 'Make text bold for emphasis'
            });
        }
        
        // Add Italic if not present (prefer <em> for semantics)
        if (!hasEm && !hasI) {
            styleMap.set('default-em', {
                name: 'Italic',
                tagName: 'em',
                classes: [],
                attributes: {},
                element: null,
                isDefault: true,
                description: 'Make text italic for emphasis'
            });
        }
        
        // Add Link if not present
        if (!hasA) {
            styleMap.set('default-a', {
                name: 'Link',
                tagName: 'a',
                classes: [],
                attributes: { href: '' },
                element: null,
                isDefault: true,
                description: 'Create a hyperlink'
            });
        }
    }
    
    /**
     * Check if styleMap already contains a specific tag type
     * 
     * @param {Map} styleMap - Style map to check
     * @param {string} tagName - Tag name to look for
     * @returns {boolean} - True if tag is already present
     */
    hasStyleTag(styleMap, tagName) {
        for (const [, styleInfo] of styleMap) {
            if (styleInfo.tagName === tagName) {
                return true;
            }
        }
        return false;
    }

    /**
     * Extract plain text from structure while preserving order
     * 
     * @param {Array} structure - Content structure array
     * @returns {string} - Plain text content
     */
    extractTextFromStructure(structure) {
        return structure.map(piece => {
            if (piece.type === 'text') {
                return piece.content;
            } else if (piece.type === 'styled') {
                // For styled elements, return the content property
                return piece.properties?.content || '';
            }
            return '';
        }).join('');
    }

    /**
     * Create structure from plain text by mapping to existing structure
     * Used when user edits plain text and we need to reapply it to structure
     * 
     * @param {string} newText - New text content
     * @param {Array} originalStructure - Original structure to map to
     * @returns {Array} - Updated structure array
     */
    mapTextToStructure(newText, originalStructure) {
        // Simple approach: if text length matches, assume same structure
        // More sophisticated approaches could use diff algorithms
        const originalText = this.extractTextFromStructure(originalStructure);

        if (newText === originalText) {
            // No changes - return original structure
            return originalStructure;
        }

        // For now, create simple text structure
        // TODO: Implement smarter text-to-structure mapping
        return [{
            type: 'text',
            content: newText
        }];
    }
}

// Export singleton instance
export const styleDetectionEngine = new StyleDetectionEngine();
