/**
 * List2Play Servers Plugin JavaScript
 */

(function() {
    'use strict';
    
    // Copy to clipboard functionality
    function initCopyToClipboard() {
        const copyButtons = document.querySelectorAll('[data-copy]');
        
        copyButtons.forEach(function(button) {
            button.addEventListener('click', function() {
                const textToCopy = this.getAttribute('data-copy');
                
                if (!textToCopy) {
                    return;
                }
                
                // Use modern Clipboard API if available
                if (navigator.clipboard && navigator.clipboard.writeText) {
                    navigator.clipboard.writeText(textToCopy).then(function() {
                        showCopyFeedback(button);
                    }).catch(function(err) {
                        fallbackCopyToClipboard(textToCopy, button);
                    });
                } else {
                    // Fallback for older browsers
                    fallbackCopyToClipboard(textToCopy, button);
                }
            });
        });
    }
    
    // Fallback copy method
    function fallbackCopyToClipboard(text, button) {
        const textArea = document.createElement('textarea');
        textArea.value = text;
        textArea.style.position = 'fixed';
        textArea.style.left = '-999999px';
        textArea.style.top = '-999999px';
        document.body.appendChild(textArea);
        textArea.focus();
        textArea.select();
        
        try {
            const successful = document.execCommand('copy');
            if (successful) {
                showCopyFeedback(button);
            }
        } catch (err) {
            // Ignore copy errors
        }
        
        document.body.removeChild(textArea);
    }
    
    // Show copy feedback
    function showCopyFeedback(button) {
        const originalText = button.textContent;
        button.textContent = 'Copied!';
        button.style.color = 'rgb(110, 231, 183)';
        
        setTimeout(function() {
            button.textContent = originalText;
            button.style.color = '';
        }, 2000);
    }
    
    // Initialize when DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initCopyToClipboard);
    } else {
        initCopyToClipboard();
    }
    
    // Re-initialize for dynamically loaded content
    if (typeof MutationObserver !== 'undefined') {
        const observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                if (mutation.addedNodes.length) {
                    initCopyToClipboard();
                }
            });
        });
        
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }
    
    // Request Queue for rate limiting
    var RequestQueue = (function() {
        var queue = [];
        var processing = false;
        var maxRequestsPerSecond = 2; // 2 requests per second
        var lastRequestTime = 0;
        var minDelay = 1000 / maxRequestsPerSecond; // 500ms between requests
        
        function processQueue() {
            if (processing || queue.length === 0) {
                return;
            }
            
            processing = true;
            var now = Date.now();
            var timeSinceLastRequest = now - lastRequestTime;
            var delay = Math.max(0, minDelay - timeSinceLastRequest);
            
            setTimeout(function() {
                var item = queue.shift();
                if (item) {
                    lastRequestTime = Date.now();
                    item.fn().then(function(result) {
                        if (item.resolve) item.resolve(result);
                    }).catch(function(error) {
                        if (item.reject) item.reject(error);
                    }).finally(function() {
                        processing = false;
                        processQueue(); // Process next item
                    });
                } else {
                    processing = false;
                }
            }, delay);
        }
        
        return {
            add: function(fn) {
                return new Promise(function(resolve, reject) {
                    queue.push({
                        fn: fn,
                        resolve: resolve,
                        reject: reject
                    });
                    processQueue();
                });
            }
        };
    })();
    
    // Initialize players history charts with lazy loading and request queue
    function initPlayersCharts() {
        const charts = document.querySelectorAll('.list2play-chart[data-server-id]');
        
        if (charts.length === 0) {
            return;
        }
        
        // Check if Chart.js is loaded
        if (typeof Chart === 'undefined') {
            console.error('Chart.js is not loaded!');
            charts.forEach(function(canvas) {
                const ctx = canvas.getContext('2d');
                ctx.fillStyle = 'rgba(148, 163, 184, 0.3)';
                ctx.font = '11px sans-serif';
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText('Chart.js not loaded', canvas.width / 2, canvas.height / 2);
            });
            return;
        }
        
        // Load all charts immediately (no lazy loading)
        // User requested all charts to load, not just visible ones
        charts.forEach(function(canvas) {
            // Skip if already initialized
            if (canvas.chart) {
                return;
            }
            loadChart(canvas);
        });
    }
    
    // Load chart for a single canvas (with request queue)
    function loadChart(canvas) {
            const serverId = canvas.getAttribute('data-server-id');
            if (!serverId) {
                return;
            }
            
            // Check if chart already initialized - destroy it first if exists
            if (canvas.chart) {
                try {
                    canvas.chart.destroy();
                    canvas.chart = null;
                } catch (e) {
                    // Ignore destroy errors
                }
            }
            
            // Mark as loading to prevent duplicate requests
            if (canvas._loading) {
                return;
            }
            canvas._loading = true;
            
            const apiBaseUrl = (typeof list2playServers !== 'undefined' && list2playServers.apiBaseUrl) 
                ? list2playServers.apiBaseUrl 
                : 'https://list2play.net/api';
            
            // Check cache first (5 minutes cache)
            const cacheKey = 'list2play_chart_' + serverId;
            const cached = sessionStorage.getItem(cacheKey);
            if (cached) {
                try {
                    const cachedData = JSON.parse(cached);
                    const cacheTime = cachedData.timestamp || 0;
                    const now = Date.now();
                    const cacheAge = now - cacheTime;
                    const cacheTTL = 5 * 60 * 1000; // 5 minutes
                    
                    if (cacheAge < cacheTTL) {
                        canvas._loading = false;
                        processChartData(cachedData.data, canvas, serverId);
                        return;
                    }
                } catch (e) {
                    // Ignore cache parse errors
                }
            }
            
            // Fetch players history data with retry logic and rate limiting handling
            function fetchChartData(retries = 1) {
                return fetch(apiBaseUrl + '/servers/' + serverId + '/stats/players?days=1&granularity=hour', {
                    method: 'GET',
                    headers: {
                        'Accept': 'application/json',
                    },
                    cache: 'default' // Allow browser cache
                })
                .then(function(response) {
                    if (!response.ok) {
                        // For 429 (rate limit), wait longer and don't retry too many times
                        if (response.status === 429 && retries > 0) {
                            const retryAfter = response.headers.get('Retry-After');
                            const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : 5000 * (2 - retries);
                            return new Promise(function(resolve) {
                                setTimeout(function() {
                                    resolve(fetchChartData(retries - 1));
                                }, waitTime);
                            });
                        }
                        // Retry on 5xx errors only (not 429)
                        if (response.status >= 500 && retries > 0) {
                            return new Promise(function(resolve) {
                                setTimeout(function() {
                                    resolve(fetchChartData(retries - 1));
                                }, 2000);
                            });
                        }
                        throw new Error('HTTP ' + response.status + ': ' + response.statusText);
                    }
                    return response.json();
                })
                .then(function(data) {
                    // Cache the response
                    try {
                        sessionStorage.setItem(cacheKey, JSON.stringify({
                            timestamp: Date.now(),
                            data: data
                        }));
                    } catch (e) {
                        // Ignore cache errors
                    }
                    return data;
                })
                .catch(function(error) {
                    // Don't retry on network errors if we already retried
                    if (retries > 0 && (error.message.includes('Failed to fetch') || error.message.includes('NetworkError'))) {
                        return new Promise(function(resolve) {
                            setTimeout(function() {
                                resolve(fetchChartData(retries - 1));
                            }, 2000);
                        });
                    }
                    throw error;
                });
            }
            
            // Add to request queue for rate limiting
            RequestQueue.add(function() {
                return fetchChartData();
            })
                .then(function(data) {
                    canvas._loading = false;
                    processChartData(data, canvas, serverId);
                })
                .catch(function(error) {
                    canvas._loading = false;
                    handleChartError(error, canvas, serverId, apiBaseUrl);
                });
    }
    
    // Process chart data and create chart
    function processChartData(data, canvas, serverId) {
        // Skip if canvas is no longer in DOM
        if (!canvas.parentNode) {
            return;
        }
        
        // Destroy existing chart if any
        if (canvas.chart) {
            try {
                canvas.chart.destroy();
            } catch (e) {
                // Ignore destroy errors
            }
            canvas.chart = null;
        }
        
        if (!data || !data.series || !Array.isArray(data.series) || data.series.length === 0) {
            // No data - show empty state
            const ctx = canvas.getContext('2d');
            ctx.fillStyle = 'rgba(148, 163, 184, 0.3)';
            ctx.font = '12px sans-serif';
            ctx.textAlign = 'center';
            ctx.fillText('No data available', canvas.width / 2, canvas.height / 2);
            canvas._loading = false;
            return;
        }
        
        // Prepare chart data
        const labels = [];
        const values = [];
        const timeLabels = []; // For X axis - show hourly labels
        let lastHour = -1;
        
        try {
            data.series.forEach(function(point, index) {
                            const timestamp = point.timestamp || point.bucket;
                            if (!timestamp) {
                                return;
                            }
                            
                            let date;
                            try {
                                date = new Date(timestamp);
                                if (isNaN(date.getTime())) {
                                    return;
                                }
                            } catch (e) {
                                return;
                            }
                
                const hours = date.getHours();
                const minutes = date.getMinutes();
                const timeStr = String(hours).padStart(2, '0') + ':' + String(minutes).padStart(2, '0');
                labels.push(timeStr);
                
                // Get max value from point
                const maxValue = point.max !== undefined ? point.max : (point.value !== undefined ? point.value : 0);
                values.push(maxValue);
                
                // Track hourly labels for X axis - show every 2 hours (when minutes === 0)
                // This ensures we show 12 labels (every 2 hours) to avoid overlap
                if (minutes === 0) {
                    // Only add if it's a new hour and it's an even hour (0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22)
                    if (hours !== lastHour && hours % 2 === 0) {
                        timeLabels.push({
                            index: index,
                            time: timeStr,
                            hour: hours
                        });
                        lastHour = hours;
                    }
                }
            });
            
            // Always show first label if we have data (even if not every 2 hours)
            // But only if it's not too close to the first 2-hour label
            if (labels.length > 0) {
                // Add first label if not already added
                const firstLabelExists = timeLabels.some(function(tl) { return tl.index === 0; });
                if (!firstLabelExists && timeLabels.length > 0) {
                    // Check distance from first 2-hour label
                    const first2HourLabel = timeLabels[0];
                    const distance = first2HourLabel.index - 0;
                    
                    // Only add if it's at least 3 points away (to avoid overlap/compression)
                    if (distance >= 3) {
                        const firstDate = new Date(data.series[0].timestamp || data.series[0].bucket);
                        if (!isNaN(firstDate.getTime())) {
                            timeLabels.unshift({
                                index: 0,
                                time: labels[0],
                                hour: firstDate.getHours()
                            });
                        }
                    }
                } else if (!firstLabelExists && timeLabels.length === 0) {
                    // If no 2-hour labels yet, add first label
                    const firstDate = new Date(data.series[0].timestamp || data.series[0].bucket);
                    if (!isNaN(firstDate.getTime())) {
                        timeLabels.unshift({
                            index: 0,
                            time: labels[0],
                            hour: firstDate.getHours()
                        });
                    }
                }
                
                // Add last label only if it's not too close to the last 2-hour label
                const lastIndex = labels.length - 1;
                const lastLabelExists = timeLabels.some(function(tl) { return tl.index === lastIndex; });
                if (!lastLabelExists && timeLabels.length > 0) {
                    const lastPoint = data.series[data.series.length - 1];
                    const lastDate = new Date(lastPoint.timestamp || lastPoint.bucket);
                    if (!isNaN(lastDate.getTime())) {
                        // Check distance from last 2-hour label
                        const last2HourLabel = timeLabels[timeLabels.length - 1];
                        const distance = lastIndex - last2HourLabel.index;
                        
                        // Only add if it's at least 3 points away (to avoid overlap)
                        if (distance >= 3) {
                            timeLabels.push({
                                index: lastIndex,
                                time: labels[lastIndex],
                                hour: lastDate.getHours()
                            });
                        }
                    }
                }
                
                // Sort timeLabels by index to ensure correct order
                timeLabels.sort(function(a, b) {
                    return a.index - b.index;
                });
            }
            
            // If still no labels found, show evenly spaced ones (fallback)
            if (timeLabels.length === 0 && labels.length > 0) {
                const step = Math.max(1, Math.floor(labels.length / 6));
                for (let i = 0; i < labels.length; i += step) {
                    if (i === 0 || i === labels.length - 1 || (i > 0 && i < labels.length - 1)) {
                        const point = data.series[i];
                        const pointDate = new Date(point.timestamp || point.bucket);
                        if (!isNaN(pointDate.getTime())) {
                            timeLabels.push({
                                index: i,
                                time: labels[i],
                                hour: pointDate.getHours()
                            });
                        }
                    }
                }
            }
        } catch (e) {
            throw e;
        }
        
        if (labels.length === 0 || values.length === 0) {
            throw new Error('No valid data points found');
        }
        
        // Create chart - destroy existing one first if it exists
        if (typeof Chart !== 'undefined') {
            try {
                // Destroy existing chart if any
                if (canvas.chart) {
                    try {
                        canvas.chart.destroy();
                    } catch (e) {
                        // Ignore destroy errors
                    }
                    canvas.chart = null;
                }
                
                canvas.chart = new Chart(canvas, {
                    type: 'line',
                    data: {
                        labels: labels,
                        datasets: [{
                            label: 'Players',
                            data: values,
                            borderColor: canvas.dataset.chartLine || 'rgb(16, 185, 129)',
                            backgroundColor: canvas.dataset.chartArea || 'rgba(16, 185, 129, 0.1)',
                            borderWidth: 2,
                            fill: true,
                            tension: 0.4,
                            pointRadius: 0,
                            pointHoverRadius: 3,
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        plugins: {
                            legend: {
                                display: false
                            },
                            tooltip: {
                                enabled: true,
                                mode: 'index',
                                intersect: false,
                                backgroundColor: 'rgba(15, 23, 42, 0.9)',
                                titleColor: 'rgb(226, 232, 240)',
                                bodyColor: 'rgb(203, 213, 225)',
                                borderColor: 'rgb(30, 41, 59)',
                                borderWidth: 1,
                                padding: 8,
                            }
                        },
                        scales: {
                            x: {
                                display: true,
                                grid: {
                                    display: false,
                                    color: canvas.dataset.chartGrid || 'rgba(148, 163, 184, 0.2)'
                                },
                                ticks: {
                                    color: canvas.dataset.chartAxis || 'rgb(148, 163, 184)',
                                    font: {
                                        size: 9,
                                        family: 'Inter, sans-serif'
                                    },
                                    maxRotation: 45,
                                    minRotation: 45,
                                    // Show labels every 2 hours (12 labels max for 24h period)
                                    maxTicksLimit: 25, // Allow enough space for all labels
                                    autoSkip: false, // Don't auto-skip - we control which labels to show
                                    stepSize: 1,
                                    callback: function(value, index) {
                                        // Show only labels that are in timeLabels array (every 2 hours)
                                        const label = labels[index];
                                        if (!label) return '';
                                        
                                        // Check if this index should be shown
                                        const shouldShow = timeLabels.some(function(tl) {
                                            return tl.index === index;
                                        });
                                        
                                        return shouldShow ? label : '';
                                    }
                                },
                                border: {
                                    display: true,
                                    color: canvas.dataset.chartGrid || 'rgba(148, 163, 184, 0.3)'
                                }
                            },
                            y: {
                                display: true,
                                grid: {
                                    display: false,
                                    color: canvas.dataset.chartGrid || 'rgba(148, 163, 184, 0.1)'
                                },
                                ticks: {
                                    color: canvas.dataset.chartAxis || 'rgb(148, 163, 184)',
                                    font: {
                                        size: 9,
                                        family: 'Inter, sans-serif'
                                    },
                                    padding: 4,
                                    callback: function(value) {
                                        return value;
                                    }
                                },
                                border: {
                                    display: true,
                                    color: canvas.dataset.chartGrid || 'rgba(148, 163, 184, 0.3)'
                                },
                                beginAtZero: true
                            }
                        },
                        interaction: {
                            mode: 'index',
                            intersect: false
                        }
                    }
                });
            } catch (chartError) {
                canvas._loading = false;
                throw chartError;
            }
        } else {
            canvas._loading = false;
            throw new Error('Chart.js is not available');
        }
    }
    
    // Handle chart errors
    function handleChartError(error, canvas, serverId, apiBaseUrl) {
        // Show user-friendly error message
        const ctx = canvas.getContext('2d');
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = 'rgba(148, 163, 184, 0.3)';
        ctx.font = '11px sans-serif';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        
        // Try to get canvas dimensions
        const rect = canvas.getBoundingClientRect();
        const centerX = rect.width / 2 || canvas.width / 2;
        const centerY = rect.height / 2 || canvas.height / 2;
        
        ctx.fillText('Failed to load', centerX, centerY);
    }
    
    function syncChartTheme(canvas) {
        if (!canvas || !canvas.chart) {
            return false;
        }

        const targetLine = canvas.dataset.chartLine || 'rgb(16, 185, 129)';
        const targetArea = canvas.dataset.chartArea || 'rgba(16, 185, 129, 0.1)';
        const targetAxis = canvas.dataset.chartAxis || 'rgb(148, 163, 184)';
        const targetGrid = canvas.dataset.chartGrid || 'rgba(148, 163, 184, 0.2)';

        const chart = canvas.chart;
        const dataset = chart.data && chart.data.datasets ? chart.data.datasets[0] : null;
        if (!dataset) {
            return false;
        }

        let hasChanges = false;

        if (dataset.borderColor !== targetLine) {
            dataset.borderColor = targetLine;
            hasChanges = true;
        }

        if (dataset.backgroundColor !== targetArea) {
            dataset.backgroundColor = targetArea;
            hasChanges = true;
        }

        const xScale = chart.options && chart.options.scales ? chart.options.scales.x : null;
        const yScale = chart.options && chart.options.scales ? chart.options.scales.y : null;

        if (xScale) {
            if (xScale.ticks && xScale.ticks.color !== targetAxis) {
                xScale.ticks.color = targetAxis;
                hasChanges = true;
            }
            if (xScale.grid && xScale.grid.color !== targetGrid) {
                xScale.grid.color = targetGrid;
                hasChanges = true;
            }
            if (xScale.border && xScale.border.color !== targetGrid) {
                xScale.border.color = targetGrid;
                hasChanges = true;
            }
        }

        if (yScale) {
            if (yScale.ticks && yScale.ticks.color !== targetAxis) {
                yScale.ticks.color = targetAxis;
                hasChanges = true;
            }
            if (yScale.grid && yScale.grid.color !== targetGrid) {
                yScale.grid.color = targetGrid;
                hasChanges = true;
            }
            if (yScale.border && yScale.border.color !== targetGrid) {
                yScale.border.color = targetGrid;
                hasChanges = true;
            }
        }

        if (hasChanges) {
            chart.update('none');
        }

        return hasChanges;
    }

    // Track initialized charts to prevent duplicate initialization
    var initializedChartIds = new Set();
    
    // Initialize only new charts (not already initialized)
    function initializeNewCharts() {
        const charts = document.querySelectorAll('.list2play-chart[data-server-id]');

        // Existing charts can remain mounted while widget style changes.
        // Re-apply chart palette from current data-* attributes without refetching data.
        charts.forEach(function(canvas) {
            syncChartTheme(canvas);
        });

        const newCharts = Array.from(charts).filter(function(canvas) {
            const serverId = canvas.getAttribute('data-server-id');
            if (!serverId) return false;
            
            // Skip if already initialized
            if (canvas.chart || initializedChartIds.has(serverId)) {
                return false;
            }
            
            initializedChartIds.add(serverId);
            return true;
        });
        
        if (newCharts.length > 0) {
            newCharts.forEach(function(canvas) {
                loadChart(canvas);
            });
        }
    }
    
    // Initialize charts when DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function() {
            initCopyToClipboard();
            initializeNewCharts();
        });
    } else {
        initCopyToClipboard();
        initializeNewCharts();
    }
    
    // Re-initialize charts for dynamically loaded content (with debouncing)
    if (typeof MutationObserver !== 'undefined') {
        var chartInitTimeout = null;
        const chartObserver = new MutationObserver(function(mutations) {
            // Debounce to avoid too many calls
            if (chartInitTimeout) {
                clearTimeout(chartInitTimeout);
            }
            chartInitTimeout = setTimeout(function() {
                initializeNewCharts();
            }, 200);
        });
        
        chartObserver.observe(document.body, {
            childList: true,
            subtree: true
        });
    }
})();

