// modules/api-manager/ObserverManager.js
import { Logger } from '../../core/logger.js';

/**
 * Observer APIs Manager - Intersection, Resize, Mutation, Performance Observers
 */
export class ObserverManager {
    constructor(config = {}) {
        this.config = config;
        this.activeObservers = new Map();
        this.observerInstances = new Map();
        
        Logger.info('[ObserverManager] Initialized with support:', {
            intersection: 'IntersectionObserver' in window,
            resize: 'ResizeObserver' in window,
            mutation: 'MutationObserver' in window,
            performance: 'PerformanceObserver' in window
        });
    }
    
    /**
     * Intersection Observer - Viewport intersection detection
     */
    intersection(elements, callback, options = {}) {
        if (!('IntersectionObserver' in window)) {
            Logger.warn('[ObserverManager] IntersectionObserver not supported');
            return this.createFallbackObserver('intersection', elements, callback);
        }
        
        const defaultOptions = {
            root: null,
            rootMargin: '50px',
            threshold: [0, 0.1, 0.5, 1.0],
            ...options
        };
        
        const observerId = this.generateId('intersection');
        const observer = new IntersectionObserver((entries, obs) => {
            const processedEntries = entries.map(entry => ({
                element: entry.target,
                isIntersecting: entry.isIntersecting,
                intersectionRatio: entry.intersectionRatio,
                boundingClientRect: entry.boundingClientRect,
                rootBounds: entry.rootBounds,
                intersectionRect: entry.intersectionRect,
                time: entry.time,
                // Enhanced data
                visibility: this.calculateVisibility(entry),
                direction: this.getScrollDirection(entry),
                position: this.getElementPosition(entry)
            }));
            
            callback(processedEntries, obs);
        }, defaultOptions);
        
        // Observe elements
        const elementList = Array.isArray(elements) ? elements : [elements];
        elementList.forEach(el => {
            if (el instanceof Element) observer.observe(el);
        });
        
        this.observerInstances.set(observerId, observer);
        this.activeObservers.set(observerId, {
            type: 'intersection',
            elements: elementList,
            callback,
            options: defaultOptions
        });
        
        Logger.info(`[ObserverManager] IntersectionObserver created: ${observerId}`);
        
        return {
            id: observerId,
            observer,
            unobserve: (element) => observer.unobserve(element),
            disconnect: () => this.disconnect(observerId),
            updateThreshold: (threshold) => this.updateIntersectionThreshold(observerId, threshold)
        };
    }
    
    /**
     * Resize Observer - Element resize detection
     */
    resize(elements, callback, options = {}) {
        if (!('ResizeObserver' in window)) {
            Logger.warn('[ObserverManager] ResizeObserver not supported');
            return this.createFallbackObserver('resize', elements, callback);
        }
        
        const observerId = this.generateId('resize');
        const observer = new ResizeObserver((entries) => {
            const processedEntries = entries.map(entry => ({
                element: entry.target,
                contentRect: entry.contentRect,
                borderBoxSize: entry.borderBoxSize,
                contentBoxSize: entry.contentBoxSize,
                devicePixelContentBoxSize: entry.devicePixelContentBoxSize,
                // Enhanced data
                dimensions: {
                    width: entry.contentRect.width,
                    height: entry.contentRect.height,
                    aspectRatio: entry.contentRect.width / entry.contentRect.height
                },
                deltaSize: this.calculateDeltaSize(entry),
                breakpoint: this.detectBreakpoint(entry.contentRect.width)
            }));
            
            callback(processedEntries);
        });
        
        const elementList = Array.isArray(elements) ? elements : [elements];
        elementList.forEach(el => {
            if (el instanceof Element) observer.observe(el);
        });
        
        this.observerInstances.set(observerId, observer);
        this.activeObservers.set(observerId, {
            type: 'resize',
            elements: elementList,
            callback,
            options
        });
        
        Logger.info(`[ObserverManager] ResizeObserver created: ${observerId}`);
        
        return {
            id: observerId,
            observer,
            unobserve: (element) => observer.unobserve(element),
            disconnect: () => this.disconnect(observerId)
        };
    }
    
    /**
     * Mutation Observer - DOM change detection
     */
    mutation(target, callback, options = {}) {
        if (!('MutationObserver' in window)) {
            Logger.warn('[ObserverManager] MutationObserver not supported');
            return null;
        }
        
        const defaultOptions = {
            childList: true,
            attributes: true,
            subtree: true,
            attributeOldValue: true,
            characterDataOldValue: true,
            ...options
        };
        
        const observerId = this.generateId('mutation');
        const observer = new MutationObserver((mutations) => {
            const processedMutations = mutations.map(mutation => ({
                type: mutation.type,
                target: mutation.target,
                addedNodes: Array.from(mutation.addedNodes),
                removedNodes: Array.from(mutation.removedNodes),
                attributeName: mutation.attributeName,
                attributeNamespace: mutation.attributeNamespace,
                oldValue: mutation.oldValue,
                // Enhanced data
                summary: this.summarizeMutation(mutation),
                impact: this.assessMutationImpact(mutation)
            }));
            
            callback(processedMutations);
        });
        
        observer.observe(target, defaultOptions);
        
        this.observerInstances.set(observerId, observer);
        this.activeObservers.set(observerId, {
            type: 'mutation',
            target,
            callback,
            options: defaultOptions
        });
        
        Logger.info(`[ObserverManager] MutationObserver created: ${observerId}`);
        
        return {
            id: observerId,
            observer,
            disconnect: () => this.disconnect(observerId),
            takeRecords: () => observer.takeRecords()
        };
    }
    
    /**
     * Performance Observer - Performance metrics monitoring
     */
    performance(callback, options = {}) {
        if (!('PerformanceObserver' in window)) {
            Logger.warn('[ObserverManager] PerformanceObserver not supported');
            return null;
        }
        
        const defaultOptions = {
            entryTypes: ['measure', 'navigation', 'paint', 'largest-contentful-paint'],
            buffered: true,
            ...options
        };
        
        const observerId = this.generateId('performance');
        const observer = new PerformanceObserver((list) => {
            const entries = list.getEntries().map(entry => ({
                name: entry.name,
                entryType: entry.entryType,
                startTime: entry.startTime,
                duration: entry.duration,
                // Enhanced data based on entry type
                details: this.enhancePerformanceEntry(entry),
                timestamp: Date.now()
            }));
            
            callback(entries);
        });
        
        observer.observe(defaultOptions);
        
        this.observerInstances.set(observerId, observer);
        this.activeObservers.set(observerId, {
            type: 'performance',
            callback,
            options: defaultOptions
        });
        
        Logger.info(`[ObserverManager] PerformanceObserver created: ${observerId}`);
        
        return {
            id: observerId,
            observer,
            disconnect: () => this.disconnect(observerId),
            takeRecords: () => observer.takeRecords()
        };
    }
    
    /**
     * Lazy Loading Helper - Uses IntersectionObserver
     */
    lazyLoad(selector = 'img[data-src], iframe[data-src]', options = {}) {
        const elements = document.querySelectorAll(selector);
        
        return this.intersection(elements, (entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    const element = entry.element;
                    
                    // Load image or iframe
                    if (element.dataset.src) {
                        element.src = element.dataset.src;
                        delete element.dataset.src;
                    }
                    
                    // Load srcset if available
                    if (element.dataset.srcset) {
                        element.srcset = element.dataset.srcset;
                        delete element.dataset.srcset;
                    }
                    
                    // Add loaded class
                    element.classList.add('loaded');
                    
                    // Stop observing this element
                    entry.observer.unobserve(element);
                    
                    Logger.info('[ObserverManager] Lazy loaded:', element.src);
                }
            });
        }, {
            rootMargin: '100px',
            ...options
        });
    }
    
    /**
     * Scroll Trigger Helper - Uses IntersectionObserver
     */
    scrollTrigger(elements, callback, options = {}) {
        return this.intersection(elements, (entries) => {
            entries.forEach(entry => {
                const triggerData = {
                    element: entry.element,
                    progress: entry.intersectionRatio,
                    isVisible: entry.isIntersecting,
                    direction: entry.direction,
                    position: entry.position
                };
                
                callback(triggerData);
            });
        }, {
            threshold: this.createThresholdArray(options.steps || 10),
            ...options
        });
    }
    
    /**
     * Viewport Detection Helper
     */
    viewport(callback, options = {}) {
        const viewportElement = document.createElement('div');
        viewportElement.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            visibility: hidden;
        `;
        document.body.appendChild(viewportElement);
        
        return this.resize([viewportElement], (entries) => {
            const viewport = entries[0];
            callback({
                width: viewport.dimensions.width,
                height: viewport.dimensions.height,
                aspectRatio: viewport.dimensions.aspectRatio,
                orientation: viewport.dimensions.width > viewport.dimensions.height ? 'landscape' : 'portrait',
                breakpoint: viewport.breakpoint
            });
        }, options);
    }
    
    // Helper Methods
    
    generateId(type) {
        return `${type}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    }
    
    disconnect(observerId) {
        const observer = this.observerInstances.get(observerId);
        if (observer) {
            observer.disconnect();
            this.observerInstances.delete(observerId);
            this.activeObservers.delete(observerId);
            Logger.info(`[ObserverManager] Observer disconnected: ${observerId}`);
        }
    }
    
    disconnectAll() {
        this.observerInstances.forEach((observer, id) => {
            observer.disconnect();
        });
        this.observerInstances.clear();
        this.activeObservers.clear();
        Logger.info('[ObserverManager] All observers disconnected');
    }
    
    calculateVisibility(entry) {
        if (!entry.isIntersecting) return 0;
        
        const visibleArea = entry.intersectionRect.width * entry.intersectionRect.height;
        const totalArea = entry.boundingClientRect.width * entry.boundingClientRect.height;
        
        return totalArea > 0 ? Math.round((visibleArea / totalArea) * 100) : 0;
    }
    
    getScrollDirection(entry) {
        // This would need to store previous positions to determine direction
        // For now, return based on intersection ratio change
        return entry.intersectionRatio > 0.5 ? 'down' : 'up';
    }
    
    getElementPosition(entry) {
        const rect = entry.boundingClientRect;
        const viewportHeight = window.innerHeight;
        
        if (rect.top < 0 && rect.bottom > 0) return 'entering-top';
        if (rect.top < viewportHeight && rect.bottom > viewportHeight) return 'entering-bottom';
        if (rect.top >= 0 && rect.bottom <= viewportHeight) return 'visible';
        return 'hidden';
    }
    
    calculateDeltaSize(entry) {
        // Would need to store previous sizes to calculate delta
        return { width: 0, height: 0 };
    }
    
    detectBreakpoint(width) {
        if (width < 576) return 'xs';
        if (width < 768) return 'sm';
        if (width < 992) return 'md';
        if (width < 1200) return 'lg';
        return 'xl';
    }
    
    summarizeMutation(mutation) {
        return `${mutation.type} on ${mutation.target.tagName}`;
    }
    
    assessMutationImpact(mutation) {
        // Simple impact assessment
        if (mutation.type === 'childList') {
            return mutation.addedNodes.length + mutation.removedNodes.length > 5 ? 'high' : 'low';
        }
        return 'medium';
    }
    
    enhancePerformanceEntry(entry) {
        const details = { raw: entry };
        
        switch (entry.entryType) {
            case 'navigation':
                details.loadTime = entry.loadEventEnd - entry.navigationStart;
                details.domContentLoaded = entry.domContentLoadedEventEnd - entry.navigationStart;
                break;
            case 'paint':
                details.paintType = entry.name;
                break;
            case 'largest-contentful-paint':
                details.element = entry.element;
                details.url = entry.url;
                break;
        }
        
        return details;
    }
    
    createThresholdArray(steps) {
        const thresholds = [];
        for (let i = 0; i <= steps; i++) {
            thresholds.push(i / steps);
        }
        return thresholds;
    }
    
    createFallbackObserver(type, elements, callback) {
        Logger.warn(`[ObserverManager] Creating fallback for ${type}Observer`);
        
        // Simple polling fallback
        const fallbackId = this.generateId(`fallback_${type}`);
        let intervalId;
        
        switch (type) {
            case 'intersection':
                intervalId = setInterval(() => {
                    const elementList = Array.isArray(elements) ? elements : [elements];
                    const entries = elementList.map(el => ({
                        element: el,
                        isIntersecting: this.isElementInViewport(el),
                        intersectionRatio: this.calculateIntersectionRatio(el)
                    }));
                    callback(entries);
                }, 100);
                break;
        }
        
        return {
            id: fallbackId,
            disconnect: () => {
                if (intervalId) clearInterval(intervalId);
            }
        };
    }
    
    isElementInViewport(el) {
        const rect = el.getBoundingClientRect();
        return (
            rect.top >= 0 &&
            rect.left >= 0 &&
            rect.bottom <= window.innerHeight &&
            rect.right <= window.innerWidth
        );
    }
    
    calculateIntersectionRatio(el) {
        const rect = el.getBoundingClientRect();
        const viewportHeight = window.innerHeight;
        const viewportWidth = window.innerWidth;
        
        const visibleHeight = Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0);
        const visibleWidth = Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0);
        
        if (visibleHeight <= 0 || visibleWidth <= 0) return 0;
        
        const visibleArea = visibleHeight * visibleWidth;
        const totalArea = rect.height * rect.width;
        
        return totalArea > 0 ? visibleArea / totalArea : 0;
    }
    
    getActiveObservers() {
        return Array.from(this.activeObservers.entries()).map(([id, data]) => ({
            id,
            ...data
        }));
    }
}