<?php

/**
 * JCOGS Image Pro - Cache Management Route
 * =========================================
 * Advanced cache configuration and management with named filesystem connections
 * 
 * @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
 * @link       https://JCOGS.net/
 * @since      2.0.0 - Named Connections System
 */

namespace JCOGSDesign\JCOGSImagePro\ControlPanel\Routes;

use Exception;
use ExpressionEngine\Library\CP\Table;

class Caching extends ImageAbstractRoute
{
    /**
     * @var string Route path for URL generation
     */
    protected $route_path = 'caching';

    /**
     * @var string Control panel page title
     */
    protected $cp_page_title;
    
    /**
     * @var array|null Cached named filesystem adapters configuration
     */
    private $cached_named_adapters_config = null;
    
    /**
     * @var array|null Cached named connections (connections only)
     */
    private $cached_named_connections = null;
    
    /**
     * Main cache management page processor
     * 
     * Handles all cache management requests including actions via URL segments.
     * EE7 MCP routing calls this method for all requests to this route.
     * 
     * @param mixed $id Route parameter (connection name for actions)
     * @return $this Fluent interface for EE7 routing
     */
    public function process($id = false)
    {
        // Check if this is a POST request with form data - redirect to proper route
        if (count($_POST) > 0) {
            ee()->functions->redirect(ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/update_settings'));
            return $this;
        }
        
        // Load language file
        $this->loadLanguage();
        
        // Check for error notifications and display them (Issue #89)
        $this->displayErrorNotifications();
        $this->loadLanguage('jcogs_img_pro_parameters');
        
        // Load CSS and JavaScript assets
        $this->_loadCacheManagementAssets();
                
        // Set page title and navigation
        $this->cp_page_title = lang('jcogs_img_pro_cp_cache_management');
        $this->build_sidebar($this->getCurrentSettings());
        $this->addBreadcrumb('caching', $this->cp_page_title);

        // Build the main page content
        $variables = $this->_buildPageContent();

        // Set the page body using EE7 view system
        $this->setBody('cache_management', $variables);

        return $this;
    }

    /**
     * Public wrapper to get cache locations table HTML for widgets or external use
     *
     * @return string HTML for cache locations table
     */
    public function getActiveCacheLocationsHtml(): string
    {
        try {
            $this->_loadCacheManagementAssets();
            $table_structure = $this->_generateCacheLocationsTableStructure();
            
            if (!$table_structure['has_data']) {
                if ($table_structure['error']) {
                    return '<div class="cache-error-message"><strong>Error building cache locations table:</strong><br>' . htmlspecialchars($table_structure['error'], ENT_QUOTES, 'UTF-8') . '</div>';
                }
                return '<div class="no-results"><p>No cache locations found.</p></div>';
            }
            
            // Create CP/Table using the shared structure
            $table = ee('CP/Table', [
                'autosort' => true,
                'autosearch' => false,
                'sortable' => true,
                'class' => 'tbl-ctrls tbl-fixed cache-locations-table'
            ]);
            
            $table->setColumns($table_structure['columns']);
            $table->setNoResultsText('No cache locations found.');
            
            // Convert structured data to CP/Table format
            $cp_table_data = [];
            foreach ($table_structure['data'] as $row) {
                $cp_table_data[] = [
                    $row['location_html'],
                    $row['type_html'],
                    $row['files_formatted'],
                    $row['size_formatted'],
                    $row['status_html'],
                    $row['last_updated'],
                    ['toolbar_items' => $row['toolbar_items']]
                ];
            }
            
            $table->setData($cp_table_data);
            $base_url = ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching');
            $view_data = $table->viewData($base_url);
            
            return ee('View')->make('_shared/table')->render($view_data);
            
        } catch (Exception $e) {
            return '<div class="cache-error-message"><strong>Error building cache locations table:</strong><br>' . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8') . '</div>';
        }
    }





    /**
     * Public method to load cache management assets (for widgets and external use)
     *
     * @return void
     */
    public function loadCacheManagementAssets(): void
    {
        $this->_loadCacheManagementAssets();
    }

    /**
     * Build the Default Cache Location group within the main form
     * 
     * @param \ExpressionEngine\Library\CP\Form $form The main form
     * @param array $named_connections Available named connections
     * @return void
     */
    private function _buildCacheControlsGroup($form, array $named_connections): void
    {
        // Create group for Default Cache Location
        $controls_group = $form->getGroup('Default Cache Location');
        
        // Default cache connection dropdown
        $connection_fieldset = $controls_group->getFieldSet('Default Cache Connection');
        $connection_fieldset->setDesc('Choose which named connection to use as the default cache location for new images.');
        
        // Build options for the dropdown
        $options = ['' => 'Select a connection...'];
        foreach ($named_connections as $connection_name => $config) {
            // Skip invalid connections
            if (!($config['is_valid'] ?? false)) {
                continue;
            }
            
            $adapter_type = $config['type'] ?? 'local';
            $type_label = strtoupper($adapter_type);
            $options[$connection_name] = $connection_name . ' (' . $type_label . ')';
        }
        
        // Get current default connection
        $current_default = $this->_getDefaultConnection();
        
        $connection_field = $connection_fieldset->getField('default_cache_connection', 'select');
        $connection_field->set('choices', $options);
        $connection_field->setValue($current_default);
        // Set attributes using the proper EE7 method - try the attrs property as a string
        $connection_field->set('attrs', 'autocomplete="off"');
    }

    /**
     * Build the Cache Locations group within the main form
     * 
     * @param \ExpressionEngine\Library\CP\Form $form The main form
     * @return void
     */
    private function _buildCacheLocationsGroup($form): void
    {
        // Create group for Cache Locations
        $locations_group = $form->getGroup('Cache Locations');

        // Use CP route endpoint to avoid front-end throttling path.
        $ajax_url = (string) ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/cache_locations_data');
        
        // ASYNC LOADING: Render loading placeholder instead of building table synchronously
        // JavaScript will load the actual table via AJAX using EE Action URL
        $table_fieldset = $locations_group->getFieldSet('Cache Location Details');
        $table_fieldset->setDesc('Detailed breakdown of cache files by connection.');
        
        $loading_html = '<div id="cache-locations-container" class="cache-locations-container" data-ajax-url="' . htmlspecialchars($ajax_url, ENT_QUOTES, 'UTF-8') . '">';
        $loading_html .= '<div class="cache-loading-state" style="text-align: center; padding: 2rem;">';
        $loading_html .= '<i class="fas fa-spinner fa-spin" style="font-size: 2rem; color: #6366f1; margin-bottom: 0.5rem;"></i>';
        $loading_html .= '<div style="color: #64748b; font-weight: 500;">Loading cache locations...</div>';
        $loading_html .= '<div style="font-size: 0.875rem; color: #94a3b8; margin-top: 0.25rem;">Retrieving data from all configured connections</div>';
        $loading_html .= '</div>';
        $loading_html .= '</div>';
        
        // Use HTML field to display the loading placeholder
        /** @var \ExpressionEngine\Library\CP\Form\Fields\Html $locations_field */
        $locations_field = $table_fieldset->getField('cache_locations_display', 'html');
        $locations_field->setContent($loading_html);
    }

    /**
     * Build the Image Cache Settings group within the main form
     * 
     * @param \ExpressionEngine\Library\CP\Form $form The main form
     * @param array $current_settings Current addon settings
     * @return void
     */
    private function _buildCacheSettingsGroup($form, array $current_settings): void
    {
        // Create group for Image Cache Settings
        $settings_group = $form->getGroup(lang('jcogs_img_cp_cache_settings'));

        // ENHANCED: Default cache duration with natural language support
        $duration_fieldset = $settings_group->getFieldSet(lang('jcogs_img_pro_cp_default_cache_duration'));
        $duration_fieldset->setDesc(lang('jcogs_img_pro_cp_default_cache_duration_desc'));

        // Initialize duration services
        try {
            $duration_parser     = new \JCOGSDesign\JCOGSImagePro\Service\DurationParser();
            $duration_form_field = new \JCOGSDesign\JCOGSImagePro\Service\DurationFormField($duration_parser);

            // Use enhanced duration field with natural language support
            $current_duration = $current_settings['img_cp_default_cache_duration'] ?? '2678400';
            $duration_field   = $duration_form_field->createField(
                $duration_fieldset,
                'img_cp_default_cache_duration',
                $current_duration,
                'cache',
            );

            // Add help text for better UX
            $help_text = $duration_form_field->getHelpText('cache');
            $duration_fieldset->setDesc(lang('jcogs_img_pro_cp_default_cache_duration_desc') . '<br><small style="color: #64748b;">' . $help_text . '</small>');

        }
        catch (Exception $e) {
            // Fallback to basic field if duration services fail
            $duration_fieldset->getField('img_cp_default_cache_duration', 'text')
                ->setValue($current_settings['img_cp_default_cache_duration'] ?? '2678400');
        }

        // Cache Auto-Management
        $auto_manage_fieldset = $settings_group->getFieldSet(lang('jcogs_img_cp_enable_cache_auto_manage'));
        $auto_manage_fieldset->setDesc(lang('jcogs_img_cp_enable_cache_auto_manage_desc'));

        $auto_manage_fieldset->getField('img_cp_cache_auto_manage', 'yes_no')
            ->setValue($current_settings['img_cp_cache_auto_manage'] ?? 'y');

        // Cache Log Preload Threshold
        $threshold_fieldset = $settings_group->getFieldSet(lang('jcogs_img_pro_cp_cache_log_preload_threshold'));
        $threshold_fieldset->setDesc(lang('jcogs_img_pro_cp_cache_log_preload_threshold_desc'));
        $threshold_fieldset->getField('img_cp_cache_log_preload_threshold', 'text')
            ->setValue($current_settings['img_cp_cache_log_preload_threshold'] ?? '10000')
            ->setRequired(true);

        // Cache Performance Optimizer
        $optimizer_fieldset = $settings_group->getFieldSet(lang('jcogs_img_pro_optimize_cache_performance') ?: 'Optimize Cache Performance');
        $optimizer_fieldset->setDesc('Run a performance benchmark to determine the optimal cache preload threshold for your server environment. This tests both full preload and on-demand loading strategies.');

        // Use CP route endpoint to avoid front-end throttling path.
        $benchmark_url = (string) ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/benchmark_data');

        $benchmark_html  = '<div id="cache-benchmark-container">';
        $benchmark_html .= '<button type="button" class="btn action" id="run-benchmark-btn" data-benchmark-url="' . htmlspecialchars($benchmark_url, ENT_QUOTES, 'UTF-8') . '">';
        $benchmark_html .= '<i class="fas fa-tachometer-alt"></i> ' . (lang('jcogs_img_pro_optimize_cache_performance') ?: 'Optimize Cache Performance');
        $benchmark_html .= '</button>';
        $benchmark_html .= '<div id="benchmark-results" style="margin-top: 20px;"></div>';
        $benchmark_html .= '</div>';

        /** @var \ExpressionEngine\Library\CP\Form\Fields\Html $optimizer_field */
        $optimizer_field = $optimizer_fieldset->getField('cache_optimizer', 'html');
        $optimizer_field->setContent($benchmark_html);
    }

    /**
     * Build the Image Cache Status group within the main form
     * 
     * @param \ExpressionEngine\Library\CP\Form $form The main form
     * @return void
     */
    private function _buildCacheStatusGroup($form): void
    {
        // Create group for Image Cache Status
        $status_group = $form->getGroup('Image Cache Status');

        // Use CP route endpoint to avoid front-end throttling path.
        $ajax_url = (string) ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/cache_statistics_data');
        
        // ASYNC LOADING: Render loading placeholder instead of calculating stats synchronously
        // JavaScript will load the actual data via AJAX using EE Action URL
        $status_html = '<div id="cache-statistics-container" class="cache-statistics-container" data-ajax-url="' . htmlspecialchars($ajax_url, ENT_QUOTES, 'UTF-8') . '">';
        $status_html .= '<div class="cache-loading-state">';
        $status_html .= '<div class="cache-stat-box loading">';
        $status_html .= '<i class="fas fa-spinner fa-spin" style="font-size: 2rem; color: #6366f1; margin-bottom: 0.5rem;"></i>';
        $status_html .= '<div class="cache-stat-label" style="color: #64748b;">Loading cache statistics...</div>';
        $status_html .= '<div class="cache-stat-sublabel" style="font-size: 0.875rem; color: #94a3b8; margin-top: 0.25rem;">This may take a moment for remote storage</div>';
        $status_html .= '</div>';
        $status_html .= '</div>';
        $status_html .= '</div>';
        
        $status_fieldset = $status_group->getFieldSet('Cache Statistics');
        $status_fieldset->setDesc('Current cache status across all configured connections.');
        
        // Use an HTML field to display the loading placeholder
        // JavaScript will replace this with actual data
        /** @var \ExpressionEngine\Library\CP\Form\Fields\Html $status_field */
        $status_field = $status_fieldset->getField('cache_status_display', 'html');
        $status_field->setContent($status_html);
    }

    /**
     * Build the Existing Connections group within the main form
     * 
     * @param \ExpressionEngine\Library\CP\Form $form The main form
     * @param array $named_connections Available named connections
     * @return void
     */
    private function _buildExistingConnectionsGroup($form, array $named_connections): void
    {
        // Create group for Existing Connections
        $connections_group = $form->getGroup('Existing Connections');

        // Use CP route endpoint to avoid front-end throttling path.
        $ajax_url = (string) ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/connection_management_data');
        
        // ASYNC LOADING: Render loading placeholder instead of building table synchronously
        // JavaScript will load the actual data via AJAX using EE Action URL
        $loading_html = '<div id="connection-management-container" class="connection-management-container" data-ajax-url="' . htmlspecialchars($ajax_url, ENT_QUOTES, 'UTF-8') . '">';
        $loading_html .= '<div class="connection-loading-state">';
        $loading_html .= '<div style="text-align: center; padding: 2rem;">';
        $loading_html .= '<i class="fas fa-spinner fa-spin" style="font-size: 2rem; color: #6366f1; margin-bottom: 0.5rem;"></i>';
        $loading_html .= '<div style="color: #64748b; margin-top: 0.5rem;">Loading connection management...</div>';
        $loading_html .= '<div style="font-size: 0.875rem; color: #94a3b8; margin-top: 0.25rem;">This may take a moment for remote storage</div>';
        $loading_html .= '</div>';
        $loading_html .= '</div>';
        $loading_html .= '</div>';
        
        $connections_fieldset = $connections_group->getFieldSet('Connection Management');
        $connections_fieldset->setDesc('Manage your named filesystem connections. <a href="' . ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/add_connection') . '" class="button button--small button--primary" style="float:right; margin-right:25px; margin-bottom:1em;">Add New Connection</a>');
        
        // Use an HTML field to display the loading placeholder
        // JavaScript will replace this with actual data
        /** @var \ExpressionEngine\Library\CP\Form\Fields\Html $connections_field */
        $connections_field = $connections_fieldset->getField('connection_management_display', 'html');
        $connections_field->setContent($loading_html);
    }

    /**
     * Build all page content sections
     * 
     * @return array Template variables for the view
     */
    private function _buildPageContent(): array
    {
        // Get current settings and cache information
        $current_settings = $this->getCurrentSettings();
        $named_connections = $this->_getNamedConnections();
        
        // Create a single form for the entire page (like legacy approach)
        $form = ee('CP/Form');
        
        // Set the base URL where the form should be processed (EE7 method)
        $form_action_url = ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/update_settings');
        $form->setBaseUrl($form_action_url);
        
        // Configure form to not show automatic buttons
        $form->set('save_btn_text', 'Save Cache Settings');
        $form->set('save_btn_text_working', 'Saving');
        $form->set('hide_top_buttons', true);  // Hide the top buttons
        
        // Build sections as groups within the main form
        $this->_buildCacheStatusGroup($form);
        $this->_buildEnvironmentCompatibilityGroup($form);
        $this->_buildCacheLocationsGroup($form);  // Add Cache Locations group
        $this->_buildExistingConnectionsGroup($form, $named_connections); // Add Existing Connections group
        $this->_buildCacheControlsGroup($form, $named_connections);
        $this->_buildCacheSettingsGroup($form, $current_settings);

        // Issue #120 diagnostics: optional URL/scheme diagnostics panel
        // Enable via query param: &imgpro_url_diag=1
        if ((string) ee()->input->get('imgpro_url_diag') === '1') {
            $this->_buildUrlDiagnosticsGroup($form);
        }
        
        $variables = [
            'cp_page_title' => $this->cp_page_title,
            'base_url' => ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching'),
            
            // Single main form containing all sections as groups
            'main_form' => $form,
            'form_url' => ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/update_settings'),
        ];

        return $variables;
    }

    /**
     * Build URL diagnostics group (Issue #120 support troubleshooting)
     *
     * Shows effective config values and request/proxy server inputs used to
     * determine ACT URL scheme generation.
     *
     * @param \ExpressionEngine\Library\CP\Form $form
     * @return void
     */
    private function _buildUrlDiagnosticsGroup($form): void
    {
        $diag_group = $form->getGroup('URL Diagnostics (Issue #120)');
        $diag_fieldset = $diag_group->getFieldSet('Resolved URL and Request Context');
        $diag_fieldset->setDesc('Temporary diagnostics enabled via <code>imgpro_url_diag=1</code>. Remove this query parameter to hide.');

        $site_url_raw = ee()->config->item('site_url', '', true);
        $site_url_resolved = ee()->config->item('site_url');
        $base_url_raw = ee()->config->item('base_url', '', true);
        $base_url_resolved = ee()->config->item('base_url');
        $site_index = ee()->config->item('site_index');
        $fetch_site_index = ee()->functions->fetch_site_index(0, 0);
        $secure_request = $this->isSecureRequest() ? 'true' : 'false';
        $endpoint_cache_stats = (string) ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/cache_statistics_data');
        $endpoint_cache_locations = (string) ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/cache_locations_data');
        $endpoint_connection_mgmt = (string) ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/connection_management_data');

        $server_keys = [
            'HTTPS',
            'SERVER_PORT',
            'HTTP_X_FORWARDED_PROTO',
            'HTTP_X_FORWARDED_SSL',
            'REQUEST_SCHEME',
            'HTTP_HOST',
            'REQUEST_URI',
        ];

        $rows = '';
        $rows .= $this->_buildDiagnosticsRow('config.site_url (raw)', $site_url_raw);
        $rows .= $this->_buildDiagnosticsRow('config.site_url (resolved)', $site_url_resolved);
        $rows .= $this->_buildDiagnosticsRow('config.base_url (raw)', $base_url_raw);
        $rows .= $this->_buildDiagnosticsRow('config.base_url (resolved)', $base_url_resolved);
        $rows .= $this->_buildDiagnosticsRow('config.site_index', $site_index);
        $rows .= $this->_buildDiagnosticsRow('functions.fetch_site_index(0,0)', $fetch_site_index);
        $rows .= $this->_buildDiagnosticsRow('ImageAbstractRoute::isSecureRequest()', $secure_request);
        $rows .= $this->_buildDiagnosticsRow('CP/URL cache_statistics_data', $endpoint_cache_stats);
        $rows .= $this->_buildDiagnosticsRow('CP/URL cache_locations_data', $endpoint_cache_locations);
        $rows .= $this->_buildDiagnosticsRow('CP/URL connection_management_data', $endpoint_connection_mgmt);

        foreach ($server_keys as $key) {
            $value = isset($_SERVER[$key]) ? (string) $_SERVER[$key] : '(not set)';
            $rows .= $this->_buildDiagnosticsRow('$_SERVER[' . $key . ']', $value);
        }

        $html  = '<div class="tbl-ctrls">';
        $html .= '<table class="mainTable" border="0" cellspacing="0" cellpadding="0">';
        $html .= '<thead><tr><th style="width:34%;">Key</th><th>Value</th></tr></thead>';
        $html .= '<tbody>' . $rows . '</tbody>';
        $html .= '</table>';
        $html .= '</div>';

        /** @var \ExpressionEngine\Library\CP\Form\Fields\Html $diag_field */
        $diag_field = $diag_fieldset->getField('url_diagnostics_display', 'html');
        $diag_field->setContent($html);
    }

    /**
     * Build a diagnostics table row.
     *
     * @param string $key
     * @param mixed $value
     * @return string
     */
    private function _buildDiagnosticsRow(string $key, $value): string
    {
        $key_safe = htmlspecialchars($key, ENT_QUOTES, 'UTF-8');
        $value_safe = htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');

        return '<tr><td><code>' . $key_safe . '</code></td><td><code>' . $value_safe . '</code></td></tr>';
    }

    /**
     * Build environment compatibility diagnostics group.
     *
     * This guard highlights common URL/configuration mismatches that can cause:
     * - ACT AJAX requests to fail in the Control Panel
     * - local upload sources to be treated as remote URLs
     *
     * @param \ExpressionEngine\Library\CP\Form $form
     * @return void
     */
    private function _buildEnvironmentCompatibilityGroup($form): void
    {
        $issues = $this->_collectEnvironmentCompatibilityIssues();
        if (empty($issues)) {
            return;
        }

        $diag_group = $form->getGroup('Environment Compatibility');
        $diag_fieldset = $diag_group->getFieldSet('Potential URL configuration problems');
        $diag_fieldset->setDesc('Advisory checks for mixed scheme/host settings that can affect AJAX loading and local image source detection.');

        $issue_items = '';
        foreach ($issues as $issue) {
            $title = htmlspecialchars((string) ($issue['title'] ?? ''), ENT_QUOTES, 'UTF-8');
            $guidance = htmlspecialchars((string) ($issue['guidance'] ?? ''), ENT_QUOTES, 'UTF-8');
            $issue_items .= '<li style="margin-bottom:0.75rem;"><strong>' . $title . '</strong><br><span style="color:#334155;">' . $guidance . '</span></li>';
        }

        $count = count($issues);
        $noun = $count === 1 ? 'issue' : 'issues';

        $html  = '<div class="app-notice app-notice--inline app-notice---important" style="margin:0.5rem 0 1rem 0;">';
        $html .= '<div class="app-notice__tag"><span class="app-notice__icon"></span></div>';
        $html .= '<div class="app-notice__content" style="padding:0.75rem 1rem;">';
        $html .= '<p style="margin:0 0 0.75rem 0;"><strong>Image Pro found ' . $count . ' configuration ' . $noun . ' that may cause unreliable behaviour.</strong></p>';
        $html .= '<ul style="margin:0 0 0.75rem 1.25rem;">' . $issue_items . '</ul>';
        $html .= '<p style="margin:0;color:#475569;">Recommended first step: define canonical <code>base_url</code> and <code>base_path</code> values in ExpressionEngine URL and Path Settings (CP > Settings > URL and Path Settings), then use <code>{base_url}</code> and <code>{base_path}</code> as prefixes for related local URLs and paths where possible. This keeps configuration portable: if the site moves server, update the base values once and dependent locations continue to resolve.</p>';
        $html .= '</div>';
        $html .= '</div>';

        /** @var \ExpressionEngine\Library\CP\Form\Fields\Html $diag_field */
        $diag_field = $diag_fieldset->getField('environment_compatibility_display', 'html');
        $diag_field->setContent($html);
    }

    /**
     * Collect compatibility issues for URL/scheme/host configuration.
     *
     * @return array<int, array{title:string,guidance:string}>
     */
    private function _collectEnvironmentCompatibilityIssues(): array
    {
        $issues = [];

        $add_issue = static function (array &$bucket, string $key, string $title, string $guidance): void {
            if (!isset($bucket[$key])) {
                $bucket[$key] = [
                    'title' => $title,
                    'guidance' => $guidance,
                ];
            }
        };

        $request_scheme = $this->isSecureRequest() ? 'https' : 'http';
        $request_host = $this->_normaliseHost((string) ($_SERVER['HTTP_HOST'] ?? ''));

        $base_url = trim((string) ee()->config->item('base_url'));
        $site_url = trim((string) ee()->config->item('site_url'));

        $base_parts = $this->_parseAbsoluteUrl($base_url);
        $site_parts = $this->_parseAbsoluteUrl($site_url);

        if ($request_scheme === 'https') {
            if (is_array($base_parts) && $base_parts['scheme'] === 'http') {
                $add_issue(
                    $issues,
                    'base_url_scheme_mismatch',
                    'base_url uses HTTP while the Control Panel request is HTTPS.',
                    'Update base_url to HTTPS. Mixed HTTP/HTTPS settings can trigger browser request blocking and "network" AJAX errors.'
                );
            }

            if (is_array($site_parts) && $site_parts['scheme'] === 'http') {
                $add_issue(
                    $issues,
                    'site_url_scheme_mismatch',
                    'site_url uses HTTP while the Control Panel request is HTTPS.',
                    'Update site_url to HTTPS. This reduces mixed-content risk for front-end action requests and diagnostics endpoints.'
                );
            }
        }

        if (is_array($base_parts) && is_array($site_parts)) {
            if ($base_parts['scheme'] !== $site_parts['scheme']) {
                $add_issue(
                    $issues,
                    'base_site_scheme_drift',
                    'base_url and site_url use different URL schemes.',
                    'Use the same scheme for both values. Scheme drift can cause inconsistent URL generation and hard-to-debug request failures.'
                );
            }

            if ($base_parts['host'] !== $site_parts['host']) {
                $add_issue(
                    $issues,
                    'base_site_host_drift',
                    'base_url and site_url use different hosts.',
                    'Use one canonical host for local site assets and ACT endpoints to avoid cross-origin behaviour and source misclassification.'
                );
            }
        }

        $reference_host = $base_parts['host'] ?? $site_parts['host'] ?? $request_host;
        $reference_scheme = $base_parts['scheme'] ?? $site_parts['scheme'] ?? $request_scheme;

        try {
            $upload_destinations = ee('Model')->get('UploadDestination')->all();
        } catch (\Throwable $e) {
            $upload_destinations = [];
        }

        foreach ($upload_destinations as $destination) {
            if (!$this->_isLocalUploadDestination($destination)) {
                continue;
            }

            $destination_name = trim((string) ($destination->name ?? 'upload directory'));
            if ($destination_name === '') {
                $destination_name = 'upload directory';
            }

            $url = trim((string) ($destination->url ?? ''));
            if ($url === '') {
                continue;
            }

            $url_parts = $this->_parseAbsoluteUrl($url);
            if (!is_array($url_parts)) {
                continue;
            }

            if ($request_scheme === 'https' && $url_parts['scheme'] === 'http') {
                $add_issue(
                    $issues,
                    'upload_http_' . $destination_name,
                    'Upload directory "' . $destination_name . '" URL uses HTTP while Control Panel is HTTPS.',
                    'Set this upload URL to HTTPS. Mixed scheme URLs often trigger blocked browser requests and can break asynchronous panels.'
                );
            }

            if ($reference_scheme !== '' && $url_parts['scheme'] !== $reference_scheme) {
                $add_issue(
                    $issues,
                    'upload_scheme_drift_' . $destination_name,
                    'Upload directory "' . $destination_name . '" URL scheme differs from your main site URL scheme.',
                    'Align upload URL schemes with your canonical site configuration to avoid mixed behaviour during source resolution.'
                );
            }

            if ($reference_host !== '' && $url_parts['host'] !== $reference_host) {
                $add_issue(
                    $issues,
                    'upload_host_drift_' . $destination_name,
                    'Upload directory "' . $destination_name . '" URL host differs from your canonical site host.',
                    'Image Pro may treat this source as remote, forcing HTTP fetches instead of local file access. Use the same host where local retrieval is expected.'
                );
            }

            if (filter_var($url_parts['host'], FILTER_VALIDATE_IP)) {
                $add_issue(
                    $issues,
                    'upload_ip_host_' . $destination_name,
                    'Upload directory "' . $destination_name . '" URL uses an IP address host.',
                    'Prefer a stable domain host for local uploads. IP-based hosts are a common source of local-vs-remote detection mismatches.'
                );
            }

            if (count($issues) >= 12) {
                break;
            }
        }

        return array_values($issues);
    }

    /**
     * Determine whether an EE upload destination uses the local adapter.
     *
     * @param mixed $destination
     * @return bool
     */
    private function _isLocalUploadDestination($destination): bool
    {
        $adapter = strtolower(trim((string) ($destination->adapter ?? '')));

        // EE local upload directories are typically stored as empty adapter or "local".
        return $adapter === '' || $adapter === 'local';
    }

    /**
     * Parse absolute URL into normalised parts.
     *
     * @param string $url
     * @return array{scheme:string,host:string}|null
     */
    private function _parseAbsoluteUrl(string $url): ?array
    {
        if ($url === '' || !preg_match('#^https?://#i', $url)) {
            return null;
        }

        $parts = parse_url($url);
        if (!is_array($parts) || empty($parts['host']) || empty($parts['scheme'])) {
            return null;
        }

        return [
            'scheme' => strtolower((string) $parts['scheme']),
            'host' => $this->_normaliseHost((string) $parts['host']),
        ];
    }

    /**
     * Normalise a host string for comparisons.
     *
     * @param string $host
     * @return string
     */
    private function _normaliseHost(string $host): string
    {
        $host = trim(strtolower($host));

        if (str_contains($host, ':')) {
            $host = explode(':', $host)[0];
        }

        return rtrim($host, '.');
    }

    /**
     * Count the number of local connections
     * 
     * Used to enforce the policy that at least one local connection must always exist.
     * 
     * @param array $connections Named connections array
     * @return int Number of local connections
     */
    private function _countLocalConnections(array $connections): int
    {
        $count = 0;
        foreach ($connections as $connection) {
            $type = $connection['type'] ?? '';
            if (strtolower($type) === 'local') {
                $count++;
            }
        }
        return $count;
    }

    /**
     * Determine cache validity based on stored connection validity and adapter info
     * 
     * @param array $adapter_info Adapter information from CacheManagementService
     * @param string $connection_name Connection name
     * @param string $adapter_type Adapter type
     * @return string Status ('valid' or 'invalid')
     */
    private function _determineCacheValidity(array $adapter_info, string $connection_name, string $adapter_type): string
    {
        // If there's a critical error in the adapter info, it's invalid
        if (isset($adapter_info['error']) || isset($adapter_info['critical_error'])) {
            return 'invalid';
        }
        
        // Get the named connections configuration to check stored validity
        $named_connections = $this->_getNamedConnections();
        
        // Check if connection exists and use its stored validity
        if (isset($named_connections[$connection_name])) {
            $connection_config = $named_connections[$connection_name];
            $is_valid = $connection_config['is_valid'] ?? false;
            
            return $is_valid ? 'valid' : 'invalid';
        }
        
        // Fallback: if connection not found in named connections, treat as invalid
        return 'invalid';
    }

    /**
     * Format last updated date from adapter info
     * 
     * @param array $adapter_info Adapter information
     * @return string Formatted date or 'Never'
     */
    private function _formatLastUpdated(array $adapter_info): string
    {
        if (!isset($adapter_info['last_modified']) || $adapter_info['last_modified'] === null) {
            return 'Never';
        }
        
        $timestamp = $adapter_info['last_modified'];
        
        // Handle different timestamp formats
        if (is_numeric($timestamp)) {
            // Unix timestamp
            $formatted = date('Y-m-d H:i:s', (int)$timestamp);
            return $formatted;
        } elseif (is_string($timestamp)) {
            // Try to parse as date string
            $parsed = strtotime($timestamp);
            if ($parsed !== false) {
                $formatted = date('Y-m-d H:i:s', $parsed);
                return $formatted;
            }
        }
        
        return 'Never';
    }

    /**
     * Generate common cache locations table data structure
     * 
     * This method provides the common table structure and data that can be used by both
     * the main caching page (as form table) and the widget (as standalone table).
     * 
     * @return array Table structure with columns and data
     */
    private function _generateCacheLocationsTableStructure(): array
    {
        try {
            // Calculate cache stats inline since _calculate_cache_statistics was removed
            $named_connections = $this->_getNamedConnections();
            $location_stats = [];
            
            // If no connections, return empty structure
            if (empty($named_connections)) {
                return [
                    'has_data' => false,
                    'columns' => [],
                    'data' => [],
                    'error' => null
                ];
            }
            
            // Build location stats for table display
            foreach ($named_connections as $connection_name => $config) {
                try {
                    $adapter_type = $config['type'] ?? 'local';
                    
                    // Get cache directory for operations
                    $cache_dir_operational = $this->filesystem_service->get_adapter_cache_path($connection_name, true);
                    $cache_dir_display = $this->_getCacheDirectoryForConnection($connection_name, $config);
                    
                    // Audit cache
                    $adapter_info = $this->filesystem_service->audit_adapter_cache($connection_name, $cache_dir_operational);
                    
                    // Skip cache locations with no files
                    if (($adapter_info['file_count'] ?? 0) == 0) {
                        continue;
                    }
                    
                    $location_key = $cache_dir_display . ' (' . $connection_name . ')';
                    $status = $this->_determineCacheValidity($adapter_info, $connection_name, $adapter_type);
                    $last_updated = $this->_formatLastUpdated($adapter_info);
                    
                    $location_stats[$location_key] = [
                        'cache_dir' => $cache_dir_display,
                        'connection_name' => $connection_name,
                        'adapter_type' => $adapter_type,
                        'file_count' => $adapter_info['file_count'] ?? 0,
                        'total_size' => $adapter_info['total_size'] ?? 0,
                        'size_formatted' => $this->utilities_service->format_file_size($adapter_info['total_size'] ?? 0),
                        'status' => $status,
                        'last_updated' => $last_updated,
                    ];
                    
                } catch (Exception $e) {
                    // Skip problematic connections
                    continue;
                }
            }
            
            if (empty($location_stats)) {
                return [
                    'has_data' => false,
                    'columns' => [],
                    'data' => [],
                    'error' => null
                ];
            }
            
            // Define common column structure
            $columns = [
                'location' => ['label' => 'Cache Location', 'sort' => true, 'encode' => false],
                'type' => ['label' => 'Connection', 'sort' => true, 'class' => 'center', 'encode' => false],
                'files' => ['label' => 'Files', 'sort' => true, 'class' => 'center'],
                'size' => ['label' => 'Size', 'sort' => true, 'class' => 'center'],
                'status' => ['label' => 'Status', 'sort' => true, 'class' => 'center', 'encode' => false],
                'last_updated' => ['label' => 'Last Updated', 'sort' => true, 'class' => 'center'],
                'actions' => ['label' => 'Actions', 'type' => Table::COL_TOOLBAR, 'sort' => false, 'class' => 'center']
            ];
            
            // Generate common table data
            $table_data = [];
            foreach ($location_stats as $location_key => $location) {
                // Build location info with clean path display only
                $location_html = '<div class="cache-location-info">';
                $location_html .= '<div style="font-weight: 600;">' . htmlspecialchars($location['cache_dir'], ENT_QUOTES, 'UTF-8') . '</div>';
                $location_html .= '</div>';
                
                // Build connection name display instead of adapter type
                $type_html = '<span class="st-' . strtolower($location['adapter_type']) . '">' . htmlspecialchars($location['connection_name'], ENT_QUOTES, 'UTF-8') . '</span>';
                
                // Build status badge
                $status_html = '';
                if ($location['status'] === 'valid') {
                    $status_html = '<span style="background-color: #f0fdf4; color: #16a34a; padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 600;">Valid</span>';
                } else {
                    $status_html = '<span style="background-color: #fef2f2; color: #dc2626; padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 600;">Invalid</span>';
                }
                
                // Build toolbar actions using EE7 toolbar format for consistency
                $toolbar_items = [];
                if ($location['file_count'] > 0 && $location['status'] === 'valid') {
                    $toolbar_items = [
                        'audit' => [
                            'href' => ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/audit_cache/' . urlencode($location['connection_name'])),
                            'title' => 'Run audit to clean expired cache files',
                            'class' => 'cache-audit-btn',
                            'data-connection' => $location['connection_name'],
                            'data-action' => 'audit',
                            'content' => '<i class="fas fa-broom"></i>'
                        ],
                        'clear' => [
                            'href' => ee('CP/URL')->make('addons/settings/jcogs_img_pro/caching/clear_cache/' . urlencode($location['connection_name'])),
                            'title' => 'Clear all cache files from this location',
                            'class' => 'cache-clear-btn',
                            'data-connection' => $location['connection_name'],
                            'data-action' => 'clear',
                            'data-confirm' => 'Clear all cache files from "' . $location['connection_name'] . '"?',
                            'content' => '<i class="fas fa-trash-alt"></i>'
                        ]
                    ];
                }
                
                // Build row data (can be used for both contexts)
                $table_data[] = [
                    'location_html' => $location_html,
                    'type_html' => $type_html,
                    'files_formatted' => number_format($location['file_count']),
                    'size_formatted' => $location['size_formatted'],
                    'status_html' => $status_html,
                    'last_updated' => $location['last_updated'],
                    'toolbar_items' => $toolbar_items,
                    // Raw data for different formatting needs
                    'location_raw' => $location
                ];
            }
            
            return [
                'has_data' => true,
                'columns' => $columns,
                'data' => $table_data,
                'error' => null
            ];
            
        } catch (Exception $e) {
            return [
                'has_data' => false,
                'columns' => [],
                'data' => [],
                'error' => $e->getMessage()
            ];
        }
    }

    /**
     * Get cache directory for a named connection
     * 
     * Updated to use the named connection configuration instead of legacy adapter settings.
     * Uses the same logic as get_connection_cache_path for consistency.
     * 
     * @param string $connection_name Connection name
     * @param array $config Connection configuration  
     * @return string Cache directory path
     */
    private function _getCacheDirectoryForConnection(string $connection_name, array $config): string
    {
        try {
            // Use the same logic as get_connection_cache_path for consistency
            return $this->_getConnectionCachePath($config);
        } catch (Exception $e) {
            // Fallback to default directory
            return '/images/jcogs_img_pro/cache/';
        }
    }
    
    /**
     * Get the cache path for a connection
     * 
     * @param array $connection Connection configuration
     * @return string Cache path
     */
    private function _getConnectionCachePath(array $connection): string
    {
        $config = $connection['config'] ?? [];
        $type = $connection['type'] ?? 'unknown';
        
        switch ($type) {
            case 'local':
                return $config['cache_directory'] ?? '/images\/jcogs_img_pro\/cache';
            case 's3':
            case 'r2':
            case 'gcs':
            case 'dospaces':
            case 'azure':
                // Cloud: Use server_path as the cache directory within the bucket
                // The bucket is already configured in the Flysystem adapter
                // Paths should be relative to the bucket root, not include bucket name
                $server_path = $config['server_path'] ?? '';
                return !empty($server_path) ? trim($server_path, '/') : 'cache/jcogs_img_pro';
            default:
                return 'Unknown';
        }
    }
    
    /**
     * Get the current default connection name (with caching for efficiency)
     * 
     * @return string Default connection name
     */
    private function _getDefaultConnection(): string
    {
        $config = $this->_getNamedAdaptersConfig();
        return $config['default_connection'] ?? '';
    }

    /**
     * Get cached named filesystem adapters configuration (with caching for efficiency)
     * 
     * @return array Full named adapters configuration with connections and default_connection
     */
    private function _getNamedAdaptersConfig(): array
    {
        if ($this->cached_named_adapters_config === null) {
            $this->cached_named_adapters_config = $this->settings_service->getNamedFilesystemAdapters();
        }
        
        return $this->cached_named_adapters_config;
    }

    /**
     * Get named filesystem connections from settings (with caching for efficiency)
     * 
     * @return array Named connections configuration
     */
    private function _getNamedConnections(): array
    {
        if ($this->cached_named_connections === null) {
            $config = $this->_getNamedAdaptersConfig();
            $this->cached_named_connections = $config['connections'] ?? [];
        }
        
        // Return connections as-is from settings, preserving stored validity status
        // The is_valid property should already be set in the stored configuration
        return $this->cached_named_connections;
    }

    /**
     * Load cache management CSS and JavaScript assets
     * 
     * Loads the cache management stylesheet and async cache loading JavaScript.
     * Called during page initialisation to ensure proper styling and functionality
     * for cache management interfaces.
     * 
     * @return void
     */
    private function _loadCacheManagementAssets()
    {
        $asset_version = defined('JCOGS_IMG_PRO_VERSION') ? JCOGS_IMG_PRO_VERSION : 'dev';
        $script_relative_path = 'jcogs_img_pro/javascript/cache-management-async.js';
        $script_disk_path = PATH_THIRD_THEMES . $script_relative_path;
        $script_version = $asset_version;

        if (is_string($script_disk_path) && is_file($script_disk_path)) {
            $mtime = @filemtime($script_disk_path);
            if ($mtime !== false) {
                $script_version = $asset_version . '-' . (string) $mtime;
            }
        }

        // Load CSS
        ee()->cp->add_to_head('<link rel="stylesheet" type="text/css" href="' . URL_THIRD_THEMES . 'jcogs_img_pro/css/cache-management.css" />');
        
        // Load async cache loading JavaScript
        ee()->cp->add_to_foot('<script defer src="' . URL_THIRD_THEMES . 'jcogs_img_pro/javascript/cache-management-async.js?v=' . rawurlencode((string) $script_version) . '"></script>');
    }
}

