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

/**
 * Worker APIs Manager - Web Workers, Service Workers, Shared Workers
 */
export class WorkerManager {
    constructor(config = {}) {
        this.config = config;
        this.activeWorkers = new Map();
        this.messageHandlers = new Map();
        this.workerScripts = new Map();
        
        // Check API support
        this.support = {
            webWorkers: 'Worker' in window,
            serviceWorker: 'serviceWorker' in navigator,
            sharedWorker: 'SharedWorker' in window,
            offscreenCanvas: 'OffscreenCanvas' in window
        };
        
        Logger.info('[WorkerManager] Initialized with support:', this.support);
    }
    
    /**
     * Web Workers for background processing
     */
    web = {
        // Create a new Web Worker
        create: (script, options = {}) => {
            if (!this.support.webWorkers) {
                throw new Error('Web Workers not supported');
            }
            
            let worker;
            const workerId = this.generateId('worker');
            
            try {
                // Handle different script types
                if (typeof script === 'string') {
                    // URL string
                    worker = new Worker(script, options);
                } else if (typeof script === 'function') {
                    // Function to blob
                    const blob = this.createWorkerBlob(script);
                    worker = new Worker(URL.createObjectURL(blob), options);
                } else if (script instanceof Blob) {
                    // Blob directly
                    worker = new Worker(URL.createObjectURL(script), options);
                } else {
                    throw new Error('Invalid script type');
                }
                
                // Enhanced worker wrapper
                const workerWrapper = {
                    id: workerId,
                    worker,
                    
                    // Send message to worker
                    send: (message, transfer = null) => {
                        if (transfer) {
                            worker.postMessage(message, transfer);
                        } else {
                            worker.postMessage(message);
                        }
                        Logger.info(`[WorkerManager] Message sent to worker: ${workerId}`);
                    },
                    
                    // Listen for messages
                    onMessage: (callback) => {
                        worker.addEventListener('message', (event) => {
                            callback(event.data, event);
                        });
                    },
                    
                    // Handle errors
                    onError: (callback) => {
                        worker.addEventListener('error', callback);
                        worker.addEventListener('messageerror', callback);
                    },
                    
                    // Terminate worker
                    terminate: () => {
                        worker.terminate();
                        this.activeWorkers.delete(workerId);
                        Logger.info(`[WorkerManager] Worker terminated: ${workerId}`);
                    },
                    
                    // Execute function in worker
                    execute: (fn, data = null) => {
                        return new Promise((resolve, reject) => {
                            const messageId = this.generateId('msg');
                            
                            const handler = (event) => {
                                if (event.data.id === messageId) {
                                    worker.removeEventListener('message', handler);
                                    
                                    if (event.data.error) {
                                        reject(new Error(event.data.error));
                                    } else {
                                        resolve(event.data.result);
                                    }
                                }
                            };
                            
                            worker.addEventListener('message', handler);
                            
                            worker.postMessage({
                                id: messageId,
                                type: 'execute',
                                function: fn.toString(),
                                data
                            });
                            
                            // Timeout after 30 seconds
                            setTimeout(() => {
                                worker.removeEventListener('message', handler);
                                reject(new Error('Worker execution timeout'));
                            }, 30000);
                        });
                    }
                };
                
                this.activeWorkers.set(workerId, workerWrapper);
                Logger.info(`[WorkerManager] Web Worker created: ${workerId}`);
                
                return workerWrapper;
                
            } catch (error) {
                Logger.error('[WorkerManager] Worker creation failed:', error);
                throw error;
            }
        },
        
        // Create worker pool for parallel processing
        createPool: (script, poolSize = navigator.hardwareConcurrency || 4, options = {}) => {
            const workers = [];
            
            for (let i = 0; i < poolSize; i++) {
                workers.push(this.web.create(script, options));
            }
            
            let currentWorker = 0;
            
            const pool = {
                workers,
                
                // Execute task on next available worker
                execute: async (fn, data = null) => {
                    const worker = workers[currentWorker];
                    currentWorker = (currentWorker + 1) % workers.length;
                    
                    return worker.execute(fn, data);
                },
                
                // Broadcast message to all workers
                broadcast: (message) => {
                    workers.forEach(worker => {
                        worker.send(message);
                    });
                },
                
                // Terminate all workers
                terminate: () => {
                    workers.forEach(worker => {
                        worker.terminate();
                    });
                    workers.length = 0;
                    Logger.info('[WorkerManager] Worker pool terminated');
                }
            };
            
            Logger.info(`[WorkerManager] Worker pool created with ${poolSize} workers`);
            return pool;
        },
        
        // Common worker tasks
        tasks: {
            // Heavy computation
            compute: (fn, data) => {
                const workerCode = `
                    self.addEventListener('message', function(e) {
                        const { id, function: fnString, data } = e.data;
                        
                        try {
                            const fn = new Function('return ' + fnString)();
                            const result = fn(data);
                            self.postMessage({ id, result });
                        } catch (error) {
                            self.postMessage({ id, error: error.message });
                        }
                    });
                `;
                
                const worker = this.web.create(workerCode);
                return worker.execute(fn, data);
            },
            
            // Image processing
            processImage: (imageData, filters) => {
                const workerCode = `
                    self.addEventListener('message', function(e) {
                        const { id, data: { imageData, filters } } = e.data;
                        
                        try {
                            const pixels = imageData.data;
                            
                            for (let i = 0; i < pixels.length; i += 4) {
                                // Apply filters
                                if (filters.brightness) {
                                    pixels[i] *= filters.brightness;     // R
                                    pixels[i + 1] *= filters.brightness; // G
                                    pixels[i + 2] *= filters.brightness; // B
                                }
                                
                                if (filters.contrast) {
                                    const factor = (259 * (filters.contrast * 255 + 255)) / (255 * (259 - filters.contrast * 255));
                                    pixels[i] = factor * (pixels[i] - 128) + 128;
                                    pixels[i + 1] = factor * (pixels[i + 1] - 128) + 128;
                                    pixels[i + 2] = factor * (pixels[i + 2] - 128) + 128;
                                }
                                
                                if (filters.grayscale) {
                                    const gray = 0.299 * pixels[i] + 0.587 * pixels[i + 1] + 0.114 * pixels[i + 2];
                                    pixels[i] = gray;
                                    pixels[i + 1] = gray;
                                    pixels[i + 2] = gray;
                                }
                            }
                            
                            self.postMessage({ id, result: imageData }, [imageData.data.buffer]);
                        } catch (error) {
                            self.postMessage({ id, error: error.message });
                        }
                    });
                `;
                
                const worker = this.web.create(workerCode);
                return worker.execute(null, { imageData, filters });
            },
            
            // Data processing
            processData: (data, processor) => {
                const workerCode = `
                    self.addEventListener('message', function(e) {
                        const { id, data, function: processorString } = e.data;
                        
                        try {
                            const processor = new Function('return ' + processorString)();
                            const result = data.map(processor);
                            self.postMessage({ id, result });
                        } catch (error) {
                            self.postMessage({ id, error: error.message });
                        }
                    });
                `;
                
                const worker = this.web.create(workerCode);
                return worker.execute(processor, data);
            }
        }
    };
    
    /**
     * Service Workers for caching and background sync
     */
    service = {
        // Register service worker
        register: async (scriptURL, options = {}) => {
            if (!this.support.serviceWorker) {
                throw new Error('Service Worker not supported');
            }
            
            try {
                const registration = await navigator.serviceWorker.register(scriptURL, options);
                
                Logger.info('[WorkerManager] Service Worker registered:', scriptURL);
                
                // Enhanced registration wrapper
                return {
                    registration,
                    scope: registration.scope,
                    
                    // Update service worker
                    update: () => registration.update(),
                    
                    // Unregister service worker
                    unregister: () => registration.unregister(),
                    
                    // Send message to service worker
                    postMessage: (message) => {
                        if (registration.active) {
                            registration.active.postMessage(message);
                        }
                    },
                    
                    // Listen for updates
                    onUpdate: (callback) => {
                        registration.addEventListener('updatefound', () => {
                            const newWorker = registration.installing;
                            
                            newWorker.addEventListener('statechange', () => {
                                if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
                                    callback(newWorker);
                                }
                            });
                        });
                    },
                    
                    // Check for updates
                    checkForUpdates: () => {
                        return registration.update();
                    }
                };
            } catch (error) {
                Logger.error('[WorkerManager] Service Worker registration failed:', error);
                throw error;
            }
        },
        
        // Get registration
        getRegistration: async (scope = '/') => {
            if (!this.support.serviceWorker) {
                return null;
            }
            
            try {
                return await navigator.serviceWorker.getRegistration(scope);
            } catch (error) {
                Logger.error('[WorkerManager] Get registration failed:', error);
                return null;
            }
        },
        
        // Get all registrations
        getRegistrations: async () => {
            if (!this.support.serviceWorker) {
                return [];
            }
            
            try {
                return await navigator.serviceWorker.getRegistrations();
            } catch (error) {
                Logger.error('[WorkerManager] Get registrations failed:', error);
                return [];
            }
        }
    };
    
    /**
     * Shared Workers for cross-tab communication
     */
    shared = {
        // Create shared worker
        create: (script, options = {}) => {
            if (!this.support.sharedWorker) {
                throw new Error('Shared Worker not supported');
            }
            
            try {
                const sharedWorker = new SharedWorker(script, options);
                const port = sharedWorker.port;
                const workerId = this.generateId('shared');
                
                // Start the port
                port.start();
                
                const workerWrapper = {
                    id: workerId,
                    worker: sharedWorker,
                    port,
                    
                    // Send message
                    send: (message, transfer = null) => {
                        if (transfer) {
                            port.postMessage(message, transfer);
                        } else {
                            port.postMessage(message);
                        }
                    },
                    
                    // Listen for messages
                    onMessage: (callback) => {
                        port.addEventListener('message', (event) => {
                            callback(event.data, event);
                        });
                    },
                    
                    // Handle errors
                    onError: (callback) => {
                        sharedWorker.addEventListener('error', callback);
                        port.addEventListener('messageerror', callback);
                    },
                    
                    // Close connection
                    close: () => {
                        port.close();
                        this.activeWorkers.delete(workerId);
                        Logger.info(`[WorkerManager] Shared Worker closed: ${workerId}`);
                    }
                };
                
                this.activeWorkers.set(workerId, workerWrapper);
                Logger.info(`[WorkerManager] Shared Worker created: ${workerId}`);
                
                return workerWrapper;
            } catch (error) {
                Logger.error('[WorkerManager] Shared Worker creation failed:', error);
                throw error;
            }
        }
    };
    
    /**
     * Offscreen Canvas for worker-based rendering
     */
    offscreen = {
        // Create offscreen canvas worker
        create: (canvas, workerScript = null) => {
            if (!this.support.offscreenCanvas) {
                throw new Error('Offscreen Canvas not supported');
            }
            
            try {
                const offscreenCanvas = canvas.transferControlToOffscreen();
                
                const defaultWorkerScript = `
                    self.addEventListener('message', function(e) {
                        const { canvas, type, data } = e.data;
                        
                        if (type === 'init') {
                            self.canvas = canvas;
                            self.ctx = canvas.getContext('2d');
                        }
                        
                        if (type === 'draw' && self.ctx) {
                            // Basic drawing operations
                            const { operations } = data;
                            
                            operations.forEach(op => {
                                switch (op.type) {
                                    case 'fillRect':
                                        self.ctx.fillRect(...op.args);
                                        break;
                                    case 'strokeRect':
                                        self.ctx.strokeRect(...op.args);
                                        break;
                                    case 'fillText':
                                        self.ctx.fillText(...op.args);
                                        break;
                                    case 'setFillStyle':
                                        self.ctx.fillStyle = op.value;
                                        break;
                                    case 'setStrokeStyle':
                                        self.ctx.strokeStyle = op.value;
                                        break;
                                }
                            });
                        }
                    });
                `;
                
                const script = workerScript || defaultWorkerScript;
                const worker = this.web.create(script);
                
                // Initialize worker with canvas
                worker.send({
                    type: 'init',
                    canvas: offscreenCanvas
                }, [offscreenCanvas]);
                
                return {
                    worker,
                    
                    // Draw operations
                    draw: (operations) => {
                        worker.send({
                            type: 'draw',
                            data: { operations }
                        });
                    },
                    
                    // Send custom message
                    send: (message) => worker.send(message),
                    
                    // Listen for messages
                    onMessage: (callback) => worker.onMessage(callback),
                    
                    // Terminate worker
                    terminate: () => worker.terminate()
                };
                
            } catch (error) {
                Logger.error('[WorkerManager] Offscreen Canvas creation failed:', error);
                throw error;
            }
        }
    };
    
    // Helper methods
    
    createWorkerBlob(fn) {
        const code = `
            (function() {
                const workerFunction = ${fn.toString()};
                
                if (typeof workerFunction === 'function') {
                    // If function expects to be called immediately
                    if (workerFunction.length === 0) {
                        workerFunction();
                    }
                }
                
                // Standard worker message handling
                self.addEventListener('message', function(e) {
                    if (e.data.type === 'execute' && e.data.function) {
                        try {
                            const fn = new Function('return ' + e.data.function)();
                            const result = fn(e.data.data);
                            self.postMessage({ 
                                id: e.data.id, 
                                result 
                            });
                        } catch (error) {
                            self.postMessage({ 
                                id: e.data.id, 
                                error: error.message 
                            });
                        }
                    }
                });
            })();
        `;
        
        return new Blob([code], { type: 'application/javascript' });
    }
    
    generateId(prefix = 'worker') {
        return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    }
    
    /**
     * Terminate all active workers
     */
    terminateAll() {
        this.activeWorkers.forEach(worker => {
            if (worker.terminate) {
                worker.terminate();
            } else if (worker.close) {
                worker.close();
            }
        });
        this.activeWorkers.clear();
        Logger.info('[WorkerManager] All workers terminated');
    }
    
    /**
     * Get worker statistics
     */
    getStats() {
        const workers = Array.from(this.activeWorkers.values());
        
        return {
            total: workers.length,
            byType: workers.reduce((acc, worker) => {
                const type = worker.id.split('_')[0];
                acc[type] = (acc[type] || 0) + 1;
                return acc;
            }, {}),
            support: this.support,
            hardwareConcurrency: navigator.hardwareConcurrency || 'unknown'
        };
    }
    
    /**
     * Create common worker utilities
     */
    utils = {
        // Benchmark function performance
        benchmark: async (fn, data, iterations = 1000) => {
            const worker = this.web.create(() => {
                self.addEventListener('message', (e) => {
                    const { id, function: fnString, data, iterations } = e.data;
                    
                    try {
                        const fn = new Function('return ' + fnString)();
                        const start = performance.now();
                        
                        for (let i = 0; i < iterations; i++) {
                            fn(data);
                        }
                        
                        const end = performance.now();
                        const totalTime = end - start;
                        const avgTime = totalTime / iterations;
                        
                        self.postMessage({
                            id,
                            result: {
                                totalTime,
                                avgTime,
                                iterations,
                                opsPerSecond: 1000 / avgTime
                            }
                        });
                    } catch (error) {
                        self.postMessage({ id, error: error.message });
                    }
                });
            });
            
            const result = await worker.execute(fn, data, iterations);
            worker.terminate();
            
            return result;
        },
        
        // Parallel array processing
        parallelMap: async (array, fn, chunkSize = null) => {
            const chunks = chunkSize || Math.ceil(array.length / (navigator.hardwareConcurrency || 4));
            const pool = this.web.createPool(() => {
                self.addEventListener('message', (e) => {
                    const { id, data: { chunk, function: fnString } } = e.data;
                    
                    try {
                        const fn = new Function('return ' + fnString)();
                        const result = chunk.map(fn);
                        self.postMessage({ id, result });
                    } catch (error) {
                        self.postMessage({ id, error: error.message });
                    }
                });
            });
            
            const promises = [];
            
            for (let i = 0; i < array.length; i += chunks) {
                const chunk = array.slice(i, i + chunks);
                promises.push(pool.execute(fn, chunk));
            }
            
            const results = await Promise.all(promises);
            pool.terminate();
            
            return results.flat();
        }
    };
}