<?php
if (!defined('ABSPATH')) exit;

/**
 * CAS Compatibility Layer
 *
 * Read-only compatibility helpers for Installation Doctor and Support Report.
 * No settings writes, no external calls, no automatic fixes.
 */

if (!function_exists('custausu_compatibility_normalize_path')) {
  function custausu_compatibility_normalize_path($path): string {
    $path = trim((string) $path);
    if ($path === '') return '';

    $parsed = wp_parse_url($path, PHP_URL_PATH);
    if (is_string($parsed) && $parsed !== '') {
      $path = $parsed;
    }

    $path = trim($path);
    if ($path === '') return '';

    if ($path[0] !== '/') {
      $path = '/' . $path;
    }

    return rtrim($path, '/') . '/';
  }
}

if (!function_exists('custausu_compatibility_normalize_cache_rule')) {
  function custausu_compatibility_normalize_cache_rule($value): string {
    $value = trim((string)$value);
    if ($value === '') return '';

    $parts = wp_parse_url($value);

    if (is_array($parts)) {
      $path  = (string)($parts['path'] ?? '');
      $query = (string)($parts['query'] ?? '');
    } else {
      $pieces = explode('?', $value, 2);
      $path  = (string)($pieces[0] ?? '');
      $query = (string)($pieces[1] ?? '');
    }

    if ($path === '') {
      $path = '/';
    }

    if ($path[0] !== '/') {
      $path = '/' . $path;
    }

    $is_file = (bool) preg_match('/\.(php|js|css|json|xml|txt|map|png|jpe?g|gif|webp|svg|ico|woff2?|ttf|eot)$/i', $path);

    if ($query !== '') {
      $path = $is_file ? rtrim($path, '/') : rtrim($path, '/') . '/';
      if ($path === '//') $path = '/';
      return $path . '?' . $query;
    }

    if ($is_file) {
      return rtrim($path, '/');
    }

    return rtrim($path, '/') . '/';
  }
}

if (!function_exists('custausu_compatibility_safe_home_cache_rule')) {
  function custausu_compatibility_safe_home_cache_rule(string $suffix = ''): string {
    $url = home_url('/' . ltrim($suffix, '/'));
    return custausu_compatibility_normalize_cache_rule($url);
  }
}

if (!function_exists('custausu_compatibility_size_to_bytes')) {
  function custausu_compatibility_size_to_bytes($value): int {
    $value = trim((string)$value);
    if ($value === '') return 0;

    $unit = strtolower(substr($value, -1));
    $num  = (float)$value;

    return match ($unit) {
      'g' => (int)($num * 1024 * 1024 * 1024),
      'm' => (int)($num * 1024 * 1024),
      'k' => (int)($num * 1024),
      default => (int)$num,
    };
  }
}

if (!function_exists('custausu_compatibility_load_plugin_functions')) {
  function custausu_compatibility_load_plugin_functions(): void {
    if (!function_exists('get_plugins')) {
      require_once ABSPATH . 'wp-admin/includes/plugin.php';
    }
  }
}

if (!function_exists('custausu_compatibility_active_plugins')) {
  function custausu_compatibility_active_plugins(): array {
    $active = (array) get_option('active_plugins', []);
    $active = array_values(array_map('strval', $active));

    if (is_multisite()) {
      $active = array_values(array_unique(array_merge(
        $active,
        array_keys((array) get_site_option('active_sitewide_plugins', []))
      )));
    }

    return $active;
  }
}

if (!function_exists('custausu_compatibility_plugin_catalog')) {
  function custausu_compatibility_plugin_catalog(): array {
    return [
      [
        'category' => 'cache',
        'severity' => 'warning',
        'patterns' => [
          'wp-rocket',
          'w3-total-cache',
          'wp-super-cache',
          'wp-fastest-cache',
          'litespeed-cache',
          'autoptimize',
          'sg-cachepress',
          'breeze',
          'hummingbird-performance',
          'wp-optimize',
          'flying-press',
          'swift-performance',
          'cache-enabler',
          'comet-cache',
          'nitropack',
          'perfmatters',
        ],
        'message_en' => 'Caching/optimization plugin detected.',
        'message_it' => 'Plugin cache/ottimizzazione rilevato.',
        'recommendation_en' => 'Exclude CAS auth pages, protected paths, REST endpoints and critical CAS scripts from aggressive caching/optimization.',
        'recommendation_it' => 'Escludi pagine auth CAS, percorsi protetti, endpoint REST e script CAS critici da cache/ottimizzazioni aggressive.',
      ],
      [
        'category' => 'cdn',
        'severity' => 'attention',
        'patterns' => [
          'cloudflare',
          'cdn-enabler',
          'bunnycdn',
          'shift8-cdn',
        ],
        'message_en' => 'CDN helper detected.',
        'message_it' => 'Helper CDN rilevato.',
        'recommendation_en' => 'Ensure CAS auth routes and REST endpoints bypass CDN cache and aggressive optimization.',
        'recommendation_it' => 'Assicurati che route auth CAS ed endpoint REST bypassino cache CDN e ottimizzazioni aggressive.',
      ],
      [
        'category' => 'security',
        'severity' => 'warning',
        'patterns' => [
          'wordfence',
          'sucuri',
          'better-wp-security',
          'solid-security',
          'all-in-one-wp-security',
          'wp-cerber',
          'limit-login-attempts-reloaded',
          'defender-security',
          'malcare',
          'cleantalk-spam-protect',
          'wps-hide-login',
          'hide-my-wp',
        ],
        'message_en' => 'Security/login protection plugin detected.',
        'message_it' => 'Plugin sicurezza/protezione login rilevato.',
        'recommendation_en' => 'Whitelist CAS auth pages, admin-ajax, REST requests and WebAuthn flows where applicable.',
        'recommendation_it' => 'Inserisci in whitelist pagine auth CAS, admin-ajax, richieste REST e flussi WebAuthn dove necessario.',
      ],
      [
        'category' => 'redirects',
        'severity' => 'attention',
        'patterns' => [
          'redirection',
          'safe-redirect-manager',
          'permalink-manager',
          'custom-permalinks',
          'pretty-link',
        ],
        'message_en' => 'Redirect/permalink plugin detected.',
        'message_it' => 'Plugin redirect/permalink rilevato.',
        'recommendation_en' => 'Verify CAS route paths after any permalink or redirect rule change.',
        'recommendation_it' => 'Verifica le route CAS dopo ogni modifica a permalink o regole redirect.',
      ],
      [
        'category' => 'smtp',
        'severity' => 'info',
        'patterns' => [
          'wp-mail-smtp',
          'post-smtp',
          'fluent-smtp',
          'easy-wp-smtp',
          'smtp-mailer',
        ],
        'message_en' => 'SMTP/email delivery plugin detected.',
        'message_it' => 'Plugin SMTP/email delivery rilevato.',
        'recommendation_en' => 'Useful for CAS email verification and password recovery. Keep mail provider logs available when debugging delivery.',
        'recommendation_it' => 'Utile per verifica email e recupero password CAS. Tieni disponibili i log provider quando analizzi la delivery.',
      ],
      [
        'category' => 'multilingual',
        'severity' => 'info',
        'patterns' => [
          'polylang',
          'sitepress-multilingual-cms',
          'translatepress',
          'weglot',
        ],
        'message_en' => 'Multilingual plugin detected.',
        'message_it' => 'Plugin multilingua rilevato.',
        'recommendation_en' => 'Confirm CAS login/register/lost-password routes for every active language.',
        'recommendation_it' => 'Conferma route CAS login/registrazione/recupero password per ogni lingua attiva.',
      ],
      [
        'category' => 'membership',
        'severity' => 'attention',
        'patterns' => [
          'memberpress',
          'paid-memberships-pro',
          'restrict-content',
          'restrict-content-pro',
          'woocommerce-memberships',
          'ultimate-member',
          'profile-builder',
          'userswp',
          'user-role-editor',
        ],
        'message_en' => 'Membership/user-role plugin detected.',
        'message_it' => 'Plugin membership/ruoli utente rilevato.',
        'recommendation_en' => 'Test protected paths and role-based redirects carefully before launch.',
        'recommendation_it' => 'Testa con attenzione percorsi protetti e redirect basati su ruolo prima del lancio.',
      ],
      [
        'category' => 'builder',
        'severity' => 'info',
        'patterns' => [
          'elementor',
          'js_composer',
          'visual-composer',
          'divi-builder',
          'beaver-builder',
          'bricks',
          'oxygen',
        ],
        'message_en' => 'Page builder detected.',
        'message_it' => 'Page builder rilevato.',
        'recommendation_en' => 'Verify that CAS templates are assigned correctly and not overridden by builder layouts.',
        'recommendation_it' => 'Verifica che i template CAS siano assegnati correttamente e non sovrascritti dal builder.',
      ],
    ];
  }
}

if (!function_exists('custausu_compatibility_plugin_matches_patterns')) {
  function custausu_compatibility_plugin_matches_patterns(string $plugin_file, array $plugin_data, array $patterns): bool {
    $haystack = strtolower($plugin_file . ' ' . (string)($plugin_data['Name'] ?? '') . ' ' . (string)($plugin_data['TextDomain'] ?? ''));

    foreach ($patterns as $pattern) {
      $pattern = strtolower(trim((string)$pattern));
      if ($pattern !== '' && strpos($haystack, $pattern) !== false) {
        return true;
      }
    }

    return false;
  }
}

if (!function_exists('custausu_compatibility_scan_plugins')) {
  function custausu_compatibility_scan_plugins(): array {
    custausu_compatibility_load_plugin_functions();

    $all_plugins = function_exists('get_plugins') ? get_plugins() : [];
    $active_plugins = custausu_compatibility_active_plugins();
    $catalog = custausu_compatibility_plugin_catalog();
    $items = [];

    foreach ($all_plugins as $plugin_file => $plugin_data) {
      $is_active = in_array((string)$plugin_file, $active_plugins, true);
      if (!$is_active) continue;

      foreach ($catalog as $rule) {
        if (!custausu_compatibility_plugin_matches_patterns((string)$plugin_file, (array)$plugin_data, (array)($rule['patterns'] ?? []))) {
          continue;
        }

        $items[] = [
          'type' => 'plugin',
          'severity' => sanitize_key((string)($rule['severity'] ?? 'info')),
          'category' => sanitize_key((string)($rule['category'] ?? 'other')),
          'plugin_file' => (string)$plugin_file,
          'plugin_name' => (string)($plugin_data['Name'] ?? $plugin_file),
          'plugin_version' => (string)($plugin_data['Version'] ?? ''),
          'message_en' => (string)($rule['message_en'] ?? ''),
          'message_it' => (string)($rule['message_it'] ?? ''),
          'recommendation_en' => (string)($rule['recommendation_en'] ?? ''),
          'recommendation_it' => (string)($rule['recommendation_it'] ?? ''),
        ];

        break;
      }
    }

    return $items;
  }
}

if (!function_exists('custausu_compatibility_scan_plugin_hygiene')) {
  function custausu_compatibility_scan_plugin_hygiene(): array {
    custausu_compatibility_load_plugin_functions();

    $all_plugins = function_exists('get_plugins') ? get_plugins() : [];
    $items = [];
    $names = [];

    foreach ($all_plugins as $plugin_file => $plugin_data) {
      $name = strtolower(trim((string)($plugin_data['Name'] ?? '')));
      if ($name !== '') {
        $names[$name][] = [
          'file' => (string)$plugin_file,
          'name' => (string)($plugin_data['Name'] ?? ''),
          'version' => (string)($plugin_data['Version'] ?? ''),
        ];
      }

      if (preg_match('/(?:_bu|_backup|-backup|backup|_old|-old|_copy|-copy|_test|-test|_dev|-dev)(?:\/|$)/i', (string)$plugin_file)) {
        $items[] = [
          'type' => 'plugin_hygiene',
          'severity' => 'attention',
          'category' => 'plugin_backup_or_copy',
          'plugin_file' => (string)$plugin_file,
          'plugin_name' => (string)($plugin_data['Name'] ?? $plugin_file),
          'plugin_version' => (string)($plugin_data['Version'] ?? ''),
          'message_en' => 'Possible backup/copy plugin folder detected.',
          'message_it' => 'Possibile cartella plugin backup/copia rilevata.',
          'recommendation_en' => 'Remove unused backup/copy plugin folders from production. Keep backups outside wp-content/plugins.',
          'recommendation_it' => 'Rimuovi da produzione cartelle plugin backup/copia non usate. Conserva i backup fuori da wp-content/plugins.',
        ];
      }
    }

    foreach ($names as $same_name_plugins) {
      if (count($same_name_plugins) < 2) continue;

      $labels = array_map(static function(array $row): string {
        return (string)$row['file'];
      }, $same_name_plugins);

      $items[] = [
        'type' => 'plugin_hygiene',
        'severity' => 'attention',
        'category' => 'duplicate_plugin_name',
        'plugin_file' => implode(', ', $labels),
        'plugin_name' => (string)($same_name_plugins[0]['name'] ?? 'Plugin'),
        'plugin_version' => '',
        'message_en' => 'Multiple plugin folders with the same plugin name were detected.',
        'message_it' => 'Sono state rilevate più cartelle plugin con lo stesso nome plugin.',
        'recommendation_en' => 'Keep only the intended plugin copy active/installed to avoid confusion during support and updates.',
        'recommendation_it' => 'Mantieni solo la copia plugin realmente voluta per evitare confusione durante supporto e aggiornamenti.',
      ];
    }

    return $items;
  }
}

if (!function_exists('custausu_compatibility_scan_forms')) {
  function custausu_compatibility_scan_forms(): array {
    custausu_compatibility_load_plugin_functions();

    $all_plugins = function_exists('get_plugins') ? get_plugins() : [];
    $active_plugins = custausu_compatibility_active_plugins();

    $form_patterns = [
      'contact-form-7' => 'Contact Form 7',
      'wpforms' => 'WPForms',
      'clean-conditional-forms' => 'Clean Conditional Forms',
      'gravityforms' => 'Gravity Forms',
      'ninja-forms' => 'Ninja Forms',
      'formidable' => 'Formidable Forms',
      'fluentform' => 'Fluent Forms',
    ];

    $detected = [];

    foreach ($all_plugins as $plugin_file => $plugin_data) {
      if (!in_array((string)$plugin_file, $active_plugins, true)) continue;

      $haystack = strtolower($plugin_file . ' ' . (string)($plugin_data['Name'] ?? ''));

      foreach ($form_patterns as $needle => $label) {
        if (strpos($haystack, $needle) !== false) {
          $detected[] = (string)($plugin_data['Name'] ?? $label);
          break;
        }
      }
    }

    $detected = array_values(array_unique($detected));

    if (count($detected) < 2) {
      return [];
    }

    $opt = get_option('custausu_global_settings', []);
    $opt = is_array($opt) ? $opt : [];
    $cf7_math_on = false;


    $severity = $cf7_math_on ? 'attention' : 'info';

    return [[
      'type' => 'forms',
      'severity' => $severity,
      'category' => 'multiple_form_plugins',
      'plugin_file' => '',
      'plugin_name' => implode(', ', $detected),
      'plugin_version' => '',
      'message_en' => 'Multiple form plugins detected.',
      'message_it' => 'Rilevati più plugin form.',
      'recommendation_en' => 'Verify which forms should use CAS/CF7 Math Check/reCAPTCHA and avoid duplicate anti-spam UX.',
      'recommendation_it' => 'Verifica quali form devono usare CAS/CF7 Math Check/reCAPTCHA ed evita UX antispam duplicate.',
    ]];
  }
}

if (!function_exists('custausu_compatibility_scan_asset_hosts')) {
  function custausu_compatibility_scan_asset_hosts(): array {
    return [];
  }
}

if (!function_exists('custausu_compatibility_scan_environment')) {
  function custausu_compatibility_scan_environment(): array {
    $items = [];

    $permalink = (string) get_option('permalink_structure', '');
    if ($permalink === '') {
      $items[] = [
        'type' => 'environment',
        'severity' => 'attention',
        'category' => 'permalinks',
        'label' => 'Permalink structure',
        'message_en' => 'Plain permalinks detected.',
        'message_it' => 'Permalink semplici rilevati.',
        'recommendation_en' => 'Use Post name permalinks when possible, then re-run CAS checks. If unsure, follow your hosting/provider WordPress permalink guide.',
        'recommendation_it' => 'Usa permalink Nome articolo quando possibile, poi riesegui i controlli CAS. Se non sai come fare, segui la guida permalink WordPress del tuo provider.',
      ];
    }

    $is_https = is_ssl() || stripos(home_url('/'), 'https://') === 0;
    if (!$is_https) {
      $items[] = [
        'type' => 'environment',
        'severity' => 'warning',
        'category' => 'https',
        'label' => 'HTTPS',
        'message_en' => 'The site does not appear to be using HTTPS.',
        'message_it' => 'Il sito non sembra usare HTTPS.',
        'recommendation_en' => 'Enable HTTPS before using authentication flows in production. Ask your hosting/provider if needed.',
        'recommendation_it' => 'Abilita HTTPS prima di usare flussi di autenticazione in produzione. Se necessario, segui la guida del tuo provider.',
      ];
    }

    $php_memory = function_exists('ini_get') ? (string) @ini_get('memory_limit') : '';
    $php_memory_unlimited = trim($php_memory) === '-1';
    $php_memory_bytes = $php_memory_unlimited ? -1 : custausu_compatibility_size_to_bytes($php_memory);

    $wp_memory = defined('WP_MEMORY_LIMIT') ? (string) WP_MEMORY_LIMIT : '';
    $wp_memory_bytes = custausu_compatibility_size_to_bytes($wp_memory);

    $wp_max_memory = defined('WP_MAX_MEMORY_LIMIT') ? (string) WP_MAX_MEMORY_LIMIT : '';
    $wp_max_memory_bytes = custausu_compatibility_size_to_bytes($wp_max_memory);

    /*
     * Prefer the real PHP runtime limit. WP_MEMORY_LIMIT can be lower than the
     * hosting PHP memory_limit and would otherwise create false positives.
     */
    $memory_check_bytes = 0;
    $memory_check_label = '';

    if ($php_memory_unlimited) {
      $memory_check_bytes = -1;
      $memory_check_label = 'unlimited';
    } elseif ($php_memory_bytes > 0) {
      $memory_check_bytes = $php_memory_bytes;
      $memory_check_label = $php_memory;
    } elseif ($wp_memory_bytes > 0) {
      $memory_check_bytes = $wp_memory_bytes;
      $memory_check_label = $wp_memory;
    }

    if ($memory_check_bytes > 0 && $memory_check_bytes < 128 * 1024 * 1024) {
      $items[] = [
        'type' => 'environment',
        'severity' => 'attention',
        'category' => 'wp_memory',
        'label' => 'PHP memory_limit / WordPress memory',
        'message_en' => 'The effective PHP/WordPress memory limit appears low.',
        'message_it' => 'Il memory limit effettivo PHP/WordPress sembra basso.',
        'recommendation_en' => 'Recommended: 128M minimum, 256M preferred for admin/support operations. Increase PHP memory_limit or WordPress memory constants if your hosting allows it.',
        'recommendation_it' => 'Consigliato: minimo 128M, preferibile 256M per operazioni admin/supporto. Aumenta PHP memory_limit o le costanti memoria WordPress se il tuo hosting lo consente.',
        'meta' => [
          'php_memory_limit' => $php_memory,
          'wp_memory_limit' => $wp_memory,
          'wp_max_memory_limit' => $wp_max_memory,
          'checked_limit' => $memory_check_label,
        ],
      ];
    }


    $env = function_exists('wp_get_environment_type') ? wp_get_environment_type() : 'production';
    $debug_severity = in_array($env, ['local', 'development'], true) ? 'info' : 'warning';

    if (defined('WP_DEBUG') && WP_DEBUG) {
      $items[] = [
        'type' => 'environment',
        'severity' => $debug_severity,
        'category' => 'debug',
        'label' => 'WP_DEBUG',
        'message_en' => 'WP_DEBUG is enabled.',
        'message_it' => 'WP_DEBUG è attivo.',
        'recommendation_en' => in_array($env, ['local', 'development'], true)
          ? 'Useful during development.'
          : 'Disable WP_DEBUG on production unless actively troubleshooting.',
        'recommendation_it' => in_array($env, ['local', 'development'], true)
          ? 'Utile in sviluppo.'
          : 'Disattiva WP_DEBUG in produzione salvo troubleshooting attivo.',
      ];
    }

    $opt = get_option('custausu_global_settings', []);
    $opt = is_array($opt) ? $opt : [];

    if (!empty($opt['debug_enabled'])) {
      $items[] = [
        'type' => 'environment',
        'severity' => $debug_severity,
        'category' => 'custausu_debug',
        'label' => 'CAS Debug',
        'message_en' => 'CAS debug is enabled.',
        'message_it' => 'Il debug CAS è attivo.',
        'recommendation_en' => in_array($env, ['local', 'development'], true)
          ? 'Useful during development.'
          : 'Turn off CAS Debug from CAS Settings when the site is not being actively diagnosed.',
        'recommendation_it' => in_array($env, ['local', 'development'], true)
          ? 'Utile in sviluppo.'
          : 'Spegni CAS Debug dalle impostazioni CAS quando il sito non è in diagnosi attiva.',
      ];
    }

    if (function_exists('wp_using_ext_object_cache') && wp_using_ext_object_cache()) {
      $items[] = [
        'type' => 'environment',
        'severity' => 'info',
        'category' => 'object_cache',
        'label' => 'Object cache',
        'message_en' => 'External object cache appears active.',
        'message_it' => 'Object cache esterna apparentemente attiva.',
        'recommendation_en' => 'Usually fine. Clear object cache after major CAS setting or plugin updates if behavior looks stale.',
        'recommendation_it' => 'Di solito va bene. Svuota object cache dopo modifiche CAS importanti se il comportamento sembra obsoleto.',
      ];
    }

    return $items;
  }
}

if (!function_exists('custausu_compatibility_scan_safety')) {
  function custausu_compatibility_scan_safety(): array {
    if (!function_exists('custausu_safe_mode_is_enabled') || !custausu_safe_mode_is_enabled()) {
      return [];
    }

    $forced = function_exists('custausu_safe_mode_is_forced') && custausu_safe_mode_is_forced();
    $source = function_exists('custausu_safe_mode_source') ? custausu_safe_mode_source() : 'unknown';
    $reason = function_exists('custausu_safe_mode_reason') ? custausu_safe_mode_reason() : '';
    $force_constant = function_exists('custausu_safe_mode_force_constant_name')
      ? custausu_safe_mode_force_constant_name()
      : 'CUSTAUSU_SAFE_MODE_FORCE';

    return [[
      'type' => 'environment',
      'severity' => $forced ? 'warning' : 'attention',
      'category' => 'safe_mode',
      'label' => 'CAS Safe Mode',
      'message_en' => 'CAS Safe Mode is active.',
      'message_it' => 'CAS Safe Mode è attivo.',
      'recommendation_en' => $forced
        ? sprintf('Safe Mode is forced by wp-config.php via %s. Remove it or set it to false when recovery is complete.', $force_constant)
        : 'Disable Safe Mode after recovery tests are complete. While active, CAS custom redirects are bypassed and protected paths fall back to the native WordPress login.',
      'recommendation_it' => $forced
        ? sprintf('Safe Mode è forzato da wp-config.php tramite %s. Rimuovilo oppure impostalo a false quando il recupero è completato.', $force_constant)
        : 'Disattiva Safe Mode dopo aver completato i test di recupero. Quando è attivo, i redirect custom CAS sono bypassati e i percorsi protetti usano il login WordPress nativo.',
      'meta' => [
        'safe_mode_source' => $source,
        'safe_mode_reason' => $reason,
        'forced_by_wp_config' => $forced,
      ],
    ]];
  }
}

if (!function_exists('custausu_compatibility_recovery_readiness')) {
  function custausu_compatibility_recovery_readiness(): array {
    if (function_exists('custausu_compatibility_load_plugin_functions')) {
      custausu_compatibility_load_plugin_functions();
    }

    $all_plugins = function_exists('get_plugins') ? get_plugins() : [];
    $active_plugins = function_exists('custausu_compatibility_active_plugins')
      ? custausu_compatibility_active_plugins()
      : (array) get_option('active_plugins', []);

    $rollback_plugin = [
      'installed' => false,
      'active' => false,
      'file' => '',
      'name' => '',
      'version' => '',
    ];

    foreach ($all_plugins as $plugin_file => $plugin_data) {
      $haystack = strtolower((string)$plugin_file . ' ' . (string)($plugin_data['Name'] ?? ''));

      if (
        strpos($haystack, 'wp-rollback') === false &&
        strpos($haystack, 'rollback') === false
      ) {
        continue;
      }

      $rollback_plugin = [
        'installed' => true,
        'active' => in_array((string)$plugin_file, $active_plugins, true),
        'file' => (string)$plugin_file,
        'name' => (string)($plugin_data['Name'] ?? $plugin_file),
        'version' => (string)($plugin_data['Version'] ?? ''),
      ];

      break;
    }

    $safe_available = function_exists('custausu_safe_mode_is_enabled');
    $safe_enabled = $safe_available && custausu_safe_mode_is_enabled();
    $safe_forced = function_exists('custausu_safe_mode_is_forced') && custausu_safe_mode_is_forced();
    $safe_force_constant = function_exists('custausu_safe_mode_force_constant_name')
      ? custausu_safe_mode_force_constant_name()
      : 'CUSTAUSU_SAFE_MODE_FORCE';

    $wp_cli_context = defined('WP_CLI') && WP_CLI;

    $status = 'ready';

    if (!$safe_available) {
      $status = 'limited';
    } elseif ($safe_forced) {
      $status = 'recovery_active';
    } elseif ($safe_enabled) {
      $status = 'safe_mode_active';
    }

    return [
      'status' => $status,
      'safe_mode' => [
        'available' => $safe_available,
        'enabled' => $safe_enabled,
        'forced_by_wp_config' => $safe_forced,
        'source' => function_exists('custausu_safe_mode_source') ? custausu_safe_mode_source() : 'unknown',
        'reason' => ($safe_enabled && function_exists('custausu_safe_mode_reason')) ? custausu_safe_mode_reason() : '',
        'wp_config_constant' => $safe_force_constant,
      ],
      'wp_cli' => [
        'current_context' => $wp_cli_context,
        'support_report_callable' => function_exists('custausu_support_report_generate'),
        'doctor_scan_callable' => function_exists('custausu_compatibility_scan'),
      ],
      'rollback' => [
        'automatic_rollback_enabled' => false,
        'wp_rollback_plugin' => $rollback_plugin,
      ],
      'instructions' => [
        'safe_mode_force' => sprintf('Add define("%s", true); to wp-config.php to force CAS Safe Mode during recovery. Remove it or set it to false when recovery is complete.', $safe_force_constant),
        'wp_cli_report' => 'wp eval \'$r = custausu_support_report_generate(); echo wp_json_encode($r, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);\'',
        'wp_cli_safe_mode_on' => 'wp eval \'custausu_safe_mode_enable("wp_cli_recovery");\'',
        'wp_cli_safe_mode_off' => 'wp eval \'custausu_safe_mode_disable();\'',
      ],
    ];
  }
}

if (!function_exists('custausu_compatibility_collect_auth_paths')) {
  function custausu_compatibility_collect_auth_paths(): array {
    $opt = get_option('custausu_global_settings', []);
    $opt = is_array($opt) ? $opt : [];

    $paths = [];

    $routes_paths = is_array($opt['routes_paths'] ?? null) ? $opt['routes_paths'] : [];
    foreach ($routes_paths as $lang_routes) {
      if (!is_array($lang_routes)) continue;

      foreach ($lang_routes as $route_path) {
        $p = custausu_compatibility_normalize_path($route_path);
        if ($p !== '') $paths[] = $p;
      }
    }

    $routes_ids = is_array($opt['routes_page_ids'] ?? null) ? $opt['routes_page_ids'] : [];
    foreach ($routes_ids as $lang_routes) {
      if (!is_array($lang_routes)) continue;

      foreach ($lang_routes as $page_id) {
        $page_id = (int) $page_id;
        if ($page_id <= 0) continue;

        $permalink = get_permalink($page_id);
        if (!is_string($permalink) || $permalink === '') continue;

        $p = custausu_compatibility_normalize_path($permalink);
        if ($p !== '') $paths[] = $p;
      }
    }

    return array_values(array_unique($paths));
  }
}

if (!function_exists('custausu_compatibility_collect_protected_paths')) {
  function custausu_compatibility_collect_protected_paths(): array {
    $opt = get_option('custausu_global_settings', []);
    $opt = is_array($opt) ? $opt : [];

    $raw = (string)($opt['protected_paths'] ?? '');
    $tokens = preg_split('/(?:\r\n|\r|\n|[\t ,;]+)/', $raw);
    $paths = [];

    foreach ((array)$tokens as $token) {
      $p = custausu_compatibility_normalize_path($token);
      if ($p !== '') $paths[] = $p;
    }

    return array_values(array_unique($paths));
  }
}

if (!function_exists('custausu_compatibility_feature_runtime_available')) {
  /**
   * The public Free ZIP contains no separately distributed feature runtime.
   * Compatibility scans therefore never execute checks for those modules.
   */
  function custausu_compatibility_feature_runtime_available(string $feature): bool {
    unset($feature);
    return false;
  }
}

if (!function_exists('custausu_compatibility_cache_guidance')) {
  function custausu_compatibility_cache_guidance(): array {
    $auth_paths = custausu_compatibility_collect_auth_paths();
    $protected_paths = custausu_compatibility_collect_protected_paths();
    $opt = get_option('custausu_global_settings', []);
    $opt = is_array($opt) ? $opt : [];

    $dynamic_paths = array_values(array_unique(array_filter([
      custausu_compatibility_normalize_cache_rule(admin_url('admin-ajax.php')),
      custausu_compatibility_safe_home_cache_rule('wp-json/'),
      custausu_compatibility_safe_home_cache_rule('?rest_route=/'),
    ])));

    $asset_paths = [];

    $add_asset = static function(string $asset) use (&$asset_paths): void {
      if (!defined('CUSTAUSU_PLUGIN_URL')) return;

      $asset = ltrim($asset, '/');

      if (defined('CUSTAUSU_PLUGIN_DIR') && !file_exists(CUSTAUSU_PLUGIN_DIR . $asset)) {
        return;
      }

      $p = custausu_compatibility_normalize_cache_rule(CUSTAUSU_PLUGIN_URL . $asset);
      if ($p !== '') {
        $asset_paths[] = $p;
      }
    };

    $add_asset('inc/js/custausu-popup.js');
    $add_asset('inc/css/custom-auth-suite.css');

    return [
      'auth_routes' => [
        'label_en' => 'CAS auth routes',
        'label_it' => 'Route auth CAS',
        'items' => $auth_paths,
        'reason_en' => 'Login, registration and password recovery pages must stay dynamic.',
        'reason_it' => 'Le pagine login, registrazione e recupero password devono restare dinamiche.',
      ],
      'protected_paths' => [
        'label_en' => 'Protected paths',
        'label_it' => 'Percorsi protetti',
        'items' => $protected_paths,
        'reason_en' => 'Protected content should not be cached publicly after authentication checks.',
        'reason_it' => 'I contenuti protetti non devono essere messi in cache pubblica dopo i controlli auth.',
      ],
      'dynamic_endpoints' => [
        'label_en' => 'Dynamic WordPress endpoints',
        'label_it' => 'Endpoint WordPress dinamici',
        'items' => $dynamic_paths,
        'reason_en' => 'AJAX and REST endpoints are used by dynamic auth, diagnostics and licensing flows.',
        'reason_it' => 'AJAX e REST sono usati da flussi auth dinamici, diagnostica e licensing.',
      ],
      'custausu_assets' => [
        'label_en' => 'CAS frontend assets',
        'label_it' => 'Asset frontend CAS',
        'items' => array_values(array_unique($asset_paths)),
        'reason_en' => 'Avoid aggressive JS optimization/defer rules that break active CAS frontend flows.',
        'reason_it' => 'Evita ottimizzazioni/defer JS aggressive che rompono i flussi frontend CAS attivi.',
      ],
    ];
  }
}

if (!function_exists('custausu_compatibility_setup_context')) {
  function custausu_compatibility_setup_context(): array {
    $opt = get_option('custausu_global_settings', []);
    $opt = is_array($opt) ? $opt : [];

    $lang = sanitize_key((string)($opt['routes_default_lang'] ?? ''));
    if ($lang === '') {
      $site_locale = (string) get_option('WPLANG');
      if ($site_locale === '') $site_locale = 'en_US';
      $lang = sanitize_key(strtolower(explode('_', $site_locale)[0] ?? 'en'));
    }
    $langs = [$lang ?: 'en'];

    $required_route_keys = ['login', 'register', 'lostpassword', 'lostpassword_confirm'];
    $routes_paths = is_array($opt['routes_paths'] ?? null) ? $opt['routes_paths'] : [];
    $routes_ids   = is_array($opt['routes_page_ids'] ?? null) ? $opt['routes_page_ids'] : [];

    $required_total = count($langs) * count($required_route_keys);
    $configured = 0;

    foreach ($langs as $lang) {
      foreach ($required_route_keys as $key) {
        $pid = (int)($routes_ids[$lang][$key] ?? 0);
        $pth = trim((string)($routes_paths[$lang][$key] ?? ''));

        if ($pid > 0 || $pth !== '') {
          $configured++;
        }
      }
    }

    $protected_count = count(custausu_compatibility_collect_protected_paths());
    $wizard_state = get_option('custausu_wizard_state', []);
    $wizard_state = is_array($wizard_state) ? $wizard_state : [];

    if ($configured === 0 && $protected_count === 0) {
      $phase = 'first_install';
    } elseif ($configured < $required_total) {
      $phase = 'setup_in_progress';
    } else {
      $phase = 'configured';
    }

    return [
      'phase' => $phase,
      'required_routes_total' => $required_total,
      'required_routes_configured' => $configured,
      'protected_paths_count' => $protected_count,
      'wizard_completed' => !empty($wizard_state['is_completed']),
      'wizard_dismissed' => !empty($wizard_state['is_dismissed']),
      'wizard_current_step' => sanitize_key((string)($wizard_state['current_step'] ?? '')),
    ];
  }
}

if (!function_exists('custausu_compatibility_risk_counts')) {
  function custausu_compatibility_risk_counts(array $items): array {
    $counts = [
      'critical' => 0,
      'warning' => 0,
      'attention' => 0,
      'info' => 0,
    ];

    foreach ($items as $item) {
      $sev = sanitize_key((string)($item['severity'] ?? 'info'));
      if (isset($counts[$sev])) {
        $counts[$sev]++;
      } else {
        $counts['info']++;
      }
    }

    return $counts;
  }
}

if (!function_exists('custausu_compatibility_support_readiness')) {
  function custausu_compatibility_support_readiness(array $scan): array {
    $counts = is_array($scan['counts'] ?? null) ? $scan['counts'] : [];
    $context = is_array($scan['setup_context'] ?? null) ? $scan['setup_context'] : custausu_compatibility_setup_context();

    $critical  = (int)($counts['critical'] ?? 0);
    $warning   = (int)($counts['warning'] ?? 0);
    $attention = (int)($counts['attention'] ?? 0);
    $phase     = sanitize_key((string)($context['phase'] ?? 'unknown'));

    if ($critical > 0) {
      $status = 'blocked';
      $summary_en = 'One or more critical items should be solved before relying on CAS.';
      $summary_it = 'Uno o più elementi critici vanno risolti prima di fare affidamento su CAS.';
    } elseif ($phase === 'first_install') {
      $status = 'setup_not_started';
      $summary_en = 'CAS setup has not started yet. Configure the core routes before evaluating production readiness.';
      $summary_it = 'Il setup CAS non è ancora iniziato. Configura le route principali prima di valutare la prontezza per la produzione.';
    } elseif ($phase === 'setup_in_progress') {
      $status = 'setup_in_progress';
      $summary_en = 'CAS setup is in progress. Complete the missing routes/settings, then re-run the Doctor.';
      $summary_it = 'Il setup CAS è in corso. Completa route/impostazioni mancanti, poi riesegui il Doctor.';
    } elseif ($warning > 0) {
      $status = 'action_required';
      $summary_en = 'CAS can be reviewed, but one or more warnings should be addressed.';
      $summary_it = 'CAS può essere verificato, ma uno o più warning vanno controllati.';
    } elseif ($attention > 0) {
      $status = 'ready_with_attention';
      $summary_en = 'CAS appears usable; review the attention items before production or support escalation.';
      $summary_it = 'CAS sembra utilizzabile; controlla gli elementi attenzione prima della produzione o del supporto.';
    } else {
      $status = 'ready';
      $summary_en = 'CAS environment looks ready from the Installation Doctor perspective.';
      $summary_it = 'L’ambiente CAS risulta pronto dal punto di vista dell’Installation Doctor.';
    }

    return [
      'status' => $status,
      'phase' => $phase,
      'blocking_issues' => $critical,
      'warnings' => $warning,
      'attention_items' => $attention,
      'summary_en' => $summary_en,
      'summary_it' => $summary_it,
    ];
  }
}

if (!function_exists('custausu_compatibility_doctor_snapshot_option_name')) {
  function custausu_compatibility_doctor_snapshot_option_name(): string {
    return 'custausu_doctor_last_snapshot';
  }
}

if (!function_exists('custausu_compatibility_doctor_history_option_name')) {
  function custausu_compatibility_doctor_history_option_name(): string {
    return 'custausu_doctor_change_history';
  }
}

if (!function_exists('custausu_compatibility_doctor_history_limit')) {
  function custausu_compatibility_doctor_history_limit(): int {
    $limit = (int) apply_filters('custausu_doctor_change_history_limit', 120);
    return max(20, min(500, $limit));
  }
}

if (!function_exists('custausu_compatibility_doctor_option_update')) {
  function custausu_compatibility_doctor_option_update(string $key, $value): void {
    if ($key === '') return;

    if (get_option($key, null) === null) {
      add_option($key, $value, '', 'no');
      return;
    }

    update_option($key, $value);
  }
}

if (!function_exists('custausu_compatibility_doctor_bool_status')) {
  function custausu_compatibility_doctor_bool_status(bool $value): string {
    return $value ? 'on' : 'off';
  }
}

if (!function_exists('custausu_compatibility_doctor_current_snapshot')) {
  function custausu_compatibility_doctor_current_snapshot(array $setup_context): array {
    $opt = get_option('custausu_global_settings', []);
    $opt = is_array($opt) ? $opt : [];

    $state = function_exists('custausu_free_package_runtime_context') ? custausu_free_package_runtime_context() : [];
    $state = is_array($state) ? $state : [];

    $license_status = sanitize_key((string)(
      $state['license']['status'] ?? $state['status'] ?? 'none'
    ));

    if ($license_status === '') {
      $license_status = 'none';
    }

    $effective_plan = sanitize_key((string)($state['effective_plan'] ?? 'freemium'));
    if ($effective_plan === '') {
      $effective_plan = 'freemium';
    }

    $package_plan = function_exists('custausu_build_package_plan')
      ? sanitize_key((string) custausu_build_package_plan())
      : '';

    $logout_redirects = is_array($opt['routes_logout_redirect'] ?? null) ? $opt['routes_logout_redirect'] : [];
    ksort($logout_redirects);

    $logout_redirect_parts = [];
    foreach ($logout_redirects as $lng => $path) {
      $lng = sanitize_key((string)$lng);
      if ($lng === '') continue;

      $path = trim((string)$path);
      $logout_redirect_parts[] = $lng . '=' . ($path !== '' ? $path : '[home]');
    }

    $logout_redirect_summary = implode(';', $logout_redirect_parts);

    $active_plugin_versions = [];

    if (function_exists('custausu_compatibility_load_plugin_functions')) {
      custausu_compatibility_load_plugin_functions();
    }

    $all_plugins = function_exists('get_plugins') ? get_plugins() : [];
    $active_plugins = function_exists('custausu_compatibility_active_plugins') ? custausu_compatibility_active_plugins() : (array)get_option('active_plugins', []);
    $catalog = function_exists('custausu_compatibility_plugin_catalog') ? custausu_compatibility_plugin_catalog() : [];

    foreach ($all_plugins as $plugin_file => $plugin_data) {
      if (!in_array((string)$plugin_file, $active_plugins, true)) {
        continue;
      }

      $matched = false;

      foreach ($catalog as $rule) {
        if (function_exists('custausu_compatibility_plugin_matches_patterns') && custausu_compatibility_plugin_matches_patterns((string)$plugin_file, (array)$plugin_data, (array)($rule['patterns'] ?? []))) {
          $matched = true;
          break;
        }
      }

      if (!$matched) {
        continue;
      }

      $active_plugin_versions[(string)$plugin_file] = [
        'name' => (string)($plugin_data['Name'] ?? $plugin_file),
        'version' => (string)($plugin_data['Version'] ?? ''),
      ];
    }

    ksort($active_plugin_versions);

    return [
      'setup_phase' => sanitize_key((string)($setup_context['phase'] ?? 'unknown')),
      'configured_routes' => (int)($setup_context['required_routes_configured'] ?? 0),
      'protected_paths' => (int)($setup_context['protected_paths_count'] ?? 0),
      'wizard_completed' => !empty($setup_context['wizard_completed']) ? 'yes' : 'no',
      'effective_plan' => $effective_plan,
      'license_status' => $license_status,
      'package_plan' => $package_plan,
      'debug_enabled' => custausu_compatibility_doctor_bool_status(!empty($opt['debug_enabled'])),
      'safe_mode_enabled' => function_exists('custausu_safe_mode_is_enabled') && custausu_safe_mode_is_enabled()
        ? (function_exists('custausu_safe_mode_source') ? 'on:' . custausu_safe_mode_source() : 'on')
        : 'off',
      'logout_redirects' => $logout_redirect_summary,
      'active_plugin_versions' => $active_plugin_versions,
    ];
  }
}

if (!function_exists('custausu_compatibility_doctor_change_item')) {
  function custausu_compatibility_doctor_change_item(string $key, string $severity, $from, $to, string $title_en, string $title_it, string $message_en, string $message_it): array {
    return [
      'event_key' => sanitize_key($key),
      'severity' => in_array($severity, ['info', 'attention', 'warning'], true) ? $severity : 'info',
      'from' => is_scalar($from) ? (string)$from : '',
      'to' => is_scalar($to) ? (string)$to : '',
      'title_en' => $title_en,
      'title_it' => $title_it,
      'message_en' => $message_en,
      'message_it' => $message_it,
    ];
  }
}

if (!function_exists('custausu_compatibility_doctor_detect_changes')) {
  function custausu_compatibility_doctor_detect_changes(array $previous, array $current): array {
    if (empty($previous)) {
      return [];
    }

    $changes = [];

    $map = [
      'setup_phase' => [
        'severity' => 'attention',
        'title_en' => 'CAS setup phase changed',
        'title_it' => 'Fase setup CAS cambiata',
        'message_en' => 'The CAS setup state changed since the last Doctor scan.',
        'message_it' => 'Lo stato del setup CAS è cambiato dall’ultimo scan del Doctor.',
      ],
      'configured_routes' => [
        'severity' => 'attention',
        'title_en' => 'Configured routes changed',
        'title_it' => 'Route configurate cambiate',
        'message_en' => 'The number of configured CAS routes changed. Review auth routes and cache exclusions.',
        'message_it' => 'Il numero di route CAS configurate è cambiato. Controlla route auth ed esclusioni cache.',
      ],
      'protected_paths' => [
        'severity' => 'attention',
        'title_en' => 'Protected paths changed',
        'title_it' => 'Percorsi protetti cambiati',
        'message_en' => 'Protected paths changed. Review page-cache exclusions for protected content.',
        'message_it' => 'I percorsi protetti sono cambiati. Controlla le esclusioni cache pagina per i contenuti protetti.',
      ],
      'effective_plan' => [
        'severity' => 'info',
        'title_en' => 'Effective CAS plan changed',
        'title_it' => 'Piano CAS effettivo cambiato',
        'message_en' => 'The effective CAS plan changed. Available features and cache guidance may change.',
        'message_it' => 'Il piano CAS effettivo è cambiato. Funzioni disponibili e guida cache possono cambiare.',
      ],
      'license_status' => [
        'severity' => 'info',
        'title_en' => 'License status changed',
        'title_it' => 'Stato licenza cambiato',
        'message_en' => 'The license status changed since the last Doctor scan.',
        'message_it' => 'Lo stato della licenza è cambiato dall’ultimo scan.',
      ],
      'package_plan' => [
        'severity' => 'info',
        'title_en' => 'Installed package changed',
        'title_it' => 'Pacchetto installato cambiato',
        'message_en' => 'The installed CAS package changed.',
        'message_it' => 'Il pacchetto CAS installato è cambiato.',
      ],

      'debug_enabled' => [
        'severity' => 'info',
        'title_en' => 'CAS Debug changed',
        'title_it' => 'Debug CAS cambiato',
        'message_en' => 'CAS Debug status changed.',
        'message_it' => 'Lo stato del Debug CAS è cambiato.',
      ],
      'safe_mode_enabled' => [
        'severity' => 'attention',
        'title_en' => 'CAS Safe Mode changed',
        'title_it' => 'CAS Safe Mode cambiato',
        'message_en' => 'CAS Safe Mode status changed. Re-check login, protected paths and recovery behavior.',
        'message_it' => 'Lo stato del Safe Mode CAS è cambiato. Ricontrolla login, percorsi protetti e comportamento di recupero.',
      ],
      'logout_redirects' => [
        'severity' => 'attention',
        'title_en' => 'Logout redirect settings changed',
        'title_it' => 'Redirect dopo logout cambiato',
        'message_en' => 'Logout redirect destinations changed. Re-check language-aware logout behavior.',
        'message_it' => 'Le destinazioni dopo logout sono cambiate. Ricontrolla il comportamento logout per lingua.',
      ],
    ];

    foreach ($map as $key => $meta) {
      $from = $previous[$key] ?? '';
      $to = $current[$key] ?? '';

      if ((string)$from === (string)$to) {
        continue;
      }

      $changes[] = custausu_compatibility_doctor_change_item(
        $key,
        (string)$meta['severity'],
        $from,
        $to,
        (string)$meta['title_en'],
        (string)$meta['title_it'],
        (string)$meta['message_en'],
        (string)$meta['message_it']
      );
    }

    $previous_plugins = is_array($previous['active_plugin_versions'] ?? null) ? $previous['active_plugin_versions'] : [];
    $current_plugins = is_array($current['active_plugin_versions'] ?? null) ? $current['active_plugin_versions'] : [];

    foreach ($current_plugins as $plugin_file => $plugin_data) {
      if (!is_array($plugin_data)) continue;

      $name = (string)($plugin_data['name'] ?? $plugin_file);
      $version = (string)($plugin_data['version'] ?? '');

      if (!isset($previous_plugins[$plugin_file])) {
        $changes[] = custausu_compatibility_doctor_change_item(
          'plugin_added',
          'attention',
          '',
          $name . ($version !== '' ? ' ' . $version : ''),
          'Relevant plugin detected',
          'Plugin rilevante rilevato',
          'A plugin that may affect CAS compatibility is now active.',
          'Un plugin che può incidere sulla compatibilità CAS ora risulta attivo.'
        );
        continue;
      }

      $previous_version = is_array($previous_plugins[$plugin_file] ?? null) ? (string)($previous_plugins[$plugin_file]['version'] ?? '') : '';
      if ($previous_version !== $version) {
        $changes[] = custausu_compatibility_doctor_change_item(
          'plugin_version_changed',
          'attention',
          $name . ($previous_version !== '' ? ' ' . $previous_version : ''),
          $name . ($version !== '' ? ' ' . $version : ''),
          'Relevant plugin version changed',
          'Versione plugin rilevante cambiata',
          'A plugin that may affect CAS compatibility changed version. Re-check cache/security exclusions if behavior changed.',
          'Un plugin che può incidere sulla compatibilità CAS ha cambiato versione. Ricontrolla esclusioni cache/security se il comportamento è cambiato.'
        );
      }
    }

    foreach ($previous_plugins as $plugin_file => $plugin_data) {
      if (isset($current_plugins[$plugin_file])) {
        continue;
      }

      if (!is_array($plugin_data)) continue;

      $name = (string)($plugin_data['name'] ?? $plugin_file);
      $version = (string)($plugin_data['version'] ?? '');

      $changes[] = custausu_compatibility_doctor_change_item(
        'plugin_removed',
        'info',
        $name . ($version !== '' ? ' ' . $version : ''),
        '',
        'Relevant plugin no longer detected',
        'Plugin rilevante non più rilevato',
        'A plugin previously detected by the Doctor is no longer active.',
        'Un plugin precedentemente rilevato dal Doctor non risulta più attivo.'
      );
    }

    return $changes;
  }
}

if (!function_exists('custausu_compatibility_doctor_change_history')) {
  function custausu_compatibility_doctor_change_history(): array {
    $history = get_option(custausu_compatibility_doctor_history_option_name(), []);
    return is_array($history) ? array_values($history) : [];
  }
}

if (!function_exists('custausu_compatibility_doctor_append_history')) {
  function custausu_compatibility_doctor_append_history(array $changes): array {
    $changes = array_values(array_filter($changes, 'is_array'));

    if (empty($changes)) {
      return custausu_compatibility_doctor_change_history();
    }

    $history = custausu_compatibility_doctor_change_history();
    $now = gmdate('Y-m-d H:i:s');

    foreach ($changes as $change) {
      $event_key = sanitize_key((string)($change['event_key'] ?? 'change'));
      $from = (string)($change['from'] ?? '');
      $to = (string)($change['to'] ?? '');

      $change['detected_at_utc'] = $now;
      $change['event_id'] = substr(hash('sha256', $now . '|' . $event_key . '|' . $from . '|' . $to . '|' . count($history)), 0, 16);

      $history[] = $change;
    }

    $history = array_slice($history, -custausu_compatibility_doctor_history_limit());

    custausu_compatibility_doctor_option_update(custausu_compatibility_doctor_history_option_name(), $history);

    return $history;
  }
}


if (!function_exists('custausu_compatibility_doctor_auth_route_consistency_snapshot')) {
  function custausu_compatibility_doctor_auth_route_consistency_snapshot(): array {
    if (!function_exists('custausu_auth_page_route_consistency_issues')) {
      return [
        'hash' => '',
        'issues_count' => 0,
        'issues' => [],
      ];
    }

    $issues = custausu_auth_page_route_consistency_issues();
    $issues = is_array($issues) ? array_values(array_filter($issues, 'is_array')) : [];

    $normalized = [];

    foreach ($issues as $issue) {
      $normalized[] = [
        'reason'       => sanitize_key((string)($issue['reason'] ?? '')),
        'lang'         => sanitize_key((string)($issue['lang'] ?? '')),
        'route'        => sanitize_key((string)($issue['route'] ?? '')),
        'page_id'      => absint($issue['page_id'] ?? 0),
        'stored_path'  => (string)($issue['stored_path'] ?? ''),
        'current_path' => (string)($issue['current_path'] ?? ''),
        'template'     => (string)($issue['template'] ?? ''),
        'expected'     => (string)($issue['expected'] ?? ''),
      ];
    }

    usort($normalized, static function (array $a, array $b): int {
      return strcmp(
        implode('|', [
          (string)($a['lang'] ?? ''),
          (string)($a['route'] ?? ''),
          (string)($a['reason'] ?? ''),
          (string)($a['page_id'] ?? ''),
          (string)($a['stored_path'] ?? ''),
          (string)($a['current_path'] ?? ''),
          (string)($a['template'] ?? ''),
          (string)($a['expected'] ?? ''),
        ]),
        implode('|', [
          (string)($b['lang'] ?? ''),
          (string)($b['route'] ?? ''),
          (string)($b['reason'] ?? ''),
          (string)($b['page_id'] ?? ''),
          (string)($b['stored_path'] ?? ''),
          (string)($b['current_path'] ?? ''),
          (string)($b['template'] ?? ''),
          (string)($b['expected'] ?? ''),
        ])
      );
    });

    $json = function_exists('wp_json_encode')
      ? wp_json_encode($normalized, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
      : json_encode($normalized);

    $json = is_string($json) ? $json : '';

    return [
      'hash' => empty($normalized) ? '' : md5($json),
      'issues_count' => count($normalized),
      'issues' => $normalized,
    ];
  }
}

if (!function_exists('custausu_compatibility_doctor_auth_route_label')) {
  function custausu_compatibility_doctor_auth_route_label(string $route): string {
    $route = sanitize_key($route);

    return match ($route) {
      'login' => 'Login',
      'register' => 'Register',
      'lostpassword' => 'Lost password',
      'lostpassword_confirm' => 'Lost password confirm',
      'landing' => 'Landing',
      default => $route !== '' ? ucfirst(str_replace('_', ' ', $route)) : 'Auth route',
    };
  }
}

if (!function_exists('custausu_compatibility_doctor_auth_route_issue_change_item')) {
  function custausu_compatibility_doctor_auth_route_issue_change_item(array $issue): array {
    $reason = sanitize_key((string)($issue['reason'] ?? ''));
    $route  = sanitize_key((string)($issue['route'] ?? ''));
    $lang   = sanitize_key((string)($issue['lang'] ?? ''));

    $route_label = custausu_compatibility_doctor_auth_route_label($route);
    $lang_label = $lang !== '' ? strtoupper($lang) : 'DEFAULT';

    $page_id = absint($issue['page_id'] ?? 0);

    $stored  = (string)($issue['stored_path'] ?? '');
    $current = (string)($issue['current_path'] ?? '');
    $template = (string)($issue['template'] ?? '');
    $expected = (string)($issue['expected'] ?? '');

    $from = $stored;
    $to = $current;

    $title_en = 'Auth route changed: ' . $route_label . ' (' . $lang_label . ')';
    $title_it = 'Route Auth modificata: ' . $route_label . ' (' . $lang_label . ')';

    $message_en = 'CAS detected that a saved Auth route no longer matches the current page configuration.';
    $message_it = 'CAS ha rilevato che una route Auth salvata non coincide più con la configurazione attuale della pagina.';

    if ($reason === 'auth_route_stale_path') {
      $message_en = 'The saved CAS path no longer matches the current page URL/slug. Update Routes & Templates or use Fix Now in the Wizard.';
      $message_it = 'Il path CAS salvato non coincide più con l’URL/slug attuale della pagina. Aggiorna Percorsi e Template oppure usa Ripara subito nel Wizard.';
    } elseif ($reason === 'auth_route_template_mismatch') {
      $from = $template;
      $to = $expected;

      $message_en = 'The selected Auth page no longer uses the expected CAS template. Reassign the correct page template.';
      $message_it = 'La pagina Auth selezionata non usa più il template CAS previsto. Riassegna il template pagina corretto.';
    } elseif ($reason === 'auth_route_page_missing') {
      $from = $page_id > 0 ? ('page #' . $page_id) : 'selected page';
      $to = 'missing';

      $message_en = 'The selected Auth page is missing, trashed or no longer available.';
      $message_it = 'La pagina Auth selezionata manca, è nel cestino o non è più disponibile.';
    }

    return [
      'type'            => 'auth_route_consistency',
      'category'        => 'auth_routes',
      'source'          => 'custausu_auth_page_route_consistency_issues',
      'reason'          => $reason,
      'severity'        => 'warning',
      'title_en'        => $title_en,
      'title_it'        => $title_it,
      'message_en'      => $message_en,
      'message_it'      => $message_it,
      'from'            => $from,
      'to'              => $to,
      'detected_at_utc' => gmdate('Y-m-d H:i:s'),
      'details'         => [
        'route'        => $route,
        'lang'         => $lang,
        'page_id'      => $page_id,
        'stored_path'  => $stored,
        'current_path' => $current,
        'template'     => $template,
        'expected'     => $expected,
      ],
    ];
  }
}

if (!function_exists('custausu_compatibility_doctor_detect_auth_route_consistency_changes')) {
  function custausu_compatibility_doctor_detect_auth_route_consistency_changes(array $previous, array $current): array {
    $prev_state = isset($previous['auth_route_consistency']) && is_array($previous['auth_route_consistency'])
      ? $previous['auth_route_consistency']
      : [];

    $curr_state = isset($current['auth_route_consistency']) && is_array($current['auth_route_consistency'])
      ? $current['auth_route_consistency']
      : [];

    $prev_hash = (string)($prev_state['hash'] ?? '');
    $curr_hash = (string)($curr_state['hash'] ?? '');

    if ($prev_hash === $curr_hash) {
      return [];
    }

    $prev_issues = isset($prev_state['issues']) && is_array($prev_state['issues'])
      ? array_values(array_filter($prev_state['issues'], 'is_array'))
      : [];

    $curr_issues = isset($curr_state['issues']) && is_array($curr_state['issues'])
      ? array_values(array_filter($curr_state['issues'], 'is_array'))
      : [];

    $changes = [];

    if (!empty($curr_issues)) {
      foreach ($curr_issues as $issue) {
        $changes[] = custausu_compatibility_doctor_auth_route_issue_change_item($issue);
      }

      return $changes;
    }

    if (!empty($prev_issues) && empty($curr_issues)) {
      $previous_summary = [];

      foreach (array_slice($prev_issues, 0, 4) as $issue) {
        $route = custausu_compatibility_doctor_auth_route_label((string)($issue['route'] ?? ''));
        $lang = strtoupper((string)($issue['lang'] ?? ''));
        $stored = (string)($issue['stored_path'] ?? '');
        $current_path = (string)($issue['current_path'] ?? '');

        $previous_summary[] = trim($route . ($lang !== '' ? ' (' . $lang . ')' : '') . ': ' . $stored . ' → ' . $current_path);
      }

      $changes[] = [
        'type'            => 'auth_route_consistency_restored',
        'category'        => 'auth_routes',
        'source'          => 'custausu_auth_page_route_consistency_issues',
        'reason'          => 'auth_route_consistency_restored',
        'severity'        => 'info',
        'title_en'        => 'Auth routes restored',
        'title_it'        => 'Route Auth ripristinate',
        'message_en'      => 'CAS Auth routes are aligned again after a previous URL/slug or template mismatch.',
        'message_it'      => 'Le route Auth CAS sono di nuovo allineate dopo un precedente disallineamento di URL/slug o template.',
        'from'            => implode(' | ', array_filter($previous_summary)),
        'to'              => 'OK',
        'detected_at_utc' => gmdate('Y-m-d H:i:s'),
        'details'         => [
          'previous_issues_count' => count($prev_issues),
        ],
      ];
    }

    return $changes;
  }
}


if (!function_exists('custausu_compatibility_doctor_track_changes')) {
  function custausu_compatibility_doctor_track_changes(array $scan): array {
    $setup_context = is_array($scan['setup_context'] ?? null) ? $scan['setup_context'] : [];
    $current = custausu_compatibility_doctor_current_snapshot($setup_context);

    if (function_exists('custausu_compatibility_doctor_auth_route_consistency_snapshot')) {
      $current['auth_route_consistency'] = custausu_compatibility_doctor_auth_route_consistency_snapshot();
    }

    $snapshot_key = custausu_compatibility_doctor_snapshot_option_name();
    $previous = get_option($snapshot_key, []);
    $previous = is_array($previous) ? $previous : [];

    $changes = custausu_compatibility_doctor_detect_changes($previous, $current);

    if (function_exists('custausu_compatibility_doctor_detect_auth_route_consistency_changes')) {
      $changes = array_merge(
        $changes,
        custausu_compatibility_doctor_detect_auth_route_consistency_changes($previous, $current)
      );
    }

    custausu_compatibility_doctor_option_update($snapshot_key, $current);

    $history = custausu_compatibility_doctor_append_history($changes);
    $history_limit = custausu_compatibility_doctor_history_limit();
    $history_count = count($history);

    return [
      'has_previous_snapshot' => !empty($previous),
      'changes_count' => count($changes),
      'has_changes' => !empty($changes),
      'changes' => $changes,
      'snapshot' => $current,
      'history_count' => $history_count,
      'history_limit' => $history_limit,
      'history_limit_reached' => $history_count >= $history_limit,
    ];
  }
}

if (!function_exists('custausu_compatibility_scan')) {
  function custausu_compatibility_scan(): array {
    static $cached_scan = null;

    if (is_array($cached_scan)) {
      return $cached_scan;
    }

    $items = array_merge(
      custausu_compatibility_scan_environment(),
      custausu_compatibility_scan_safety(),
      custausu_compatibility_scan_plugins(),
      custausu_compatibility_scan_plugin_hygiene(),
      custausu_compatibility_scan_forms(),
      custausu_compatibility_scan_asset_hosts()
    );

    $scan = [
      'schema_version' => 3,
      'generated_at_utc' => gmdate('Y-m-d H:i:s'),
      'setup_context' => custausu_compatibility_setup_context(),
      'items' => $items,
      'counts' => custausu_compatibility_risk_counts($items),
      'cache_guidance' => custausu_compatibility_cache_guidance(),
    ];

    $scan['support_readiness'] = custausu_compatibility_support_readiness($scan);
    $scan['recovery_readiness'] = custausu_compatibility_recovery_readiness();
    $scan['change_tracking'] = custausu_compatibility_doctor_track_changes($scan);
    $scan['change_history'] = custausu_compatibility_doctor_change_history();

    $cached_scan = $scan;

    return $cached_scan;
  }
}