<?php

if (!defined('ABSPATH')) { exit; }

if (!class_exists('Aegisify_Bundled_Plugins_Page')) {

class Aegisify_Bundled_Plugins_Page {
	private static $booted = false;
	private static $license_cache = null;
	private static $license_cache_at = 0;

	private static $config = array(
		'parent_slug' => 'aegisify-dashboard',

		'menu_slug'   => 'aegisify-bundled-plugins',

		'page_title'  => 'Aegisify Plugins',
		'menu_title'  => 'Aegisify Plugins',

		'view_cap'    => 'manage_options',

		'products' => array(
			'aegisify' => array(
				'plugin_file' => 'aegisify/aegisify.php',
				'clm_slug'    => 'aegisify',
				'title'       => 'Aegisify Core',
				'desc'        => 'The Aegisify Core plugin that powers licensing, shared services, and the central dashboard for the Aegisify suite.',
				'image'       => '../../assets/aegisify_logo1_small.png',
			),
			'aegisify-shield' => array(
				'plugin_file' => 'aegisify-shield/aegisshield-security.php',
				'clm_slug'    => 'aegisshield',
				'zip_url'     => 'https://aegisify.com/wp-content/uploads/2026/04/aegisify-shield-7.2.3.zip',
				'title'       => 'Aegisify Shield Security',
				'desc'        => 'Full WordPress security suite with Pro Upgrade: hardening, monitoring, malware checks, integrity tools, and protection layers built for real admins.',
				'image'       => '../../assets/aegisifyshield.png', 
			),
			'aegisify-waf' => array(
				'plugin_file' => 'aegisify-waf/aegiswaf.php',
				'clm_slug'    => 'aegiswaf',
				'zip_url'     => 'https://aegisify.com/wp-content/uploads/2026/04/aegisify-waf-1.13.zip',
				'title'       => 'Aegisify WAF & Firewall',
				'desc'        => 'Web Application Firewall for WordPress, DDOS Layer7, Bot Control, API Shield and Logs Attack Story. Advanced features upgrades',
				'image'       => '../../assets/aegisifywaf.png',
			),
			'aegisify-seo' => array(
				'plugin_file' => 'aegisify-seo/aegisseo.php',
				'clm_slug'    => 'aegisseo',
				'zip_url'     => 'https://aegisify.com/wp-content/uploads/2026/04/aegisify-seo-2.3.zip',
				'title'       => 'Aegisify SEO',
				'desc'        => 'SEO OPs Center, Global SEO Automation, Social Meta, Schema, Advanced SiteMAPS, Issues & Fixes, Evidence, Linking Assistance and FREE advanced features.',
				'image'       => '../../assets/aegisifyseo.png',
			),
			'aegisify-backup' => array(
				'plugin_file' => 'aegisify-backup/aegisbackup.php',
				'clm_slug'    => 'aegisbackup',
				'zip_url'     => 'https://aegisify.com/wp-content/uploads/2026/03/aegisify-backup-1.5.3.zip',
				'title'       => 'Aegisify Backup, DR & Migrate',
				'desc'        => 'Aegisify Backup is a professional WordPress backup, migration, and disaster-recovery plugin engineered for stability and safe restores without risky background jobs',
				'image'       => '../../assets/aegisifybackup.png',
			),
			'aegisify-spam' => array(
				'plugin_file' => 'aegisify-spam/aegisspam.php',
				'clm_slug'    => 'aegisspam',
				'zip_url'     => 'https://aegisify.com/wp-content/uploads/2026/03/aegisify-spam-1.1.19.zip',
				'title'       => 'Aegisify Spam Protection',
				'desc'        => 'Local-first anti-spam protection for WordPress comments, registrations, and Contact Forms with transparent scoring, logs, allow/deny lists, firewall, and local Machine learning.',
				'image'       => '../../assets/aegisifyspam.png', // set your image URL if you want
			),
			'aegisify-link' => array(
				'plugin_file' => 'aegisify-link/aegislink.php',
				'clm_slug'    => 'aegislink',
				'zip_url'     => 'https://aegisify.com/wp-content/uploads/2026/03/aegisify-link-1.2.zip',
				'title'       => 'Aegisify Link',
				'desc'        => 'Expert Smart internal linking + linking assistant by Aegisify. Create keyword URL smart links and generate context-aware internal link suggestions.',
				'image'       => '../../assets/aegisifylinks.png', // set your image URL if you want
			),
			'aegisify-sitemap' => array(
				'plugin_file' => 'aegisify-sitemap/aegissitemap.php',
				'clm_slug'    => 'aegissitemap',
				'zip_url'     => 'https://aegisify.com/wp-content/uploads/2026/03/aegisify-sitemap-1.3.3.zip',
				'title'       => 'Aegisify SiteMap',
				'desc'        => 'Enterprise XML sitemap engine by Aegisify: fast, cache-friendly sitemap index, HTML sitemap, images, and optional video/news sitemaps.',
				'image'       => '../../assets/aegisifysitemap.png', // set your image URL if you want
			),
		),
	);
	
	public static function init(array $override = array()) : void {
		if (!is_admin()) { return; }

		if (!did_action('init')) {
			add_action('init', function() use ($override) {
				self::init($override);
			}, 1);
			return;
		}

		if (self::$booted) { return; }
		self::$booted = true;

		$override_products = array();
		if (isset($override['products']) && is_array($override['products'])) {
			$override_products = $override['products'];
			unset($override['products']);
		}

		self::$config = array_merge(self::$config, $override);

		if (!empty($override_products)) {
			self::$config['products'] = array_merge((array) self::$config['products'], $override_products);
		}

		$products = (array) self::$config['products'];

		if (isset($products['aegisify-link']) && is_array($products['aegisify-link'])) {
			if (defined('AEGISLINK_BASENAME') && AEGISLINK_BASENAME) {
				$products['aegisify-link']['plugin_file'] = AEGISLINK_BASENAME;
			}
		}
		
		if (isset($products['aegisify-seo']) && is_array($products['aegisify-seo'])) {
			if (defined('AEGISSEO_BASENAME') && AEGISSEO_BASENAME) {
				$products['aegisify-seo']['plugin_file'] = AEGISSEO_BASENAME;
			}
		}

		self::$config['products'] = $products;

		add_action('admin_post_aegisify_products_install', array(__CLASS__, 'handle_install'));
		add_action('admin_post_aegisify_products_update', array(__CLASS__, 'handle_update'));
		add_action('admin_post_aegisify_products_check_updates', array(__CLASS__, 'handle_check_updates'));
	}

	public static function handle_check_updates() : void {
		if (!current_user_can('manage_options')) {
			wp_die('You do not have permission to perform this action.');
		}

		check_admin_referer('aegisify_products_check_updates');

		// Updates are only available to licensed Aegisify Core installations.
		// Always validate Core licensing before attempting any CLM update communications.
		if (!self::core_is_licensed(true)) {
			self::redirect_back(
				'Aegisify Core is not licensed. Updates are only available for licensed products.',
				'error'
			);
		}

		$products = self::get_products();
		$checked  = 0;
		$updated  = 0;

		foreach ($products as $key => $p) {
			$slug = !empty($p['clm_slug']) ? sanitize_key((string) $p['clm_slug']) : sanitize_key((string) $key);
			if ($slug === '') {
				continue;
			}

			if ($slug === 'aegisshield' || $slug === 'aegisshield-security') {
				$slug = 'aegisshield';
			}

			$checked++;
			$opt = 'clm_update_history_' . $slug;

						$installed_version = '';
			if (!empty($p['plugin_file'])) {
				$installed_version = self::get_installed_version((string) $p['plugin_file']);
			}

			if ($installed_version === '') {
				continue;
			}

			$remote = self::fetch_remote_update_metadata($slug, $installed_version);

			// Never allow an update check to "downgrade" the stored/latest version.
			$history = get_option($opt, array());
			if (!is_array($history)) {
				$history = array();
			}

			$best_version = '';
			foreach ($history as $row) {
				if (!is_array($row) || empty($row['version'])) { continue; }
				$v = trim((string) $row['version']);
				if ($v === '') { continue; }
				$v_norm = ltrim($v, "vV \t\n\r\0\x0B");
				if ($best_version === '' || version_compare($v_norm, ltrim($best_version, "vV \t\n\r\0\x0B"), '>')) {
					$best_version = $v;
				}
			}

			if (!empty($remote) && !empty($remote['version'])) {
				$remote_ver = trim((string) $remote['version']);
				$remote_norm = ltrim($remote_ver, "vV \t\n\r\0\x0B");
				$best_norm   = ltrim((string) $best_version, "vV \t\n\r\0\x0B");

				// Only write/update history when the remote version is actually newer than what we already know.
				if ($remote_ver !== '' && ($best_version === '' || version_compare($remote_norm, $best_norm, '>'))) {
					$entry = array(
						'version'       => (string) $remote_ver,
						'download_url'  => (string) ($remote['download_url'] ?? ''),
						'changelog_url' => (string) ($remote['changelog_url'] ?? ''),
						'checked_at'    => time(),
					);

					// Prepend newest entry, keep the rest (do not destroy CLM history).
					array_unshift($history, $entry);

					// De-duplicate by version, keep first (newest).
					$seen = array();
					$dedup = array();
					foreach ($history as $hrow) {
						if (!is_array($hrow) || empty($hrow['version'])) { continue; }
						$hv = trim((string) $hrow['version']);
						if ($hv === '') { continue; }
						$hk = ltrim($hv, "vV \t\n\r\0\x0B");
						if (isset($seen[$hk])) { continue; }
						$seen[$hk] = true;
						$dedup[] = $hrow;
					}
					$history = $dedup;

					update_option($opt, $history, false);
					$updated++;
					continue;
				}
			}

			if (function_exists('wp_cache_delete')) {
				wp_cache_delete($opt, 'options');
				wp_cache_delete('alloptions', 'options');
				wp_cache_delete('notoptions', 'options');
			}

			$history = get_option($opt, array());
			if (!is_array($history)) {
				$history = array();
			}

			if (empty($history)) {
				$rebuilt = self::rebuild_update_history_from_db($slug);
				if (!empty($rebuilt)) {
					$history = $rebuilt;
				}
			}

			update_option($opt, $history, false);
			$updated++;
		}

		update_option('aegisify_last_updates_check', time(), false);
		self::redirect_back(
			sprintf('Checked updates for %d products.', (int) $checked),
			'updated'
		);
	}

	private static function rebuild_update_history_from_db(string $product_slug) : array {
		global $wpdb;

		$product_slug = sanitize_key($product_slug);
		if ($product_slug === '') {
			return array();
		}

		$cache_group = 'aegisify_clm';
		$cache_ttl   = (defined('HOUR_IN_SECONDS') ? HOUR_IN_SECONDS : 3600);
		$ident_rx    = '/^[A-Za-z0-9_]+$/';

		$candidates = array(
			$wpdb->prefix . 'clm_update_history',
			$wpdb->prefix . 'clm_updates',
			$wpdb->prefix . 'clm_product_updates',
		);

		foreach ($candidates as $table) {
			// Validate identifiers early (table/columns cannot be placeholders in $wpdb->prepare()).
			if (!is_string($table) || !preg_match($ident_rx, $table)) {
				continue;
			}
			$table_sql = esc_sql($table);

			$exists_key = 'clm_tbl_exists_' . md5($table);
			$exists = wp_cache_get($exists_key, $cache_group);
			if ($exists === false) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Required to detect optional CLM tables.
				$exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
				wp_cache_set($exists_key, (string) $exists, $cache_group, $cache_ttl);
			}
			if ($exists !== $table) {
				continue;
			}

			$cols_key = 'clm_tbl_cols_' . md5($table);
			$colnames = wp_cache_get($cols_key, $cache_group);
			if (!is_array($colnames)) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- DESCRIBE is required to map optional CLM schemas safely.
				// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Identifier (table name) is validated/escaped; placeholders cannot be used for identifiers.
				$cols = $wpdb->get_results( 'DESCRIBE `' . $table_sql . '`', ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
				if (!is_array($cols) || empty($cols)) {
					continue;
				}

				$colnames = array();
				foreach ($cols as $c) {
					if (!empty($c['Field'])) {
						$colnames[strtolower((string) $c['Field'])] = (string) $c['Field'];
					}
				}
				wp_cache_set($cols_key, $colnames, $cache_group, $cache_ttl);
			}

			$slug_col = $colnames['product_slug'] ?? ($colnames['slug'] ?? ($colnames['product'] ?? ''));
			$ver_col  = $colnames['version'] ?? '';
			$url_col  = $colnames['download_url'] ?? ($colnames['url'] ?? ($colnames['download'] ?? ''));

			if ($slug_col === '' || $ver_col === '' || $url_col === '') {
				continue;
			}

			if (!preg_match($ident_rx, $slug_col) || !preg_match($ident_rx, $ver_col) || !preg_match($ident_rx, $url_col)) {
				continue;
			}


			$rows_key = 'clm_updates_' . md5($table . '|' . $product_slug . '|' . $slug_col . '|' . $ver_col . '|' . $url_col);
			$rows = wp_cache_get($rows_key, $cache_group);
			if (!is_array($rows)) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Safe query; identifiers validated/escaped, value prepared.
				$rows = $wpdb->get_results(
					// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Dynamic identifiers are validated/escaped; placeholders cannot be used for identifiers.
					$wpdb->prepare(
						'SELECT `' . esc_sql($ver_col) . '` AS version, `' . esc_sql($url_col) . '` AS download_url FROM `' . $table_sql . '` WHERE `' . esc_sql($slug_col) . '` = %s', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
						$product_slug
					),
					ARRAY_A
				); // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Dynamic identifiers are validated/escaped; placeholders are used for values.
wp_cache_set($rows_key, (is_array($rows) ? $rows : array()), $cache_group, $cache_ttl);
			}



			if (!is_array($rows) || empty($rows)) {
				continue;
			}

			$out = array();
			foreach ($rows as $r) {
				if (!is_array($r)) { continue; }
				$ver = isset($r['version']) ? trim((string) $r['version']) : '';
				$url = isset($r['download_url']) ? (string) $r['download_url'] : '';
				if ($ver === '') { continue; }
				$out[] = array(
					'version'      => $ver,
					'download_url' => $url,
				);
			}

			if (!empty($out)) {
				return $out;
			}
		}

		return array();
	}

	private static function get_installed_version(string $plugin_file) : string {
		$plugin_file = self::resolve_plugin_file($plugin_file);
		if (!$plugin_file || !file_exists(WP_PLUGIN_DIR . '/' . $plugin_file)) {
			return '';
		}
		if (!function_exists('get_plugins')) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}
		$plugins = get_plugins();
		if (isset($plugins[$plugin_file]['Version'])) {
			return (string) $plugins[$plugin_file]['Version'];
		}
		return '';
	}

	private static function fetch_remote_update_metadata(string $product_slug, string $installed_version = '') : array {
		$product_slug = sanitize_key($product_slug);
		if ($product_slug === '') {
			return array();
		}

		// Hard gate: never communicate with CLM for updates unless Aegisify Core is licensed.
		if (!self::core_is_licensed(false)) {
			return array();
		}

		$host = apply_filters('aegisify_clm_host', 'https://aegisify.com');
		$host = untrailingslashit((string) $host);

		$endpoint = $host . '/wp-json/chue-license/v1/' . rawurlencode($product_slug) . '/update-check';

		$license_key = (string) get_option('aegisify_license_key', '');
		$email = (string) get_option('aegisify_license_email', get_option('admin_email', ''));

		$payload = array(
			'product'     => $product_slug,
			'slug'        => $product_slug,
			'version'     => (string) $installed_version,
			'site_url'    => home_url(),
			'license_key' => $license_key,
			'email'       => $email,
		);

		$response = wp_remote_post($endpoint, array(
			'timeout'     => 20,
			'redirection' => 3,
			'blocking'    => true,
			'headers'     => array(
				'Content-Type' => 'application/json',
				'Accept'       => 'application/json',
			),
			'body'        => wp_json_encode($payload),
		));

		if (is_wp_error($response)) {
			return array();
		}

		$code = (int) wp_remote_retrieve_response_code($response);
		$raw  = (string) wp_remote_retrieve_body($response);
		if ($code < 200 || $code >= 300 || $raw === '') {
			return array();
		}

		$data = json_decode($raw, true);
		if (!is_array($data)) {
			return array();
		}

		$d = $data;
		if (!empty($data['data']) && is_array($data['data'])) {
			$d = $data['data'];
		}

		if (isset($d['update_available']) && !$d['update_available']) {
			return array();
		}

		if (empty($d['success']) || empty($d['update_available']) || empty($d['version']) || empty($d['download_url'])) {
			return array();
		}

		$out = array(
			'version'       => (string) $d['version'],
			'download_url'  => esc_url_raw((string) $d['download_url']),
			'changelog_url' => !empty($d['changelog_url']) ? esc_url_raw((string) $d['changelog_url']) : '',
		);

		if (!empty($d['homepage'])) {
			$out['homepage'] = esc_url_raw((string) $d['homepage']);
		}

		return $out;
	}

	private static function core_is_licensed(bool $force_refresh = false) : bool {
		// Small request-local cache to avoid repeated CLM pings during a single admin request.
		if (is_bool(self::$license_cache) && self::$license_cache_at > 0 && (time() - (int) self::$license_cache_at) < 10) {
			return (bool) self::$license_cache;
		}

		$licensed = false;
		try {
			if (function_exists('aegisify_is_pro')) {
				$licensed = (bool) aegisify_is_pro($force_refresh);
			} elseif (class_exists('Aegisify_Core') && method_exists('Aegisify_Core', 'instance')) {
				$core = \Aegisify_Core::instance();
				if ($core && is_object($core) && method_exists($core, 'license')) {
					$lic = $core->license();
					if ($lic && is_object($lic) && method_exists($lic, 'is_pro_active')) {
						$licensed = (bool) $lic->is_pro_active(true, (defined('AEGISIFY_VERSION') ? (string) AEGISIFY_VERSION : ''));
					}
				}
			}
		} catch (\Throwable $e) {
			$licensed = false;
		}

		self::$license_cache = (bool) $licensed;
		self::$license_cache_at = time();
		return (bool) $licensed;
	}

	private static function get_clm_latest_info(string $product_slug) : array {
		global $wpdb;

		$product_slug = sanitize_key($product_slug);
		if ($product_slug === '') {
			return array('version' => '', 'download_url' => '');
		}

		$cache_group = 'aegisify_clm';
		$cache_ttl   = (defined('HOUR_IN_SECONDS') ? HOUR_IN_SECONDS : 3600);
		$ident_rx    = '/^[A-Za-z0-9_]+$/';

		if ($product_slug === 'aegisshield' || $product_slug === 'aegisshield-security') {
			$product_slug = 'aegisshield';
		}

		$history = get_option('clm_update_history_' . $product_slug, array());

		if (is_array($history) && !empty($history)) {
			$best_version = '';
			$best_url     = '';

			foreach ($history as $row) {
				if (!is_array($row)) { continue; }

				$ver = isset($row['version']) ? (string) $row['version'] : '';
				$dl  = isset($row['download_url']) ? (string) $row['download_url'] : '';

				$ver = trim($ver);
				if ($ver === '') { continue; }

				$ver_norm = ltrim($ver, "vV \t\n\r\0\x0B");

				if ($best_version === '' || version_compare($ver_norm, ltrim($best_version, "vV \t\n\r\0\x0B"), '>')) {
					$best_version = $ver;
					$best_url     = $dl;
				}
			}

			if ($best_version !== '') {
				return array(
					'version'      => $best_version,
					'download_url' => (string) $best_url,
				);
			}
		}

		$table = $wpdb->prefix . 'clm_products';
		if (!is_string($table) || !preg_match($ident_rx, $table)) {
			return array('version' => '', 'download_url' => '');
		}
		$table_sql = esc_sql($table);

		$exists_key = 'clm_tbl_exists_' . md5($table);
		$exists = wp_cache_get($exists_key, $cache_group);
		if ($exists === false) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Required to detect optional CLM table.
			$exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
			wp_cache_set($exists_key, (string) $exists, $cache_group, $cache_ttl);
		}
		if ($exists !== $table) {
			return array('version' => '', 'download_url' => '');
		}

		$row_key = 'clm_products_row_' . md5($table . '|' . $product_slug);
		$row = wp_cache_get($row_key, $cache_group);
		if (!is_array($row)) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Safe query; table validated/escaped, value prepared.
			$row = $wpdb->get_row(
				// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Dynamic table identifier validated/escaped; placeholders cannot be used for identifiers.
				$wpdb->prepare(
					'SELECT version, download_url FROM `' . $table_sql . '` WHERE slug = %s LIMIT 1', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
					$product_slug
				),
				ARRAY_A
			); // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Dynamic table identifier validated/escaped; value is prepared.
wp_cache_set($row_key, (is_array($row) ? $row : array()), $cache_group, $cache_ttl);
		}

		return is_array($row)
			? array(
				'version'      => isset($row['version']) ? (string) $row['version'] : '',
				'download_url' => isset($row['download_url']) ? (string) $row['download_url'] : '',
			)
			: array('version' => '', 'download_url' => '');
	}
	private static function maybe_handle_inline_install() : void {
		if (empty($_GET['aegisify_install'])) {
			return;
		}

		if (!current_user_can('install_plugins')) {
			wp_die('You do not have permission to install plugins.');
		}

		$key = sanitize_key((string) $_GET['aegisify_install']);
		check_admin_referer('aegisify_inline_install_' . $key);
		$__aegisify_ob_level = ob_get_level();
		ob_start();
		$__aegisify_ob_cleanup = static function() use ($__aegisify_ob_level) : void {
			while (ob_get_level() > $__aegisify_ob_level) {
				@ob_end_clean();
			}
		};

		$products = self::get_products();
		if (empty($products[$key])) {
			wp_die('Invalid product.');
		}

		$p = $products[$key];
		$plugin_file = !empty($p['plugin_file']) ? (string) $p['plugin_file'] : '';
		if ($plugin_file && self::is_installed($plugin_file)) {
			self::redirect_back('Plugin is already installed.', 'updated');
		}

		$download = !empty($p['zip_url']) ? esc_url_raw((string) $p['zip_url']) : '';
		if (!$download || !preg_match('#^https?://#i', $download)) {
			self::redirect_back('Install failed: Missing or invalid zip_url for this product.', 'error');
		}

		$check = self::validate_zip_url($download);
		if (empty($check['ok'])) {
			self::redirect_back('Install failed: ' . $check['error'], 'error');
		}

		require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/plugin.php';

		$req_uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
		$url = remove_query_arg( array( 'aegisify_notice', 'aegisify_type', 'aegisify_notice_nonce' ), $req_uri );

		$creds = request_filesystem_credentials($url);
		if ($creds === false) {
			exit;
		}
		if (!WP_Filesystem($creds)) {
			request_filesystem_credentials($url, '', true);
			exit;
		}

		$skin = new Automatic_Upgrader_Skin();
		$upgrader = new Plugin_Upgrader($skin);

		$result = $upgrader->install($download);

		if (is_wp_error($result)) {
			$__aegisify_ob_cleanup();
			self::redirect_back('Install failed: ' . $result->get_error_message(), 'error');
		}

		if (!$result) {
			$__aegisify_ob_cleanup();
			self::redirect_back('Install failed: WordPress could not install the package (permissions / invalid zip).', 'error');
		}

		$__aegisify_ob_cleanup();
		self::redirect_back('Installed successfully.', 'updated');
	}

	private static function maybe_handle_inline_toggle() : void {
		if (empty($_GET['aegisify_toggle']) || empty($_GET['plugin'])) {
			return;
		}

		if (!current_user_can('activate_plugins')) {
			wp_die('You do not have permission to activate plugins.');
		}

		$action = sanitize_key((string) $_GET['aegisify_toggle']);
		if (!in_array($action, array('activate', 'deactivate'), true)) {
			wp_die('Invalid action.');
		}

		$plugin_file = isset($_GET['plugin']) ? sanitize_text_field( (string) wp_unslash($_GET['plugin']) ) : '';
		$plugin_file = ltrim($plugin_file, '/');

		// Nonce matches the one we generate in our action URLs.
		check_admin_referer('aegisify_toggle_' . $action . '_' . $plugin_file);

		require_once ABSPATH . 'wp-admin/includes/plugin.php';

		if ($action === 'activate') {
			$result = activate_plugin($plugin_file);

			if (is_wp_error($result)) {
				self::redirect_back('Activate failed: ' . $result->get_error_message(), 'error');
			}

			self::redirect_back('Plugin activated successfully.', 'updated');
		}

		deactivate_plugins($plugin_file, false, false);
		self::redirect_back('Plugin deactivated successfully.', 'updated');
	}
	
	public static function add_submenu() : void {
		$parent = (string) self::$config['parent_slug'];
		$slug   = (string) self::$config['menu_slug'];

		add_submenu_page(
			$parent,
			(string) self::$config['page_title'],
			(string) self::$config['menu_title'],
			(string) self::$config['view_cap'],
			$slug,
			array(__CLASS__, 'render_page')
		);
	}

	private static function get_products() : array {
		$products = (array) self::$config['products'];

		if (has_filter('aegisify_bundled_plugins_products')) {
			$products = (array) apply_filters('aegisify_bundled_plugins_products', $products, self::$config);
		}

		return $products;
	}

	private static function resolve_asset_url(string $img) : string {
		$img = trim($img);
		if ($img === '') { return ''; }
		if (preg_match('#^(https?:)?//#i', $img) || stripos($img, 'data:') === 0) {
			return $img;
		}
		if (strpos($img, '..') !== false) {
			$img = basename($img);
		}
		$img = ltrim($img, '/');
		if (stripos($img, 'assets/') === 0) {
			$img = substr($img, strlen('assets/'));
		}

		$base = plugin_dir_url(__FILE__) . '../../assets/';
		return $base . $img;
	}
	
	private static function resolve_plugin_file(string $plugin_file): string {
		$plugin_file = ltrim($plugin_file, '/');
		if (!$plugin_file) { return ''; }

		if (!did_action('init')) {
			return $plugin_file;
		}

		if (file_exists(WP_PLUGIN_DIR . '/' . $plugin_file)) {
			return $plugin_file;
		}

		
		// Slug migration support: allow either old plugin folder slugs (legacy) or new WordPress.org slugs (aegisify-*)
		$folder_map = array(
			'aegisbackup/'           => 'aegisify-backup/',
			'aegiswaf/'              => 'aegisify-waf/',
			'aegisshield-security/'  => 'aegisify-shield/',
			'aegisseo/'              => 'aegisify-seo/',
			'aegisspam/'             => 'aegisify-spam/',
			'aegissitemap/'          => 'aegisify-sitemap/',
			'aegislink/'             => 'aegisify-link/',
			'aegisticket/'           => 'aegisify-ticket/',
		);

		foreach ($folder_map as $old => $new) {
			if (stripos($plugin_file, $old) === 0) {
				$alt = $new . substr($plugin_file, strlen($old));
				if (file_exists(WP_PLUGIN_DIR . '/' . $alt)) {
					return $alt;
				}
			} elseif (stripos($plugin_file, $new) === 0) {
				$alt = $old . substr($plugin_file, strlen($new));
				if (file_exists(WP_PLUGIN_DIR . '/' . $alt)) {
					return $alt;
				}
			}
		}
if (!function_exists('get_plugins')) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		$target = strtolower($plugin_file);
		foreach (array_keys(get_plugins()) as $pf) {
			if (strtolower($pf) === $target) {
				return $pf; 
			}
		}

		return $plugin_file;
	}

	private static function is_installed(string $plugin_file) : bool {
		$plugin_file = self::resolve_plugin_file($plugin_file);
		if (!$plugin_file) { return false; }
		return file_exists(WP_PLUGIN_DIR . '/' . $plugin_file);
	}
	
	private static function is_active_plugin(string $plugin_file) : bool {
		$plugin_file = self::resolve_plugin_file($plugin_file);
		if (!$plugin_file) { return false; }

		if (!function_exists('is_plugin_active')) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		return is_plugin_active($plugin_file);
	}

	private static function validate_zip_url(string $url) : array {

		$cache_key = 'aegisify_zipcheck_' . md5($url);
		$cached = get_transient($cache_key);
		if (is_array($cached) && isset($cached['ok'])) {
			return $cached;
		}

		$head = wp_remote_head($url, array(
			'timeout'     => 10,
			'redirection' => 5,
		));

		if (is_wp_error($head)) {
			$out = array('ok' => false, 'error' => 'Zip URL request failed: ' . $head->get_error_message());
			set_transient($cache_key, $out, DAY_IN_SECONDS);
			return $out;
		}

		$code = (int) wp_remote_retrieve_response_code($head);
		if ($code < 200 || $code >= 400) {
			$out = array('ok' => false, 'error' => 'Zip URL returned HTTP ' . $code . ' (blocked or redirected).');
			set_transient($cache_key, $out, DAY_IN_SECONDS);
			return $out;
		}

		$content_type = (string) wp_remote_retrieve_header($head, 'content-type');
		if ($content_type && stripos($content_type, 'text/html') !== false) {
			$out = array('ok' => false, 'error' => 'Zip URL returned HTML instead of a ZIP file.');
			set_transient($cache_key, $out, DAY_IN_SECONDS);
			return $out;
		}

		$get = wp_remote_get($url, array(
			'timeout'     => 10,
			'redirection' => 5,
			'headers'     => array(
				'Range' => 'bytes=0-1',
			),
			'limit_response_size' => 2,
		));

		if (is_wp_error($get)) {
			$out = array('ok' => false, 'error' => 'Zip URL download failed: ' . $get->get_error_message());
			set_transient($cache_key, $out, DAY_IN_SECONDS);
			return $out;
		}

		$get_code = (int) wp_remote_retrieve_response_code($get);
		if ($get_code !== 200 && $get_code !== 206) {
			$out = array('ok' => false, 'error' => 'Zip URL byte-check returned HTTP ' . $get_code . '.');
			set_transient($cache_key, $out, DAY_IN_SECONDS);
			return $out;
		}

		$body = (string) wp_remote_retrieve_body($get);
		if (strlen($body) < 2 || substr($body, 0, 2) !== 'PK') {
			$out = array('ok' => false, 'error' => 'Downloaded file is not a valid ZIP archive.');
			set_transient($cache_key, $out, DAY_IN_SECONDS);
			return $out;
		}

		$out = array('ok' => true);
		set_transient($cache_key, $out, DAY_IN_SECONDS);
		return $out;
	}

	private static function redirect_back(string $notice = '', string $type = 'updated') : void {
		$parent = (string) self::$config['parent_slug'];
		$slug   = (string) self::$config['menu_slug'];

		$url = admin_url('admin.php?page=' . rawurlencode($slug));

		if ($notice) {
			$url = add_query_arg(array(
				'aegisify_notice' => rawurlencode($notice),
				'aegisify_type'   => rawurlencode($type),
				'aegisify_notice_nonce' => wp_create_nonce('aegisify_notice'),
			), $url);
		}

		if (!headers_sent()) {
			wp_safe_redirect($url);
			exit;
		}

		$safe_url = esc_url($url);
		echo '<!doctype html><html><head><meta charset="utf-8">';
		echo '<meta http-equiv="refresh" content="' . esc_attr( '0;url=' . $safe_url ) . '">';
		echo '<script>window.location.href=' . wp_json_encode($url) . ';</script>';
		echo '</head><body>';
		echo '<p>Redirecting… <a href="' . esc_url( $safe_url ) . '">Click here if you are not redirected.</a></p>';
		echo '</body></html>';
		exit;
	}
	
	private static function get_wp_activate_url(string $plugin_file) : string {
		$plugin_file = ltrim($plugin_file, '/');

		$url = admin_url('admin.php?page=' . rawurlencode((string) self::$config['menu_slug']) .
			'&aegisify_toggle=activate&plugin=' . rawurlencode($plugin_file)
		);

		return wp_nonce_url($url, 'aegisify_toggle_activate_' . $plugin_file);
	}

	private static function get_wp_deactivate_url(string $plugin_file) : string {
		$plugin_file = ltrim($plugin_file, '/');

		$url = admin_url('admin.php?page=' . rawurlencode((string) self::$config['menu_slug']) .
			'&aegisify_toggle=deactivate&plugin=' . rawurlencode($plugin_file)
		);

		return wp_nonce_url($url, 'aegisify_toggle_deactivate_' . $plugin_file);
	}

	private static function get_wp_update_url(string $plugin_file) : string {
		$plugin_file = ltrim($plugin_file, '/');
		$url = admin_url('update.php?action=upgrade-plugin&plugin=' . rawurlencode($plugin_file));
		return wp_nonce_url($url, 'upgrade-plugin_' . $plugin_file);
	}

	private static function has_update(string $plugin_file) : bool {
		$plugin_file = ltrim($plugin_file, '/');
		$updates = get_site_transient('update_plugins');
		return (is_object($updates) && isset($updates->response) && isset($updates->response[$plugin_file]));
	}


	public static function handle_install() : void {
		if (!current_user_can('install_plugins')) {
			self::redirect_back('You do not have permission to install plugins.', 'error');
		}
		check_admin_referer('aegisify_products_install');
		$__aegisify_ob_level = ob_get_level();
		ob_start();
		$__aegisify_ob_cleanup = static function() use ($__aegisify_ob_level) : void {
			while (ob_get_level() > $__aegisify_ob_level) {
				@ob_end_clean();
			}
		};

		$key = isset($_POST['product_key']) ? sanitize_key((string) $_POST['product_key']) : '';
		$products = self::get_products();

		if (!$key || !isset($products[$key])) {
			self::redirect_back('Invalid product.', 'error');
		}
		
		$p = $products[$key];

		$download = isset($p['zip_url']) ? esc_url_raw((string) $p['zip_url']) : '';
		if (!$download || !preg_match('#^https?://#i', $download)) {
			self::redirect_back('Install failed: Missing or invalid zip_url for this product.', 'error');
		}

		$check = self::validate_zip_url($download);
		if (empty($check['ok'])) {
			$__aegisify_ob_cleanup();
			self::redirect_back('Install failed: ' . $check['error'], 'error');
		}

		require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/misc.php';
		require_once ABSPATH . 'wp-admin/includes/plugin.php';

		$skin = new Automatic_Upgrader_Skin();
		$upgrader = new Plugin_Upgrader($skin);
		$result = $upgrader->install($download);

		if (is_wp_error($result)) {
			$__aegisify_ob_cleanup();
			self::redirect_back('Install failed: ' . $result->get_error_message(), 'error');
		}

		if (!$result) {
			$err = (isset($skin->result) && is_wp_error($skin->result))
				? $skin->result->get_error_message()
				: 'Unknown installer failure.';
			$__aegisify_ob_cleanup();
			self::redirect_back('Install failed: ' . $err, 'error');
		}

		$__aegisify_ob_cleanup();
		self::redirect_back('Installed successfully.', 'updated');
	}

	public static function handle_update() : void {
		if (!current_user_can('update_plugins')) {
			self::redirect_back('You do not have permission to update plugins.', 'error');
		}
		check_admin_referer('aegisify_products_update');

		$key = isset($_POST['product_key']) ? sanitize_key((string) $_POST['product_key']) : '';
		$products = self::get_products();
		if (!$key || !isset($products[$key])) {
			self::redirect_back('Invalid product.', 'error');
		}
		$p = $products[$key];
		$plugin_file = isset($p['plugin_file']) ? (string) $p['plugin_file'] : '';
		if (!$plugin_file || !self::is_installed($plugin_file)) {
			self::redirect_back('Update failed: Plugin is not installed.', 'error');
		}

		$clm_slug = !empty($p['clm_slug']) ? sanitize_key((string) $p['clm_slug']) : $key;
		$clm = self::get_clm_latest_info($clm_slug);
		$download = !empty($clm['download_url']) ? esc_url_raw((string) $clm['download_url']) : '';
		if (!$download || !preg_match('#^https?://#i', $download)) {
			self::redirect_back('Update failed: Missing or invalid CLM Download URL for this product.', 'error');
		}

		$check = self::validate_zip_url($download);
		if (empty($check['ok'])) {
			self::redirect_back('Update failed: ' . $check['error'], 'error');
		}

		require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/misc.php';
		require_once ABSPATH . 'wp-admin/includes/plugin.php';

		$skin = new Automatic_Upgrader_Skin();
		$upgrader = new Plugin_Upgrader($skin);
		$plugin_file = self::resolve_plugin_file($plugin_file);

		$result = $upgrader->run(
			array(
				'package'           => $download,
				'destination'       => WP_PLUGIN_DIR,
				'clear_destination' => true,
				'clear_working'     => true,
				'hook_extra'        => array(
					'plugin' => $plugin_file,
					'theme'  => '',
					'type'   => 'plugin',
					'action' => 'update',
				),
			)
		);

		if (is_wp_error($result)) {
			self::redirect_back('Update failed: ' . $result->get_error_message(), 'error');
		}
		if (!$result) {
			$err = (isset($skin->result) && is_wp_error($skin->result))
				? $skin->result->get_error_message()
				: 'Unknown updater failure.';
			self::redirect_back('Update failed: ' . $err, 'error');
		}

		self::redirect_back('Updated successfully.', 'updated');
	}
	
	public static function render_page() : void {
		self::maybe_handle_inline_install();
		self::maybe_handle_inline_toggle(); 
		if (!current_user_can((string) self::$config['view_cap'])) {
			wp_die(esc_html__('Sorry, you are not allowed to access this page.', 'aegisify'));
		}

		$notice = '';
		$type   = '';
		if ( isset($_GET['aegisify_notice'], $_GET['aegisify_notice_nonce']) ) {
			$notice_nonce = sanitize_text_field( (string) wp_unslash( $_GET['aegisify_notice_nonce'] ) );
			if ( wp_verify_nonce( $notice_nonce, 'aegisify_notice' ) ) {
				$notice = sanitize_text_field( (string) wp_unslash( $_GET['aegisify_notice'] ) );
				$type   = isset($_GET['aegisify_type']) ? sanitize_key( (string) wp_unslash( $_GET['aegisify_type'] ) ) : '';
			}
		}

		$products  = self::get_products();
		$licensed  = self::core_is_licensed(false);
		?>
		<div class="wrap">
			<h1 style="margin-bottom:8px;"><?php echo esc_html(self::$config['page_title']); ?></h1>

			<?php if (!$licensed): ?>
				<div class="notice notice-error" style="max-width:980px;">
					<p><strong><?php echo esc_html__('Not Licensed:', 'aegisify'); ?></strong> <?php echo esc_html__('Aegisify Core is not licensed. Updates are only available for licensed products. Please register/activate your license to enable update checks and downloads.', 'aegisify'); ?></p>
				</div>
			<?php endif; ?>
			<div class="aegisify-banner" style="margin: 12px 0 20px 0;">
				<img
					src="<?php echo esc_url( plugin_dir_url( __FILE__ ) . '../../assets/all.png' ); ?>"
					alt="<?php echo esc_attr__( 'Aegisify Plugin Suite', 'aegisify' ); ?>"
					style="
						width:70%;
						max-width:800px;
						align: center;
						height:auto;
						display:block;
						border-radius:8px;
						box-shadow:0 2px 6px rgba(0,0,0,0.08);
					"
				/>
			</div>			
			<p style="max-width: 980px;">
			    <h1 style="margin-bottom:8px;"><?php echo esc_html('Everything Your WordPress Site Needs One Platform. One Price.'); ?></h1>
				<?php echo esc_html__('Welcome to Aegisify, and thank you for choosing our WordPress products. Every Aegisify plugin is carefully engineered, thoroughly tested, and built with a deep commitment to delivering real value to the WordPress community. We believe powerful tools should be accessible to everyone, which is why each product includes meaningful, high-impact features that comes free out of the box!  No forced upgrades, no hidden restrictions, and no pressure to go Pro. You’ll continue to receive updates, improvements, and critical bug fixes whether you upgrade or not. Pro features are available for those who want advanced capabilities, but the choice is always yours. We’re grateful for your trust and proud to support your WordPress site with tools designed to protect, optimize, and grow your business today and into the future.', 'aegisify'); ?>
				<h3 style="margin-bottom:8px;"><?php echo esc_html('Built for site owners, freelancers, and agencies who want stability'); ?></h3></p>
				<div style="margin-bottom: 8px; font-size: 14px;">
					<strong><?php echo esc_html__('Aegisify:', 'aegisify'); ?></strong>
					<a href="https://aegisify.com" target="_blank" rel="noopener noreferrer">https://aegisify.com</a>
				</div>

				<?php
					$quicklinks = array(
						array(
							'label' => __('Register Account', 'aegisify'),
							'url'   => 'https://aegisify.com/register/',
							'icon'  => 'dashicons-id',
						),
						array(
							'label' => __('Buy Now', 'aegisify'),
							'url'   => 'https://aegisify.com/profile/',
							'icon'  => 'dashicons-cart',
						),
						array(
							'label' => __('Open a Ticket', 'aegisify'),
							'url'   => 'https://aegisify.com/profile/',
							'icon'  => 'dashicons-sos',
						),
						array(
							'label' => __('Report a Bug', 'aegisify'),
							'url'   => 'https://aegisify.com/report-a-bug/',
							'icon'  => 'dashicons-dismiss',
						),
						array(
							'label' => __('Donate', 'aegisify'),
							'url'   => 'https://www.paypal.com/ncp/payment/8ZU8J9GTCALCU',
							'icon'  => 'dashicons-heart',
						),
					);
				?>

				<div style="display:flex; flex-wrap:wrap; gap:10px;">
					<?php foreach ($quicklinks as $q): ?>
						<a
							href="<?php echo esc_url($q['url']); ?>"
							target="_blank"
							rel="noopener noreferrer"
							style="
								display:inline-flex;
								align-items:center;
								gap:6px;
								padding:8px 10px;
								border:1px solid #dcdcde;
								border-radius:8px;
								background:#fff;
								text-decoration:none;
								font-size:13px;
							"
						>
							<span class="dashicons <?php echo esc_attr($q['icon']); ?>" style="font-size:18px; width:18px; height:18px;"></span>
							<span><?php echo esc_html($q['label']); ?></span>
						</a>
					<?php endforeach; ?>

					<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline-flex; align-items:center;">
						<?php wp_nonce_field('aegisify_products_check_updates'); ?>
						<input type="hidden" name="action" value="aegisify_products_check_updates" />
						<button
							type="submit"
							class="button"
							<?php echo $licensed ? '' : 'disabled="disabled" aria-disabled="true"'; ?>
							style="
								display:inline-flex;
								align-items:center;
								gap:6px;
								padding:8px 10px;
								border-radius:8px;
								font-size:13px;
							"
						>
							<span class="dashicons dashicons-update" style="font-size:18px; width:18px; height:18px;"></span>
							<span><?php echo esc_html__('Check Updates', 'aegisify'); ?></span>
						</button>
					</form>
				</div>

			</p>

			<?php if ($notice): ?>
				<div class="<?php echo esc_attr(($type === 'error') ? 'notice notice-error' : 'notice notice-success'); ?> is-dismissible">
					<p><?php echo esc_html($notice); ?></p>
				</div>
			<?php endif; ?>

			<div class="notice notice-info" style="max-width:980px;">
				<p>
					<?php echo esc_html__('Tip: WordPress may request filesystem credentials (FTP) depending on server permissions. That is normal for plugin installs on some hosts.', 'aegisify'); ?>
				</p>
			</div>

			<style>
				.aegisify-grid{
					display:grid;
					grid-template-columns: repeat(4, minmax(0, 1fr));
					gap: 16px;
					max-width: 1200px;
				}
				@media (max-width: 1200px){
					.aegisify-grid{ grid-template-columns: repeat(3, minmax(0,1fr)); }
				}
				@media (max-width: 900px){
					.aegisify-grid{ grid-template-columns: repeat(2, minmax(0,1fr)); }
				}
				@media (max-width: 520px){
					.aegisify-grid{ grid-template-columns: 1fr; }
				}
				.aegisify-card{
					background:#fff;
					border:1px solid #e5e5e5;
					border-radius:8px;
					overflow:hidden;
					box-shadow: 0 1px 2px rgba(0,0,0,0.04);
					display:flex;
					flex-direction:column;
					min-height: 280px;
				}
				.aegisify-card__top{
					position:relative;
					padding: 18px 18px 10px;
					border-bottom: 1px solid #f0f0f0;
					display:flex;
					align-items:center;
					justify-content:center;
					min-height: 120px;
				}
				.aegisify-card__badge{
					position:absolute;
					top:10px;
					left:10px;
					background:#2271b1;
					color:#fff;
					font-size: 11px;
					padding: 4px 8px;
					border-radius: 4px;
					letter-spacing: .3px;
					text-transform: uppercase;
				}
				.aegisify-card__img{
					max-width: 78%;
					max-height: 78px;
					width:auto;
					height:auto;
					object-fit:contain;
				}
				.aegisify-card__body{
					padding: 14px 16px 0;
					flex:1;
				}
				.aegisify-card__title{
					margin: 0 0 8px;
					font-size: 14px;
					font-weight: 700;
				}
				.aegisify-card__desc{
					margin: 0 0 14px;
					color:#4b5563;
					font-size: 12.5px;
					line-height: 1.45;
					min-height: 54px;
				}
				.aegisify-card__footer{
					padding: 12px 16px 16px;
					display:flex;
					align-items:center;
					justify-content:space-between;
					gap: 10px;
				}
				.aegisify-status{
					font-size: 12px;
					color:#111827;
				}
				.aegisify-status strong{ font-weight:700; }
				.aegisify-actions form{ display:inline; }
				.aegisify-versions{ margin: 0 0 12px; font-size: 12px; color:#111827; }
				.aegisify-versions .muted{ color:#6b7280; }
			</style>

			<div class="aegisify-grid">
				<?php foreach ($products as $k => $p): ?>
					<?php
						$k = (string) $k;
						$title = isset($p['title']) ? (string) $p['title'] : $k;
						$desc  = isset($p['desc']) ? (string) $p['desc'] : '';
						$img_raw = isset($p['image']) ? (string) $p['image'] : '';
						$img     = self::resolve_asset_url($img_raw);
						$plugin_file = isset($p['plugin_file']) ? (string) $p['plugin_file'] : '';

						$installed = ($plugin_file ? self::is_installed($plugin_file) : false);
						$active = ($installed && $plugin_file) ? self::is_active_plugin($plugin_file) : false;
						$update_available = ($installed && $plugin_file) ? self::has_update($plugin_file) : false;
						$current_version = $installed ? self::get_installed_version($plugin_file) : '';
						$clm_slug = !empty($p['clm_slug']) ? sanitize_key((string) $p['clm_slug']) : $k;
						$clm_latest = self::get_clm_latest_info($clm_slug);
						$clm_latest_version = isset($clm_latest['version']) ? (string) $clm_latest['version'] : '';
						$clm_download_url = isset($clm_latest['download_url']) ? (string) $clm_latest['download_url'] : '';

						// Versions are independent of slugs. Slugs only tell us *where* to check.
						// If the installed plugin is already newer than what CLM reports, show installed as "latest"
						// to avoid confusing "one version behind" displays.
						$cv_norm = ltrim((string) $current_version, "vV \t\n\r\0\x0B");
						$lv_norm = ltrim((string) $clm_latest_version, "vV \t\n\r\0\x0B");

						if ($installed && $current_version && $clm_latest_version && version_compare($cv_norm, $lv_norm, '>')) {
							$clm_latest_version = (string) $current_version;
							$clm_download_url = '';
							$lv_norm = $cv_norm;
						}

						$clm_update_needed = ($installed && $current_version && $clm_latest_version)
							? version_compare($lv_norm, $cv_norm, '>')
							: false;
					?>
					<div class="aegisify-card">
						<div class="aegisify-card__top">
							<div class="aegisify-card__badge"><?php echo esc_html($installed ? 'Installed' : 'Available'); ?></div>
							<?php if ($img): ?>
								<img class="aegisify-card__img" src="<?php echo esc_url($img); ?>" alt="<?php echo esc_attr($title); ?>">
							<?php else: ?>
								<div style="font-weight:800;font-size:18px;opacity:.25;"><?php echo esc_html($title); ?></div>
							<?php endif; ?>
						</div>

						<div class="aegisify-card__body">
							<div class="aegisify-card__title"><?php echo esc_html($title); ?></div>
							<div class="aegisify-card__desc"><?php echo esc_html($desc); ?></div>
							<div class="aegisify-versions">
								<div>
									<strong><?php echo esc_html__('Current Installed Version:', 'aegisify'); ?></strong>
									<span class="muted"><?php echo esc_html($installed ? ($current_version ?: '—') : __('Not Installed', 'aegisify')); ?></span>
								</div>
								<div>
									<strong><?php echo esc_html__('Latest Version:', 'aegisify'); ?></strong>
									<span class="muted"><?php echo esc_html($clm_latest_version ?: '—'); ?></span>
								</div>
							</div>
						</div>

						<div class="aegisify-card__footer">
							<div class="aegisify-status">
								<strong><?php echo esc_html($installed ? 'Status:' : 'Status:'); ?></strong>
								<?php echo esc_html(!$installed ? ' Not Installed' : ($active ? ' Active' : ' Installed')); ?>
							</div>

							<div class="aegisify-actions">
								<?php if ($installed && $clm_update_needed && $clm_download_url && current_user_can('update_plugins')): ?>
									<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
										<?php wp_nonce_field('aegisify_products_update'); ?>
										<input type="hidden" name="action" value="aegisify_products_update" />
										<input type="hidden" name="product_key" value="<?php echo esc_attr($k); ?>" />
										<button type="submit" class="button button-primary"><?php echo esc_html__('Update', 'aegisify'); ?></button>
									</form>
								<?php endif; ?>

								<?php if ($installed && $update_available && current_user_can('update_plugins')): ?>
									<?php $update_url = self::get_wp_update_url($plugin_file); ?>
									<a href="<?php echo esc_url($update_url); ?>" class="button button-primary">
										<?php echo esc_html__('Update Plugin', 'aegisify'); ?>
									</a>
								<?php endif; ?>

								<?php if (!$installed): ?>
									<?php
										$install_url = add_query_arg(
											array('aegisify_install' => $k),
											menu_page_url((string) self::$config['menu_slug'], false)
										);
										$install_url = wp_nonce_url($install_url, 'aegisify_inline_install_' . $k);
									?>
									<a href="<?php echo esc_url($install_url); ?>" class="button button-primary">
										<?php echo esc_html__('Install Plugin', 'aegisify'); ?>
									</a>

								<?php elseif (!$active): ?>
									<?php $activate_url = self::get_wp_activate_url($plugin_file); ?>
									<a href="<?php echo esc_url($activate_url); ?>" class="button button-secondary">
										<?php echo esc_html__('Activate Plugin', 'aegisify'); ?>
									</a>

								<?php else: ?>
									<?php $deactivate_url = self::get_wp_deactivate_url($plugin_file); ?>
									<a href="<?php echo esc_url($deactivate_url); ?>" class="button button-secondary">
										<?php echo esc_html__('Deactivate Plugin', 'aegisify'); ?>
									</a>
								<?php endif; ?>
							</div>
						</div>
					</div>
				<?php endforeach; ?>
			</div>

		</div>
		<?php
	}
}

} 