/**
 * Interactive Gantt Chart for Trace Visualization
 *
 * Features:
 * - Hierarchical span tree with expand/collapse
 * - Interactive zoom and pan
 * - Time scale with markers
 * - Critical path highlighting
 * - Keyboard shortcuts
 * - Hover tooltips
 */

/* eslint-disable no-unused-vars */
class GanttChart {
    constructor(containerId, traceData) {
        this.container = document.getElementById(containerId);
        this.trace = traceData;
        this.zoomLevel = 1;
        this.panOffset = 0;
        this.collapsedSpans = new Set();
        this.spans = this.buildSpanTree(traceData.spans);
        this.criticalPath = this.calculateCriticalPath();

        this.init();
    }

    /**
     * Build hierarchical span tree from flat span list
     */
    buildSpanTree(spans) {
        const spanMap = new Map();
        const roots = [];

        // Create map of all spans
        spans.forEach((span) => {
            spanMap.set(span.span_id, {
                ...span,
                children: [],
                depth: 0,
            });
        });

        // Build tree structure
        spans.forEach((span) => {
            const node = spanMap.get(span.span_id);
            if (span.parent_span_id && spanMap.has(span.parent_span_id)) {
                const parent = spanMap.get(span.parent_span_id);
                parent.children.push(node);
                node.depth = parent.depth + 1;
            } else {
                roots.push(node);
            }
        });

        // Flatten tree for rendering (depth-first)
        const flatten = (node) => {
            const result = [node];
            if (!this.collapsedSpans.has(node.span_id)) {
                node.children.forEach((child) => {
                    result.push(...flatten(child));
                });
            }
            return result;
        };

        return roots.flatMap(flatten);
    }

    /**
     * Calculate critical path (slowest sequential chain)
     */
    calculateCriticalPath() {
        const criticalSpans = new Set();

        const findCriticalPath = (spans) => {
            if (spans.length === 0) {
                return [];
            }

            // Find span with longest duration + children duration
            let maxPath = [];
            let maxDuration = 0;

            spans.forEach((span) => {
                const childPath = findCriticalPath(span.children);
                const totalDuration =
                    (span.duration_ms || 0) +
                    childPath.reduce((sum, s) => sum + (s.duration_ms || 0), 0);

                if (totalDuration > maxDuration) {
                    maxDuration = totalDuration;
                    maxPath = [span, ...childPath];
                }
            });

            return maxPath;
        };

        const roots = this.spans.filter((s) => !s.parent_span_id);
        const path = findCriticalPath(roots);
        path.forEach((span) => criticalSpans.add(span.span_id));

        return criticalSpans;
    }

    /**
     * Initialize the chart
     */
    init() {
        this.render();
        this.attachEventListeners();
    }

    /**
     * Render the complete chart
     */
    render() {
        const totalDuration = this.trace.duration_ms || 1;
        const traceStart = new Date(this.trace.start_time);

        const html = `
            <div class="gantt-container">
                <!-- Toolbar -->
                <div class="gantt-toolbar">
                    <div class="gantt-info">
                        <strong>Total Duration:</strong> ${totalDuration.toFixed(2)} ms
                        <span style="margin-left: 1rem; color: #6b7280;">
                            ${this.spans.length} spans
                        </span>
                    </div>
                    <div class="gantt-controls">
                        <button onclick="ganttChart.zoomIn()" class="gantt-btn" title="Zoom In (=)">🔍+</button>
                        <button onclick="ganttChart.zoomOut()" class="gantt-btn" title="Zoom Out (-)">🔍−</button>
                        <button onclick="ganttChart.resetZoom()" class="gantt-btn" title="Reset (0)">⟲</button>
                        <button onclick="ganttChart.expandAll()" class="gantt-btn" title="Expand All">▼</button>
                        <button onclick="ganttChart.collapseAll()" class="gantt-btn" title="Collapse All">▶</button>
                    </div>
                </div>

                <!-- Time Scale -->
                <div class="gantt-timescale">
                    ${this.renderTimeScale(totalDuration)}
                </div>

                <!-- Spans -->
                <div class="gantt-spans">
                    ${this.spans.map((span) => this.renderSpan(span, totalDuration, traceStart)).join("")}
                </div>

                <!-- Legend -->
                <div class="gantt-legend">
                    <div class="legend-item">
                        <span class="legend-color" style="background: #3b82f6;"></span>
                        <span>Client</span>
                    </div>
                    <div class="legend-item">
                        <span class="legend-color" style="background: #10b981;"></span>
                        <span>Server</span>
                    </div>
                    <div class="legend-item">
                        <span class="legend-color" style="background: #8b5cf6;"></span>
                        <span>Internal</span>
                    </div>
                    <div class="legend-item">
                        <span class="legend-color" style="background: #ef4444;"></span>
                        <span>Error</span>
                    </div>
                    <div class="legend-item">
                        <span class="legend-color critical-path"></span>
                        <span>Critical Path</span>
                    </div>
                </div>
            </div>
        `;

        this.container.innerHTML = html;
    }

    /**
     * Render time scale markers
     */
    renderTimeScale(totalDuration) {
        const markers = [];
        const step = this.calculateTimeStep(totalDuration);

        for (let t = 0; t <= totalDuration; t += step) {
            const percent = (t / totalDuration) * 100;
            markers.push(`
                <div class="time-marker" style="left: ${percent}%;">
                    <div class="time-tick"></div>
                    <div class="time-label">${t.toFixed(0)}ms</div>
                </div>
            `);
        }

        return markers.join("");
    }

    /**
     * Calculate appropriate time step for markers
     */
    calculateTimeStep(totalDuration) {
        if (totalDuration < 10) {
            return 1;
        }
        if (totalDuration < 50) {
            return 5;
        }
        if (totalDuration < 100) {
            return 10;
        }
        if (totalDuration < 500) {
            return 50;
        }
        if (totalDuration < 1000) {
            return 100;
        }
        if (totalDuration < 5000) {
            return 500;
        }
        return 1000;
    }

    /**
     * Render individual span row
     */
    renderSpan(span, totalDuration, traceStart) {
        const duration = span.duration_ms || 0;
        const startMs = new Date(span.start_time) - traceStart;
        const leftPercent =
            (startMs / totalDuration) * 100 * this.zoomLevel + this.panOffset;
        const widthPercent = (duration / totalDuration) * 100 * this.zoomLevel;

        const hasChildren = span.children && span.children.length > 0;
        const isCollapsed = this.collapsedSpans.has(span.span_id);
        const isCritical = this.criticalPath.has(span.span_id);

        // Determine color based on span kind
        let color = "#3b82f6"; // client (blue)
        if (span.kind === "server") {
            color = "#10b981"; // green
        }
        if (span.kind === "internal") {
            color = "#8b5cf6"; // purple
        }
        if (span.status === "error") {
            color = "#ef4444"; // red
        }

        const indentPx = span.depth * 20;

        return `
            <div class="span-row ${isCritical ? "critical-path-row" : ""}" data-span-id="${span.span_id}">
                <div class="span-name" style="padding-left: ${indentPx}px;">
                    ${
                        hasChildren
                            ? `
                        <button class="span-toggle" onclick="ganttChart.toggleSpan('${span.span_id}')">
                            ${isCollapsed ? "▶" : "▼"}
                        </button>
                    `
                            : '<span class="span-spacer"></span>'
                    }
                    <span class="span-label" title="${span.name}">
                        ${span.name}
                    </span>
                </div>
                <div class="span-timeline">
                    <div class="span-bar ${isCritical ? "critical-path-bar" : ""}"
                         style="left: ${Math.max(0, leftPercent)}%;
                                width: ${widthPercent}%;
                                background: ${color};"
                         title="${span.name}\nDuration: ${duration.toFixed(2)}ms\nKind: ${span.kind}\nStatus: ${span.status}"
                         onclick="ganttChart.showSpanDetails('${span.span_id}')">
                        ${widthPercent > 5 ? `<span class="span-bar-label">${duration.toFixed(1)}ms</span>` : ""}
                    </div>
                </div>
                <div class="span-duration">${duration.toFixed(2)} ms</div>
            </div>
        `;
    }

    /**
     * Toggle span expand/collapse
     */
    toggleSpan(spanId) {
        if (this.collapsedSpans.has(spanId)) {
            this.collapsedSpans.delete(spanId);
        } else {
            this.collapsedSpans.add(spanId);
        }
        this.spans = this.buildSpanTree(this.trace.spans);
        this.render();
    }

    /**
     * Expand all spans
     */
    expandAll() {
        this.collapsedSpans.clear();
        this.spans = this.buildSpanTree(this.trace.spans);
        this.render();
    }

    /**
     * Collapse all spans to top level
     */
    collapseAll() {
        this.trace.spans.forEach((span) => {
            if (span.parent_span_id) {
                this.collapsedSpans.add(span.parent_span_id);
            }
        });
        this.spans = this.buildSpanTree(this.trace.spans);
        this.render();
    }

    /**
     * Zoom in
     */
    zoomIn() {
        this.zoomLevel = Math.min(this.zoomLevel * 1.5, 10);
        this.render();
    }

    /**
     * Zoom out
     */
    zoomOut() {
        this.zoomLevel = Math.max(this.zoomLevel / 1.5, 0.1);
        this.render();
    }

    /**
     * Reset zoom and pan
     */
    resetZoom() {
        this.zoomLevel = 1;
        this.panOffset = 0;
        this.render();
    }

    /**
     * Show detailed span information
     */
    showSpanDetails(spanId) {
        const span = this.trace.spans.find((s) => s.span_id === spanId);
        if (!span) {
            return;
        }

        alert(
            `Span Details:\n\nName: ${span.name}\nDuration: ${span.duration_ms}ms\nKind: ${span.kind}\nStatus: ${span.status}\n\nAttributes:\n${JSON.stringify(span.attributes, null, 2)}`,
        );
    }

    /**
     * Attach keyboard and mouse event listeners
     */
    attachEventListeners() {
        document.addEventListener("keydown", (e) => {
            if (!this.container.isConnected) {
                return;
            }

            switch (e.key) {
                case "=":
                case "+":
                    this.zoomIn();
                    e.preventDefault();
                    break;
                case "-":
                case "_":
                    this.zoomOut();
                    e.preventDefault();
                    break;
                case "0":
                    this.resetZoom();
                    e.preventDefault();
                    break;
            }
        });
    }
}

// Global instance (will be initialized from template)
// eslint-disable-next-line prefer-const
let ganttChart = null;
