<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <template id="view_memory">
        &lt;!DOCTYPE html&gt;
        <html lang="en">
        <head>
            <meta charset="UTF-8"/>
            <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
            <title>Memory Usage Visualization</title>
            <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
            <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels"></script>
            <style>
                /* General body and layout styles */
                body {
                    font-family: Arial, sans-serif;
                    margin: 20px;
                    background-color: #f9f9f9;
                }
                .container {
                    display: flex;
                    flex-direction: column;
                    gap: 20px;
                }

                /* Row definitions for the new layout */
                .top-row {
                    height: 40vh; /* Line graph takes 40% of the viewport height */
                }
                .bottom-row {
                    display: flex;
                    gap: 20px;
                    /* The height of this row will be determined by its content */
                }

                /* Chart container styles */
                .chart-container {
                    position: relative;
                    background: #fff;
                    border: 1px solid #ddd;
                    border-radius: 5px;
                    padding: 15px;
                    height: 100%;
                    width: 100%;
                }
                
                /* Specific styling for the bottom row elements */
                .bar-chart-wrapper {
                    flex: 2; /* Bar chart takes 2/3 of the width */
                    min-height: 400px; /* Minimum height for the bar chart */
                }
                .traceback-panel {
                    flex: 1; /* Traceback panel takes 1/3 of the width */
                    background: #f5f5f5;
                    border: 1px solid #ddd;
                    border-radius: 5px;
                    padding: 15px;
                    overflow-y: auto;
                    min-height: 400px; /* Match min-height for alignment */
                    height: fit-content; /* Adjust height to content */
                }

                /* Traceback panel content styling */
                .traceback-header {
                    font-weight: bold;
                    margin-bottom: 10px;
                    color: #333;
                }
                .traceback-item {
                    background: white;
                    margin: 5px 0;
                    padding: 8px;
                    border-radius: 3px;
                    border-left: 3px solid #007acc;
                    font-family: monospace;
                    font-size: 12px;
                }
                .traceback-file {
                    color: #007acc;
                    font-weight: bold;
                }
                .copyable {
                    cursor: pointer;
                    user-select: all;
                    padding: 2px 4px;
                    border-radius: 3px;
                    transition: background-color 0.2s;
                }
                .copyable:hover {
                    background-color: #e8f4f8;
                }
                .traceback-line {
                    color: #666;
                }
                .no-selection {
                    color: #999;
                    font-style: italic;
                    text-align: center;
                    padding-top: 50px;
                }
            </style>
        </head>
        <body>
            <div class="container">
                <!-- Top row for the full-width line chart -->
                <div class="top-row">
                    <div class="chart-container">
                        <canvas id="totalMemoryChart"></canvas>
                    </div>
                </div>
                <!-- Bottom row for the bar chart and traceback panel -->
                <div class="bottom-row">
                    <div class="bar-chart-wrapper chart-container" id="memoryChartContainer">
                        <canvas id="memoryChart"></canvas>
                    </div>
                    <div class="traceback-panel" id="tracebackPanel">
                        <div class="no-selection">Click a point on the top chart, then a bar below to see details.</div>
                    </div>
                </div>
            </div>

            <script>
                // Get data from Odoo template
                const rawData = JSON.parse(atob('<t t-esc="memory_graph"/>'));

                // --- UTILITY FUNCTIONS ---
                function formatBytes(bytes) {
                    if (bytes === 0) return '0 B';
                    const k = 1024;
                    const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
                    const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k));
                    const value = bytes / Math.pow(k, i);
                    return `${value.toFixed(1)} ${sizes[i]}`;
                }

                function formatTimestamp(timestamp) {
                    const date = new Date(timestamp * 1000);
                    return date.toLocaleString();
                }

                function getLastFileFromTraceback(traceback) {
                    if (!traceback || traceback.length === 0) return "unknown:0";
                    const lastFrame = traceback[traceback.length - 1];
                    return `${lastFrame[0]}:${lastFrame[1]}`;
                }
                
                function getFullTracebackKey(traceback) {
                    if (!traceback || traceback.length === 0) return "unknown";
                    return traceback.map(frame => `${frame[0]}:${frame[1]}`).join(' -> ');
                }

                // --- DYNAMIC UI UPDATES ---
                function updateChartHeight(chart, dataLength) {
                    const container = document.getElementById('memoryChartContainer');
                    if (dataLength === 0) {
                        container.style.height = '400px'; // Reset to min-height
                        chart.resize();
                        return;
                    }
                    
                    const minBarHeight = 35; // Minimum height per bar for readability
                    const padding = 100; // Extra space for axes and labels
                    const newHeight = Math.max(400, (dataLength * minBarHeight) + padding);
                    
                    container.style.height = newHeight + 'px';
                    chart.resize();
                }

                function showTraceback(traceback, size, fullTracebackKey) {
                    const panel = document.getElementById('tracebackPanel');
                    if (!traceback || traceback.length === 0) {
                        panel.innerHTML = '<div class="no-selection">No traceback available</div>';
                        return;
                    }

                    let html = `<div class="traceback-header">Traceback (${formatBytes(size)})</div>`;

                    if (fullTracebackKey) {
                        html += `
                            <div style="margin-bottom: 10px; padding: 8px; background: #e8f4f8; border-radius: 3px; font-size: 11px; color: #555;">
                                <strong>Full Path:</strong> <span class="copyable" title="Click to select for copying">${fullTracebackKey}</span>
                            </div>
                        `;
                    }

                    traceback.forEach(frame => {
                        const [filename, lineNo, functionName, code] = frame;
                        html += `
                            <div class="traceback-item">
                                <div class="traceback-file copyable" title="Click to select for copying">${filename}:${lineNo}</div>
                                <div>in <strong>${functionName || 'unknown'}</strong></div>
                                ${code ? `<div class="traceback-line">${code}</div>` : ''}
                            </div>
                        `;
                    });

                    panel.innerHTML = html;
                }

                // --- CHART LOGIC ---
                const totalSizes = rawData.map(entry =>
                    entry.samples.reduce((sum, sample) => sum + sample.size, 0)
                );
                const lineLabels = rawData.map(entry => formatTimestamp(entry.start));

                let selectedIndexes = [];
                let currentBreakdownData = [];

                const updateBarChart = () => {
                    if (selectedIndexes.length === 1) {
                        const entry = rawData[selectedIndexes[0]];
                        const groupedData = {};
                        entry.samples.forEach(sample => {
                            const key = getLastFileFromTraceback(sample.traceback);
                            if (!groupedData[key]) {
                                groupedData[key] = { size: 0, traceback: sample.traceback, samples: [] };
                            }
                            groupedData[key].size += sample.size;
                            groupedData[key].samples.push(sample);
                        });

                        currentBreakdownData = Object.entries(groupedData).map(([key, data]) => ({
                            label: key,
                            size: data.size,
                            traceback: data.traceback,
                            samples: data.samples
                        })).sort((a, b) => b.size - a.size);

                        breakdownChart.data.labels = currentBreakdownData.map(item => item.label);
                        breakdownChart.data.datasets = [{
                            label: `Memory Usage`,
                            data: currentBreakdownData.map(item => item.size),
                            backgroundColor: 'rgba(54, 162, 235, 0.6)',
                            borderColor: 'rgba(54, 162, 235, 1)',
                            borderWidth: 1
                        }];
                        breakdownChart.options.plugins.title.text = `Memory Breakdown for ${formatTimestamp(entry.start)}`;

                    } else if (selectedIndexes.length === 2) {
                        const [idx1, idx2] = selectedIndexes.sort((a, b) => a - b);
                        const entry1 = rawData[idx1];
                        const entry2 = rawData[idx2];

                        const groupDataByFullTraceback = (entry) => {
                            const grouped = {};
                            entry.samples.forEach(sample => {
                                const key = getFullTracebackKey(sample.traceback);
                                if (!grouped[key]) {
                                    grouped[key] = { size: 0, traceback: sample.traceback, lastFile: getLastFileFromTraceback(sample.traceback) };
                                }
                                grouped[key].size += sample.size;
                            });
                            return grouped;
                        };

                        const data1 = groupDataByFullTraceback(entry1);
                        const data2 = groupDataByFullTraceback(entry2);
                        const labelSet = new Set([...Object.keys(data1), ...Object.keys(data2)]);
                        
                        let diffData = [];
                        labelSet.forEach(fullTracebackKey => {
                            const size1 = data1[fullTracebackKey]?.size || 0;
                            const size2 = data2[fullTracebackKey]?.size || 0;
                            const diff = size2 - size1;
                            if (diff !== 0) {
                                const item = data2[fullTracebackKey] || data1[fullTracebackKey];
                                diffData.push({ 
                                    label: item.lastFile,
                                    diff: diff,
                                    traceback: item.traceback,
                                    fullTracebackKey: fullTracebackKey
                                });
                            }
                        });

                        currentBreakdownData = diffData.sort((a, b) => b.diff - a.diff);

                        breakdownChart.data.labels = currentBreakdownData.map(item => item.label);
                        breakdownChart.data.datasets = [{
                            label: `Difference`,
                            data: currentBreakdownData.map(item => item.diff),
                            backgroundColor: item => item.raw >= 0 ? 'rgba(75, 192, 192, 0.6)' : 'rgba(255, 99, 132, 0.6)',
                            borderColor: item => item.raw >= 0 ? 'rgba(75, 192, 192, 1)' : 'rgba(255, 99, 132, 1)',
                            borderWidth: 1
                        }];
                        breakdownChart.options.plugins.title.text = `Memory Difference: ${formatTimestamp(entry2.start)} vs ${formatTimestamp(entry1.start)}`;

                    } else {
                        currentBreakdownData = [];
                        breakdownChart.data.labels = [];
                        breakdownChart.data.datasets = [];
                        breakdownChart.options.plugins.title.text = 'Memory Breakdown';
                        document.getElementById('tracebackPanel').innerHTML = 
                            '<div class="no-selection">Click a point on the top chart, then a bar below to see details.</div>';
                    }
                    
                    updateChartHeight(breakdownChart, currentBreakdownData.length);
                    breakdownChart.update();
                    totalChart.update();
                };

                // --- CHART INSTANTIATION ---
                const totalChart = new Chart(document.getElementById('totalMemoryChart'), {
                    type: 'line',
                    data: {
                        labels: lineLabels,
                        datasets: [{
                            label: 'Total Memory Usage',
                            data: totalSizes,
                            fill: false,
                            borderColor: 'rgba(75, 192, 192, 1)',
                            tension: 0.1,
                            pointBackgroundColor: ctx => selectedIndexes.includes(ctx.dataIndex) ? 'rgba(255, 99, 132, 1)' : 'rgba(75, 192, 192, 1)',
                            pointRadius: ctx => selectedIndexes.includes(ctx.dataIndex) ? 7 : 4,
                            pointBorderWidth: ctx => selectedIndexes.includes(ctx.dataIndex) ? 3 : 1
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        plugins: {
                            title: { display: true, text: 'Total Memory Usage Over Time (Click to select, Shift+Click to compare)' },
                            tooltip: { callbacks: { label: context => `Total: ${formatBytes(context.raw)}` } }
                        },
                        onClick: (evt, elements) => {
                            if (elements.length > 0) {
                                const idx = elements[0].index;
                                const isSelected = selectedIndexes.includes(idx);
                                if (evt.native.shiftKey) {
                                    if (isSelected) {
                                        selectedIndexes = selectedIndexes.filter(i => i !== idx);
                                    } else {
                                        selectedIndexes.push(idx);
                                        if (selectedIndexes.length > 2) selectedIndexes.shift();
                                    }
                                } else {
                                    selectedIndexes = isSelected &amp;&amp; selectedIndexes.length === 1 ? [] : [idx];
                                }
                            } else {
                                selectedIndexes = [];
                            }
                            updateBarChart();
                        },
                        scales: {
                            y: { beginAtZero: true, ticks: { callback: formatBytes }, title: { display: true, text: 'Total Memory' } },
                            x: { title: { display: true, text: 'Time' } }
                        }
                    }
                });

                const breakdownChart = new Chart(document.getElementById('memoryChart'), {
                    type: 'bar',
                    data: { labels: [], datasets: [] },
                    options: {
                        indexAxis: 'y',
                        responsive: true,
                        maintainAspectRatio: false,
                        layout: { padding: { right: 120 } },
                        plugins: {
                            legend: { display: false },
                            title: { display: true, text: 'Memory Breakdown' },
                            tooltip: { callbacks: { label: context => `${formatBytes(context.raw)}` } },
                            datalabels: {
                                anchor: 'end',
                                align: 'right',
                                color: '#000',
                                font: { weight: 'bold', size: 11 },
                                formatter: (value) => formatBytes(value),
                                display: true,
                                clip: false
                            }
                        },
                        onClick: (evt, elements) => {
                            if (elements.length > 0 &amp;&amp; currentBreakdownData.length > 0) {
                                const item = currentBreakdownData[elements[0].index];
                                const fullKey = item.fullTracebackKey || getFullTracebackKey(item.traceback);
                                showTraceback(item.traceback, Math.abs(item.diff || item.size), fullKey);
                            }
                        },
                        scales: {
                            x: { beginAtZero: false, ticks: { callback: formatBytes }, title: { display: true, text: 'Memory Usage / Difference' } },
                            y: { title: { display: true, text: 'File:Line' }, ticks: { maxRotation: 0, font: { size: 11 } } }
                        }
                    },
                    plugins: [ChartDataLabels]
                });

                // Initial render
                updateBarChart();
            </script>
        </body>
        </html>
    </template>
</odoo>
