/**
 * JCOGS Image Pro - Async Cache Management
 * ==========================================
 * Handles asynchronous loading of cache statistics and locations
 * to improve page load performance, especially with remote storage.
 * 
 * @category   ExpressionEngine Add-on
 * @package    JCOGS Image Pro
 * @author     JCOGS Design <contact@jcogs.net>
 * @copyright  Copyright (c) 2021 - 2025 JCOGS Design
 * @license    https://jcogs.net/add-ons/license/jcogs_img_pro
 * @version    2.0.6
 * @since      Issue #71 - Async Cache Loading
 */

(function($) {
    'use strict';

    /**
     * Normalize endpoint URLs for browser safety.
     *
     * Resolves relative URLs against current page and, when CP is loaded over
     * HTTPS, upgrades accidental HTTP endpoints to HTTPS to avoid mixed content.
     */
    function normalizeEndpointUrl(rawUrl) {
        if (!rawUrl) {
            return rawUrl;
        }

        try {
            var resolved = new URL(rawUrl, window.location.href);

            if (window.location.protocol === 'https:' && resolved.protocol === 'http:') {
                resolved.protocol = 'https:';
            }

            return resolved.toString();
        } catch (e) {
            return rawUrl;
        }
    }

    /**
     * Check whether a response/body looks like an EE throttling message.
     */
    function isThrottlingResponseText(text) {
        if (!text) {
            return false;
        }

        var value = String(text).toLowerCase();
        return value.indexOf('throttl') !== -1 ||
            value.indexOf('too many request') !== -1 ||
            value.indexOf('too many page') !== -1 ||
            value.indexOf('rate limit') !== -1 ||
            value.indexOf('te veel pagina') !== -1 ||
            value.indexOf('probeer het later nog eens') !== -1;
    }

    /**
     * Build a user-facing error from AJAX failure details.
     */
    function buildAjaxErrorMessage(baseMessage, jqXHR, textStatus, errorThrown) {
        if (textStatus === 'timeout') {
            return 'Request timed out. Remote storage may be slow or unavailable.';
        }

        if (jqXHR && jqXHR.status === 0) {
            return 'Network request blocked or unreachable. This is often caused by mixed HTTP/HTTPS settings or a cross-origin endpoint. Check that site_url, base_url, and upload directory URLs use the same HTTPS host as the Control Panel.';
        }

        var responseText = jqXHR && jqXHR.responseText ? jqXHR.responseText : '';
        if (isThrottlingResponseText(responseText) || (errorThrown && isThrottlingResponseText(errorThrown))) {
            return 'Request throttled by ExpressionEngine. Please wait a moment and try again.';
        }

        // Some environments surface throttled/non-JSON responses as parser errors.
        if (textStatus === 'parsererror') {
            return 'Request throttled by ExpressionEngine. Please wait a moment and try again.';
        }

        if (errorThrown && String(errorThrown).toLowerCase().indexOf('json.parse') !== -1) {
            return 'Request throttled by ExpressionEngine. Please wait a moment and try again.';
        }

        if (errorThrown) {
            return baseMessage + ': ' + errorThrown;
        }

        return baseMessage;
    }

    /**
     * Parse JSON safely, including plain-text fallback from throttled responses.
     */
    function parseJsonResponse(responseText) {
        if (typeof responseText === 'object' && responseText !== null) {
            return responseText;
        }

        if (!responseText) {
            throw new Error('Empty response');
        }

        try {
            return JSON.parse(responseText);
        } catch (e) {
            var trimmed = String(responseText).trim().toLowerCase();

            if (trimmed.indexOf('<!doctype html') === 0 || trimmed.indexOf('<html') === 0) {
                throw new Error('Received an HTML response from the server. This can happen when returning via browser history after Control Panel settings changes. Reload this page and try again.');
            }

            if (isThrottlingResponseText(responseText)) {
                throw new Error('Request throttled by ExpressionEngine. Please wait a moment and try again.');
            }

            var responsePreview = String(responseText).replace(/\s+/g, ' ').slice(0, 180);
            throw new Error('Cache endpoint returned non-JSON content. Preview: ' + responsePreview);
        }
    }

    /**
     * Execute an AJAX request that expects JSON but tolerates plain-text fallbacks.
     */
    function requestJson(url, timeout, successHandler, baseErrorMessage, container, errorHandler) {
        $.ajax({
            url: url,
            method: 'GET',
            dataType: 'text',
            timeout: timeout,
            success: function(rawResponse) {
                try {
                    var response = parseJsonResponse(rawResponse);
                    successHandler(response);
                } catch (err) {
                    var parseErrorMessage = err.message || baseErrorMessage;
                    if (typeof errorHandler === 'function') {
                        errorHandler(parseErrorMessage);
                        return;
                    }
                    showError(container, parseErrorMessage);
                }
            },
            error: function(jqXHR, textStatus, errorThrown) {
                var errorMessage = buildAjaxErrorMessage(baseErrorMessage, jqXHR, textStatus, errorThrown);
                if (typeof errorHandler === 'function') {
                    errorHandler(errorMessage);
                    return;
                }
                showError(container, errorMessage);
            }
        });
    }
    
    /**
     * Load and render cache statistics
     * 
     * @param {boolean} forceRefresh - If true, bypass cache and recalculate (Issue #95)
     */
    function loadCacheStatistics(forceRefresh) {
        var container = $('#cache-statistics-container');
        if (!container.length) {
            return;
        }
        
        var ajaxUrl = container.data('ajax-url');
        if (!ajaxUrl) {
            showError(container, 'Missing AJAX URL configuration');
            return;
        }

        ajaxUrl = normalizeEndpointUrl(ajaxUrl);
        
        // Add force_refresh parameter if requested (Issue #95)
        if (forceRefresh) {
            ajaxUrl += (ajaxUrl.indexOf('?') !== -1 ? '&' : '?') + 'force_refresh=1';
        }
        
        requestJson(ajaxUrl, 30000, function(response) {
            if (response.success && response.data) {
                renderCacheStatistics(container, response.data);
            } else {
                showError(container, response.error || 'Failed to load cache statistics');
            }
        }, 'Failed to load cache statistics', container);
    }
    
    /**
     * Render cache statistics boxes
     */
    function renderCacheStatistics(container, data) {
        var html = '<div class="cache-statistics-container">';
        
        // Box 1: Total files
        html += '<div class="cache-stat-box primary">';
        html += '<div class="cache-stat-value primary">' + formatNumber(data.total_files || 0) + '</div>';
        html += '<div class="cache-stat-label primary">Processed images stored in cache</div>';
        html += '</div>';
        
        // Box 2: Total size
        html += '<div class="cache-stat-box success">';
        html += '<div class="cache-stat-value success">' + (data.total_size_formatted || '0 B') + '</div>';
        html += '<div class="cache-stat-label success">Total size of stored processed images</div>';
        html += '</div>';
        
        // Box 3: Active locations
        var connectionCount = data.connections ? Object.keys(data.connections).length : 0;
        html += '<div class="cache-stat-box info">';
        html += '<div class="cache-stat-value info">' + formatNumber(connectionCount) + '</div>';
        html += '<div class="cache-stat-label info">Number of active named connections</div>';
        html += '</div>';
        
        html += '</div>';
        
        // Add fade-in animation
        container.fadeOut(200, function() {
            container.html(html).fadeIn(300);
        });
    }
    
    /**
     * Load and render cache locations table
     */
    function loadCacheLocations() {
        var container = $('#cache-locations-container');
        if (!container.length) {
            return;
        }
        
        var ajaxUrl = container.data('ajax-url');
        if (!ajaxUrl) {
            showError(container, 'Missing AJAX URL configuration');
            return;
        }

        ajaxUrl = normalizeEndpointUrl(ajaxUrl);
        
        requestJson(ajaxUrl, 30000, function(response) {
            if (response.success && response.html) {
                // Add fade-in animation
                container.fadeOut(200, function() {
                    container.html(response.html).fadeIn(300);
                });
            } else {
                showError(container, response.error || 'Failed to load cache locations');
            }
        }, 'Failed to load cache locations', container);
    }
    
    /**
     * Load and render connection management table
     */
    function loadConnectionManagement() {
        var container = $('#connection-management-container');
        if (!container.length) {
            return;
        }
        
        var ajaxUrl = container.data('ajax-url');
        if (!ajaxUrl) {
            showError(container, 'Missing AJAX URL configuration');
            return;
        }

        ajaxUrl = normalizeEndpointUrl(ajaxUrl);
        
        requestJson(ajaxUrl, 30000, function(response) {
            if (response.success && response.html) {
                // Add fade-in animation
                container.fadeOut(200, function() {
                    container.html(response.html).fadeIn(300);
                });
            } else {
                showError(container, response.error || 'Failed to load connection management');
            }
        }, 'Failed to load connection management', container);
    }
    
    /**
     * Show error message in container
     */
    function showError(container, message) {
        var html = '<div class="cache-error-message" style="padding: 2rem; text-align: center;">';
        html += '<i class="fas fa-exclamation-triangle" style="font-size: 2rem; color: #ef4444; margin-bottom: 0.5rem;"></i>';
        html += '<div style="color: #dc2626; font-weight: 600; margin-bottom: 0.5rem;">Error Loading Data</div>';
        html += '<div style="color: #64748b; font-size: 0.875rem;">' + escapeHtml(message) + '</div>';
        html += '<button type="button" class="btn action retry-load" style="margin-top: 1rem;">Retry</button>';
        html += '</div>';
        
        container.fadeOut(200, function() {
            container.html(html).fadeIn(300);
            
            // Add retry handler
            container.find('.retry-load').on('click', function() {
                // Determine which loader to retry based on container ID
                if (container.attr('id') === 'cache-statistics-container') {
                    container.html(getLoadingHtml('Loading cache statistics...', 'Retrieving data from all configured connections'));
                    loadCacheStatistics();
                } else if (container.attr('id') === 'cache-locations-container') {
                    container.html(getLoadingHtml('Loading cache locations...', 'Retrieving data from all configured connections'));
                    loadCacheLocations();
                } else if (container.attr('id') === 'connection-management-container') {
                    container.html(getLoadingHtml('Loading connection management...', 'This may take a moment for remote storage'));
                    loadConnectionManagement();
                }
            });
        });
    }
    
    /**
     * Get loading HTML for retry operations
     */
    function getLoadingHtml(title, subtitle) {
        var html = '<div class="cache-loading-state" style="text-align: center; padding: 2rem;">';
        html += '<i class="fas fa-spinner fa-spin" style="font-size: 2rem; color: #6366f1; margin-bottom: 0.5rem;"></i>';
        html += '<div style="color: #64748b; font-weight: 500;">' + escapeHtml(title) + '</div>';
        html += '<div style="font-size: 0.875rem; color: #94a3b8; margin-top: 0.25rem;">' + escapeHtml(subtitle) + '</div>';
        html += '</div>';
        return html;
    }
    
    /**
     * Format number with thousands separators
     */
    function formatNumber(num) {
        return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    }
    
    /**
     * Escape HTML to prevent XSS
     */
    function escapeHtml(text) {
        var map = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#039;'
        };
        return text.replace(/[&<>"']/g, function(m) { return map[m]; });
    }
    
    /**
     * Run cache performance benchmark
     */
    function runCacheBenchmark() {
        var btn = $('#run-benchmark-btn');
        var resultsContainer = $('#benchmark-results');
        var benchmarkUrl = btn.data('benchmark-url');
        
        if (!benchmarkUrl) {
            alert('Benchmark URL not configured');
            return;
        }

        benchmarkUrl = normalizeEndpointUrl(benchmarkUrl);
        
        // Disable button and show loading state
        btn.prop('disabled', true);
        btn.html('<i class="fas fa-spinner fa-spin"></i> Running benchmark...');
        resultsContainer.html('<div class="alert alert-info">Running performance tests, please wait...</div>');
        
        requestJson(benchmarkUrl, 60000, function(response) {
            btn.prop('disabled', false);
            btn.html('<i class="fas fa-tachometer-alt"></i> Re-run Benchmark');

            if (response.success && response.html) {
                resultsContainer.html(response.html);

                // Add some styling
                resultsContainer.find('.benchmark-results').css({
                    'border': '1px solid #e2e8f0',
                    'border-radius': '8px',
                    'padding': '20px',
                    'background': '#f8fafc'
                });

                resultsContainer.find('.benchmark-section').css({
                    'margin-bottom': '20px',
                    'padding': '15px',
                    'background': 'white',
                    'border-radius': '6px'
                });

                resultsContainer.find('.benchmark-table').css({
                    'width': '100%',
                    'border-collapse': 'collapse'
                });

                resultsContainer.find('.benchmark-table th, .benchmark-table td').css({
                    'padding': '8px',
                    'text-align': 'left',
                    'border-bottom': '1px solid #e2e8f0'
                });

                resultsContainer.find('.recommendation-box').css({
                    'background': '#eff6ff',
                    'border-left': '4px solid #3b82f6',
                    'padding': '15px',
                    'border-radius': '4px'
                });

                resultsContainer.find('.recommended-value').css({
                    'font-size': '1.25rem',
                    'color': '#3b82f6',
                    'font-weight': 'bold'
                });
            } else {
                resultsContainer.html('<div class="alert alert-error">' + (response.error || 'Benchmark failed') + '</div>');
            }
        }, 'Benchmark failed', resultsContainer, function(errorMessage) {
            btn.prop('disabled', false);
            btn.html('<i class="fas fa-tachometer-alt"></i> Run Benchmark');
            resultsContainer.html('<div class="alert alert-error">' + escapeHtml(errorMessage) + '</div>');
        });
    }
    
    /**
     * Apply recommended threshold value
     */
    window.applyRecommendedThreshold = function(threshold) {
        // Find the threshold input field in Advanced Settings
        var thresholdField = $('input[name="img_cp_cache_log_preload_threshold"]');
        
        if (thresholdField.length) {
            thresholdField.val(threshold);
            thresholdField.trigger('change');
            
            // Show success message
            alert('Threshold value updated to ' + threshold + '. Remember to save your settings!');
            
            // Scroll to the threshold field
            $('html, body').animate({
                scrollTop: thresholdField.offset().top - 100
            }, 500);
        } else {
            alert('Could not find threshold field. Please navigate to Advanced Settings to update manually.');
        }
    };
    
    /**
     * Initialize async loading on page load
     */
    $(document).ready(function() {
        // Check if page was loaded after a cache operation (Issue #95)
        // EE alerts with 'audit-completed', 'cache-cleared' indicate successful operation
        var hasCacheAlert = $('.alert--success:contains("Audit"), .alert--success:contains("Clear")').length > 0;
        var forceRefresh = hasCacheAlert; // Force refresh if we just completed a cache operation
        
        // Load cache statistics if container exists
        if ($('#cache-statistics-container').length) {
            loadCacheStatistics(forceRefresh);
        }
        
        // Load cache locations if container exists (also force refresh after operations)
        if ($('#cache-locations-container').length) {
            loadCacheLocations();
        }
        
        // Load connection management if container exists
        if ($('#connection-management-container').length) {
            loadConnectionManagement();
        }
        
        // Bind benchmark button click handler
        $(document).on('click', '#run-benchmark-btn', function(e) {
            e.preventDefault();
            runCacheBenchmark();
        });
    });
    
})(jQuery);

