/**
 * Unit tests for StyleDetectionEngine
 * Tests based on actual demo examples from simple/index.html
 */
import { StyleDetectionEngine } from './style-detection.js';

// Mock DOM environment for testing
const mockDocument = {
    createElement: (tagName) => ({
        tagName: tagName.toUpperCase(),
        classList: new Set(),
        attributes: [],
        textContent: '',
        outerHTML: `<${tagName}></${tagName}>`,
        cloneNode: () => mockDocument.createElement(tagName)
    })
};

global.document = mockDocument;

describe('StyleDetectionEngine', () => {
    let engine;
    
    beforeEach(() => {
        engine = new StyleDetectionEngine();
    });

    describe('Demo Example 1: Styled Strong Element', () => {
        test('should detect <strong class="emph"> as Emphasis style', () => {
            // Simulate: <p class="insertr">Hello <strong class="emph">world</strong> and welcome!</p>
            const mockElement = createMockElement('p', {
                children: [
                    createMockElement('strong', {
                        classes: ['emph'],
                        textContent: 'world'
                    })
                ]
            });

            const detectedStyles = engine.detectStyles(mockElement);
            
            expect(detectedStyles.size).toBe(1);
            expect(detectedStyles.has('strong_emph')).toBe(true);
            
            const emphStyle = detectedStyles.get('strong_emph');
            expect(emphStyle.name).toBe('Emphasis');
            expect(emphStyle.tagName).toBe('strong');
            expect(emphStyle.classes).toEqual(['emph']);
            expect(emphStyle.textContent).toBe('world');
        });
    });

    describe('Demo Example 2: Styled Link Element', () => {
        test('should detect <a class="fancy"> as Fancy Link style', () => {
            // Simulate: <p class="insertr">Visit our <a class="fancy" href="#about">about page</a> for more info.</p>
            const mockElement = createMockElement('p', {
                children: [
                    createMockElement('a', {
                        classes: ['fancy'],
                        attributes: { href: '#about' },
                        textContent: 'about page'
                    })
                ]
            });

            const detectedStyles = engine.detectStyles(mockElement);
            
            expect(detectedStyles.size).toBe(1);
            const fancyLinkStyle = detectedStyles.get('a_fancy');
            expect(fancyLinkStyle.name).toBe('Fancy Link');
            expect(fancyLinkStyle.tagName).toBe('a');
            expect(fancyLinkStyle.attributes.href).toBe('#about');
        });
    });

    describe('Demo Example 3: Mixed Content with Complex Attributes', () => {
        test('should detect link with multiple attributes', () => {
            // Simulate: <a href="https://example.com" rel="noopener" target="_blank" class="fancy">our site</a>
            const mockElement = createMockElement('p', {
                children: [
                    createMockElement('a', {
                        classes: ['fancy'],
                        attributes: { 
                            href: 'https://example.com',
                            rel: 'noopener',
                            target: '_blank'
                        },
                        textContent: 'our site'
                    }),
                    createMockElement('span', {
                        classes: ['highlight'],
                        textContent: 'amazing'
                    })
                ]
            });

            const detectedStyles = engine.detectStyles(mockElement);
            
            expect(detectedStyles.size).toBe(2);
            
            const linkStyle = detectedStyles.get('a_fancy');
            expect(linkStyle.attributes.rel).toBe('noopener');
            expect(linkStyle.attributes.target).toBe('_blank');
            
            const highlightStyle = detectedStyles.get('span_highlight');
            expect(highlightStyle.name).toBe('Highlight');
        });
    });

    describe('Demo Example 4: Multiple Styled Elements', () => {
        test('should detect multiple different styled elements', () => {
            // Simulate: <strong class="brand">Acme Corp</strong> <span class="highlight">innovative</span> <em class="emph">modern</em>
            const mockElement = createMockElement('p', {
                children: [
                    createMockElement('strong', {
                        classes: ['brand'],
                        textContent: 'Acme Corp'
                    }),
                    createMockElement('span', {
                        classes: ['highlight'],
                        textContent: 'innovative'
                    }),
                    createMockElement('em', {
                        classes: ['emph'],
                        textContent: 'modern'
                    })
                ]
            });

            const detectedStyles = engine.detectStyles(mockElement);
            
            expect(detectedStyles.size).toBe(3);
            expect(detectedStyles.has('strong_brand')).toBe(true);
            expect(detectedStyles.has('span_highlight')).toBe(true);
            expect(detectedStyles.has('em_emph')).toBe(true);
            
            expect(detectedStyles.get('strong_brand').name).toBe('Brand');
            expect(detectedStyles.get('span_highlight').name).toBe('Highlight');
            expect(detectedStyles.get('em_emph').name).toBe('Emphasis Italic');
        });
    });

    describe('Demo Example 5: Button with Data Attributes', () => {
        test('should detect button with data attributes', () => {
            // Simulate: <button class="btn" data-action="signup" data-analytics="cta-main">Sign Up Now</button>
            const mockElement = createMockElement('p', {
                children: [
                    createMockElement('button', {
                        classes: ['btn'],
                        attributes: {
                            'data-action': 'signup',
                            'data-analytics': 'cta-main'
                        },
                        textContent: 'Sign Up Now'
                    })
                ]
            });

            const detectedStyles = engine.detectStyles(mockElement);
            
            expect(detectedStyles.size).toBe(1);
            const buttonStyle = detectedStyles.get('button_btn');
            expect(buttonStyle.name).toBe('Button Style');
            expect(buttonStyle.attributes['data-action']).toBe('signup');
            expect(buttonStyle.attributes['data-analytics']).toBe('cta-main');
        });
    });

    describe('Style Name Generation', () => {
        test('should generate correct names for common patterns', () => {
            expect(engine.generateStyleName('strong', ['highlight-price'], {})).toBe('Highlight Price');
            expect(engine.generateStyleName('span', ['brand-color'], {})).toBe('Brand Color');
            expect(engine.generateStyleName('a', ['fancy'], {})).toBe('Fancy Link');
            expect(engine.generateStyleName('button', ['btn'], {})).toBe('Button Style');
        });

        test('should handle elements with IDs', () => {
            const name = engine.generateStyleName('span', [], { id: 'unique-element' });
            expect(name).toBe('Style (unique-element)');
        });

        test('should fallback to tag names for unstyled elements', () => {
            expect(engine.generateStyleName('blockquote', [], {})).toBe('Quote');
            expect(engine.generateStyleName('code', [], {})).toBe('Code');
        });
    });

    describe('Template Extraction', () => {
        test('should create proper templates with placeholders', () => {
            const mockElement = createMockElement('strong', {
                classes: ['emph'],
                attributes: { 'data-test': 'value' }
            });

            const template = engine.extractTemplate(mockElement);
            expect(template).toContain('{{TEXT}}');
            expect(template).toContain('class="emph"');
            expect(template).toContain('data-test="value"');
        });
    });

    describe('Element Creation from Templates', () => {
        test('should create elements with correct styling', () => {
            const styleInfo = {
                tagName: 'strong',
                classes: ['emph'],
                attributes: { 'data-test': 'value' }
            };

            const element = engine.createElementFromTemplate(styleInfo, 'test content');
            
            expect(element.tagName.toLowerCase()).toBe('strong');
            expect(element.textContent).toBe('test content');
            expect(element.classList.contains('emph')).toBe(true);
            expect(element.getAttribute('data-test')).toBe('value');
        });
    });

    describe('Complex Nesting Detection', () => {
        test('should detect when elements have nested styled children', () => {
            const mockElement = createMockElement('p', {
                children: [
                    createMockElement('strong', { classes: ['emph'] })
                ]
            });

            expect(engine.hasNestedStyledElements(mockElement)).toBe(true);

            const simpleElement = createMockElement('p', { children: [] });
            expect(engine.hasNestedStyledElements(simpleElement)).toBe(false);
        });
    });

    describe('Attribute Significance Detection', () => {
        test('should identify significant attributes', () => {
            expect(engine.hasSignificantAttributes({ 'data-action': 'test' })).toBe(true);
            expect(engine.hasSignificantAttributes({ 'aria-label': 'test' })).toBe(true);
            expect(engine.hasSignificantAttributes({ href: '#link' })).toBe(true);
            expect(engine.hasSignificantAttributes({ id: 'unique' })).toBe(true);
            expect(engine.hasSignificantAttributes({})).toBe(false);
        });
    });

    describe('Edge Cases', () => {
        test('should skip elements without styling', () => {
            const mockElement = createMockElement('p', {
                children: [
                    createMockElement('span', { classes: [], attributes: {} }),
                    createMockElement('strong', { classes: ['emph'] })
                ]
            });

            const detectedStyles = engine.detectStyles(mockElement);
            expect(detectedStyles.size).toBe(1);
            expect(detectedStyles.has('strong_emph')).toBe(true);
        });

        test('should handle empty elements', () => {
            const mockElement = createMockElement('p', { children: [] });
            const detectedStyles = engine.detectStyles(mockElement);
            expect(detectedStyles.size).toBe(0);
        });

        test('should handle elements with only whitespace', () => {
            const mockElement = createMockElement('p', {
                children: [
                    createMockElement('span', {
                        classes: ['test'],
                        textContent: '   '
                    })
                ]
            });

            const detectedStyles = engine.detectStyles(mockElement);
            expect(detectedStyles.size).toBe(1);
        });
    });
});

// Helper function to create mock DOM elements for testing
function createMockElement(tagName, options = {}) {
    const element = {
        tagName: tagName.toUpperCase(),
        classList: new Set(options.classes || []),
        children: options.children || [],
        textContent: options.textContent || '',
        attributes: [],
        id: options.attributes?.id || '',
        outerHTML: `<${tagName}${formatAttributes(options)}></${tagName}>`,
        
        // Mock methods
        cloneNode: (deep) => createMockElement(tagName, options),
        setAttribute: (name, value) => {
            element.attributes[name] = value;
        },
        getAttribute: (name) => element.attributes[name],
        removeAttribute: (name) => {
            delete element.attributes[name];
        }
    };

    // Add attributes
    if (options.attributes) {
        Object.entries(options.attributes).forEach(([key, value]) => {
            element.attributes.push({ name: key, value: value });
        });
    }

    // Add classList methods
    element.classList.contains = (className) => element.classList.has(className);
    element.classList.add = (className) => element.classList.add(className);

    return element;
}

function formatAttributes(options) {
    let attrs = '';
    if (options.classes && options.classes.length > 0) {
        attrs += ` class="${options.classes.join(' ')}"`;
    }
    if (options.attributes) {
        Object.entries(options.attributes).forEach(([key, value]) => {
            attrs += ` ${key}="${value}"`;
        });
    }
    return attrs;
}