/**
 * LiveComponents Performance Profiler
 *
 * Provides detailed performance profiling with flamegraph and timeline visualization.
 *
 * Features:
 * - Real-time performance metrics collection
 * - Flamegraph generation for call stack visualization
 * - Timeline visualization for event sequencing
 * - Export to Chrome DevTools format
 * - Integration with Performance API
 *
 * @module PerformanceProfiler
 */

export class PerformanceProfiler {
    constructor(options = {}) {
        this.enabled = options.enabled ?? false;
        this.maxSamples = options.maxSamples ?? 1000;
        this.samplingInterval = options.samplingInterval ?? 10; // ms
        this.autoStart = options.autoStart ?? false;

        this.samples = [];
        this.timeline = [];
        this.marks = new Map();
        this.measures = new Map();
        this.isRecording = false;
        this.recordingStartTime = null;

        if (this.autoStart && this.enabled) {
            this.start();
        }
    }

    /**
     * Start performance profiling
     */
    start() {
        if (this.isRecording) {
            console.warn('[PerformanceProfiler] Already recording');
            return;
        }

        this.isRecording = true;
        this.recordingStartTime = performance.now();
        this.samples = [];
        this.timeline = [];

        console.log('[PerformanceProfiler] Started recording');
    }

    /**
     * Stop performance profiling
     * @returns {Object} Profiling results
     */
    stop() {
        if (!this.isRecording) {
            console.warn('[PerformanceProfiler] Not recording');
            return null;
        }

        this.isRecording = false;
        const duration = performance.now() - this.recordingStartTime;

        const results = {
            duration,
            samples: this.samples,
            timeline: this.timeline,
            marks: Array.from(this.marks.entries()),
            measures: Array.from(this.measures.entries()),
            summary: this.generateSummary()
        };

        console.log('[PerformanceProfiler] Stopped recording', results);
        return results;
    }

    /**
     * Mark a specific point in time
     * @param {string} name - Mark name
     * @param {Object} metadata - Additional metadata
     */
    mark(name, metadata = {}) {
        if (!this.isRecording && !this.enabled) return;

        const timestamp = performance.now();
        const mark = {
            name,
            timestamp,
            relativeTime: timestamp - this.recordingStartTime,
            metadata
        };

        this.marks.set(name, mark);

        // Also use native Performance API
        if (performance.mark) {
            performance.mark(name);
        }

        // Add to timeline
        this.timeline.push({
            type: 'mark',
            ...mark
        });
    }

    /**
     * Measure duration between two marks
     * @param {string} name - Measure name
     * @param {string} startMark - Start mark name
     * @param {string} endMark - End mark name (optional, defaults to now)
     */
    measure(name, startMark, endMark = null) {
        if (!this.isRecording && !this.enabled) return;

        const start = this.marks.get(startMark);
        if (!start) {
            console.warn(`[PerformanceProfiler] Start mark "${startMark}" not found`);
            return;
        }

        const endTime = endMark
            ? this.marks.get(endMark)?.timestamp
            : performance.now();

        if (!endTime) {
            console.warn(`[PerformanceProfiler] End mark "${endMark}" not found`);
            return;
        }

        const duration = endTime - start.timestamp;
        const measure = {
            name,
            startMark,
            endMark,
            duration,
            startTime: start.timestamp,
            endTime,
            relativeStartTime: start.relativeTime,
            relativeEndTime: endTime - this.recordingStartTime
        };

        this.measures.set(name, measure);

        // Native Performance API
        if (performance.measure && endMark) {
            try {
                performance.measure(name, startMark, endMark);
            } catch (e) {
                // Ignore if marks don't exist in native API
            }
        }

        // Add to timeline
        this.timeline.push({
            type: 'measure',
            ...measure
        });
    }

    /**
     * Record a sample for flamegraph
     * @param {string} functionName - Function name
     * @param {Array<string>} stackTrace - Call stack
     * @param {number} duration - Execution duration
     */
    sample(functionName, stackTrace = [], duration = 0) {
        if (!this.isRecording) return;

        if (this.samples.length >= this.maxSamples) {
            console.warn('[PerformanceProfiler] Max samples reached');
            return;
        }

        this.samples.push({
            timestamp: performance.now(),
            relativeTime: performance.now() - this.recordingStartTime,
            functionName,
            stackTrace,
            duration
        });
    }

    /**
     * Generate flamegraph data
     * @returns {Object} Flamegraph data structure
     */
    generateFlamegraph() {
        if (this.samples.length === 0) {
            return null;
        }

        // Build call tree from samples
        const root = {
            name: '(root)',
            value: 0,
            children: []
        };

        for (const sample of this.samples) {
            let currentNode = root;
            const stack = [sample.functionName, ...sample.stackTrace].reverse();

            for (const funcName of stack) {
                let child = currentNode.children.find(c => c.name === funcName);

                if (!child) {
                    child = {
                        name: funcName,
                        value: 0,
                        children: []
                    };
                    currentNode.children.push(child);
                }

                child.value += sample.duration || 1;
                currentNode = child;
            }
        }

        return root;
    }

    /**
     * Generate timeline data for visualization
     * @returns {Array} Timeline events
     */
    generateTimeline() {
        return this.timeline.map(event => ({
            ...event,
            category: this.categorizeEvent(event),
            color: this.getEventColor(event)
        }));
    }

    /**
     * Generate summary statistics
     * @returns {Object} Summary data
     */
    generateSummary() {
        const durations = Array.from(this.measures.values()).map(m => m.duration);

        if (durations.length === 0) {
            return {
                totalMeasures: 0,
                totalSamples: this.samples.length,
                totalDuration: 0
            };
        }

        const totalDuration = durations.reduce((sum, d) => sum + d, 0);
        const avgDuration = totalDuration / durations.length;
        const maxDuration = Math.max(...durations);
        const minDuration = Math.min(...durations);

        // Calculate percentiles
        const sortedDurations = [...durations].sort((a, b) => a - b);
        const p50 = sortedDurations[Math.floor(sortedDurations.length * 0.5)];
        const p90 = sortedDurations[Math.floor(sortedDurations.length * 0.9)];
        const p99 = sortedDurations[Math.floor(sortedDurations.length * 0.99)];

        return {
            totalMeasures: this.measures.size,
            totalSamples: this.samples.length,
            totalDuration,
            avgDuration,
            maxDuration,
            minDuration,
            percentiles: { p50, p90, p99 }
        };
    }

    /**
     * Export to Chrome DevTools Performance format
     * @returns {Object} Chrome DevTools trace event format
     */
    exportToChromeTrace() {
        const events = [];

        // Add marks
        for (const [name, mark] of this.marks.entries()) {
            events.push({
                name,
                cat: 'mark',
                ph: 'R', // Instant event
                ts: mark.timestamp * 1000, // microseconds
                pid: 1,
                tid: 1,
                args: mark.metadata
            });
        }

        // Add measures as duration events
        for (const [name, measure] of this.measures.entries()) {
            events.push({
                name,
                cat: 'measure',
                ph: 'B', // Begin
                ts: measure.startTime * 1000,
                pid: 1,
                tid: 1
            });
            events.push({
                name,
                cat: 'measure',
                ph: 'E', // End
                ts: measure.endTime * 1000,
                pid: 1,
                tid: 1
            });
        }

        // Add samples
        for (const sample of this.samples) {
            events.push({
                name: sample.functionName,
                cat: 'sample',
                ph: 'X', // Complete event
                ts: sample.timestamp * 1000,
                dur: (sample.duration || 1) * 1000,
                pid: 1,
                tid: 1,
                args: {
                    stackTrace: sample.stackTrace
                }
            });
        }

        return {
            traceEvents: events.sort((a, b) => a.ts - b.ts),
            displayTimeUnit: 'ms',
            metadata: {
                'clock-domain': 'PERFORMANCE_NOW'
            }
        };
    }

    /**
     * Categorize timeline event
     * @private
     */
    categorizeEvent(event) {
        if (event.type === 'mark') {
            if (event.name.includes('action')) return 'action';
            if (event.name.includes('render')) return 'render';
            if (event.name.includes('state')) return 'state';
            return 'other';
        }

        if (event.type === 'measure') {
            if (event.duration > 100) return 'slow';
            if (event.duration > 16) return 'normal';
            return 'fast';
        }

        return 'unknown';
    }

    /**
     * Get color for event visualization
     * @private
     */
    getEventColor(event) {
        const colorMap = {
            action: '#4CAF50',
            render: '#2196F3',
            state: '#FF9800',
            slow: '#F44336',
            normal: '#FFC107',
            fast: '#8BC34A',
            other: '#9E9E9E',
            unknown: '#607D8B'
        };

        return colorMap[this.categorizeEvent(event)] || '#607D8B';
    }

    /**
     * Clear all profiling data
     */
    clear() {
        this.samples = [];
        this.timeline = [];
        this.marks.clear();
        this.measures.clear();
        this.recordingStartTime = null;

        // Clear native Performance API
        if (performance.clearMarks) {
            performance.clearMarks();
        }
        if (performance.clearMeasures) {
            performance.clearMeasures();
        }
    }

    /**
     * Get current profiling status
     * @returns {Object} Status information
     */
    getStatus() {
        return {
            enabled: this.enabled,
            isRecording: this.isRecording,
            samplesCount: this.samples.length,
            marksCount: this.marks.size,
            measuresCount: this.measures.size,
            timelineEventsCount: this.timeline.length,
            recordingDuration: this.isRecording
                ? performance.now() - this.recordingStartTime
                : 0
        };
    }
}

/**
 * LiveComponents Performance Integration
 *
 * Automatic profiling for LiveComponents actions and lifecycle events
 */
export class LiveComponentsProfiler extends PerformanceProfiler {
    constructor(component, options = {}) {
        super(options);
        this.component = component;
        this.actionCallDepth = 0;

        if (this.enabled) {
            this.instrumentComponent();
        }
    }

    /**
     * Instrument component for automatic profiling
     * @private
     */
    instrumentComponent() {
        // Hook into action calls
        const originalCall = this.component.call.bind(this.component);
        this.component.call = (actionName, params, options) => {
            this.actionCallDepth++;
            const markName = `action:${actionName}:${this.actionCallDepth}`;

            this.mark(`${markName}:start`, {
                actionName,
                params,
                depth: this.actionCallDepth
            });

            const result = originalCall(actionName, params, options);

            // Handle promise results
            if (result instanceof Promise) {
                return result.finally(() => {
                    this.mark(`${markName}:end`);
                    this.measure(`action:${actionName}`, `${markName}:start`, `${markName}:end`);
                    this.actionCallDepth--;
                });
            }

            this.mark(`${markName}:end`);
            this.measure(`action:${actionName}`, `${markName}:start`, `${markName}:end`);
            this.actionCallDepth--;

            return result;
        };

        // Hook into state updates
        if (this.component.state) {
            const originalSet = this.component.state.set.bind(this.component.state);
            this.component.state.set = (key, value) => {
                this.mark(`state:set:${key}`, { key, value });
                return originalSet(key, value);
            };
        }

        // Hook into renders
        if (this.component.render) {
            const originalRender = this.component.render.bind(this.component);
            this.component.render = () => {
                this.mark('render:start');
                const result = originalRender();

                if (result instanceof Promise) {
                    return result.finally(() => {
                        this.mark('render:end');
                        this.measure('render', 'render:start', 'render:end');
                    });
                }

                this.mark('render:end');
                this.measure('render', 'render:start', 'render:end');
                return result;
            };
        }
    }
}

// Export default instance
export default PerformanceProfiler;
