<?php
/**
 * Htaccess management for high-performance mode.
 * 
 * @package WPStaticHTMLPages
 */

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

class WPSH_Htaccess
{
	/**
	 * Check whether WooCommerce is installed/active enough for WC helpers to exist.
	 *
	 * @return bool
	 */
	private static function is_woocommerce_active()
	{
		return (class_exists('WooCommerce') || function_exists('WC') || function_exists('wc_get_page_id'));
	}

	/**
	 * Check if the plugin rules are present in .htaccess.
	 */
	public static function check_status()
	{
		$htaccess_path = ABSPATH . '.htaccess';

		global $wp_filesystem;
		if (empty($wp_filesystem)) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
			WP_Filesystem();
		}

		if (!$wp_filesystem->exists($htaccess_path)) {
			return false;
		}
		$content = $wp_filesystem->get_contents($htaccess_path);
		$marker_suffix = (is_multisite() && function_exists('get_current_blog_id')) ? ' - Site ' . get_current_blog_id() : '';
		$marker_start = '# BEGIN Moon Static HTML Pages' . $marker_suffix;
		return (false !== strpos($content, $marker_start));
	}

	/**
	 * Update .htaccess with the necessary rewrite rules.
	 */
	public static function update_rules()
	{
		$htaccess_path = ABSPATH . '.htaccess';

		global $wp_filesystem;
		if (empty($wp_filesystem)) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
			WP_Filesystem();
		}

		if (!$wp_filesystem->exists($htaccess_path) || !$wp_filesystem->is_writable($htaccess_path)) {
			return false;
		}

		$content = $wp_filesystem->get_contents($htaccess_path);
		$marker_suffix = (is_multisite() && function_exists('get_current_blog_id')) ? ' - Site ' . get_current_blog_id() : '';
		$marker_start = '# BEGIN Moon Static HTML Pages' . $marker_suffix;
		$marker_end = '# END Moon Static HTML Pages' . $marker_suffix;

		// Determine both the physical filesystem path and the public URL path
		// for the static directory. Using both is more portable across hosts
		// where DOCUMENT_ROOT and ABSPATH don't line up exactly.
		$static_dir = WPSH_Helper::get_dir();
		$static_url_path = wp_parse_url(WPSH_Helper::get_url(), PHP_URL_PATH);
		$static_url_path = '/' . ltrim((string) $static_url_path, '/');
		$static_dir_check = untrailingslashit(wp_normalize_path($static_dir));
		$pagination_base = 'page';
		global $wp_rewrite;
		if (is_object($wp_rewrite) && !empty($wp_rewrite->pagination_base)) {
			$pagination_base = trim(sanitize_title((string) $wp_rewrite->pagination_base), '/');
		}
		if (empty($pagination_base)) {
			$pagination_base = 'page';
		}

		// Get site domain and path for scoping.
		$home_url_data = wp_parse_url(home_url('/'));
		$domain = isset($home_url_data['host']) ? $home_url_data['host'] : '';
		$base_path = isset($home_url_data['path']) ? '/' . trim($home_url_data['path'], '/') . '/' : '/';
		if ('//' === $base_path)
			$base_path = '/';

		$rules = "\n$marker_start\n";
		$rules .= "<IfModule mod_rewrite.c>\n";
		$rules .= "RewriteEngine On\n";
		$rules .= "RewriteBase /\n"; // Always use root base for consistent path handling

		// REDIRECTION RULES.
		$redirs = get_option('wpsh_redirections', array());
		foreach ($redirs as $idx => $r) {
			if (empty($r['active']) || '0' === $r['active'])
				continue;
			$src = WPSH_Helper::clean_redir_source($r['source']);
			$src = empty($src) ? '^$' : '^' . preg_quote($src, '#') . '/?$';
			$code = intval($r['code']) ?: 301;
			$rules .= "RewriteRule $src {$r['target']} [L,R=$code]\n";
		}

		// Multisite domain check (if needed).
		$domain_cond = is_multisite() ? "RewriteCond %{HTTP_HOST} ^" . preg_quote($domain, '#') . "$ [NC]\n" : "";

		$dynamic_query_args_default = array();
		if (self::is_woocommerce_active()) {
			$dynamic_query_args_default = array(
				'wc-ajax',
				'add-to-cart',
				'added-to-cart',
				'remove_item',
				'undo_item',
				'update_cart',
				'apply_coupon',
				'remove_coupon',
			);
		}
		$dynamic_query_args = (array) apply_filters(
			'wpsh_dynamic_query_args',
			$dynamic_query_args_default
		);
		$dynamic_query_args = array_values(array_filter(array_map('sanitize_key', $dynamic_query_args)));
		$dynamic_query_cond = '';
		foreach ($dynamic_query_args as $dynamic_query_arg) {
			$dynamic_query_cond .= "RewriteCond %{QUERY_STRING} !(^|&)" . preg_quote($dynamic_query_arg, '#') . "(=|&|$) [NC]\n";
		}

		$dynamic_cookie_names = (array) apply_filters(
			'wpsh_dynamic_cookie_names',
			array()
		);
		$dynamic_cookie_names = array_values(array_filter(array_map('sanitize_key', $dynamic_cookie_names)));
		$dynamic_cookie_prefixes = (array) apply_filters(
			'wpsh_dynamic_cookie_prefixes',
			array()
		);
		$dynamic_cookie_prefixes = array_values(array_filter(array_map('sanitize_key', $dynamic_cookie_prefixes)));

		$dynamic_cookie_cond = '';
		foreach ($dynamic_cookie_names as $dynamic_cookie_name) {
			$dynamic_cookie_cond .= "RewriteCond %{HTTP_COOKIE} !" . preg_quote($dynamic_cookie_name, '#') . "= [NC]\n";
		}
		foreach ($dynamic_cookie_prefixes as $dynamic_cookie_prefix) {
			$dynamic_cookie_cond .= "RewriteCond %{HTTP_COOKIE} !" . preg_quote($dynamic_cookie_prefix, '#') . " [NC]\n";
		}

		// Build a RewriteCond that excludes dynamic path prefixes (cart, checkout, my-account, etc.)
		// so Apache never serves static HTML for pages that must remain dynamic.
		$dynamic_path_prefixes_default = array();
		if (self::is_woocommerce_active()) {
			$dynamic_path_prefixes_default = array(
				'cart',
				'checkout',
				'my-account',
			);
		}
		$dynamic_path_prefixes = (array) apply_filters(
			'wpsh_dynamic_path_prefixes',
			$dynamic_path_prefixes_default
		);
		$dynamic_path_prefixes = array_values(array_filter(array_map('sanitize_title', $dynamic_path_prefixes)));
		$dynamic_path_cond = '';
		foreach ($dynamic_path_prefixes as $path_prefix) {
			$dynamic_path_cond .= "RewriteCond %{REQUEST_URI} !^" . rtrim(preg_quote($base_path, '#'), '/') . "/" . preg_quote($path_prefix, '#') . "(/|$) [NC]\n";
		}

		// Build logged-in bypass conditions for Shop and Product pages
		$logged_in_cache_paths = array();
		if (self::is_woocommerce_active()) {
			if (function_exists('wc_get_page_id')) {
				$shop_page_id = wc_get_page_id('shop');
				if ($shop_page_id) {
					$shop_page = get_post($shop_page_id);
					if ($shop_page) {
						$logged_in_cache_paths[] = '^' . preg_quote(rtrim($base_path, '/') . '/' . $shop_page->post_name, '#') . '(/|$)';
					}
				}
				$product_permalink = get_option('woocommerce_permalinks');
				$product_base = !empty($product_permalink['product_base']) ? trim($product_permalink['product_base'], '/') : 'product';
				$product_base_parts = explode('/', $product_base);
				$first_part = $product_base_parts[0];
				if (strpos($first_part, '%') === false) {
					$logged_in_cache_paths[] = '^' . preg_quote(rtrim($base_path, '/') . '/' . $first_part, '#') . '(/|$)';
				} else {
					$logged_in_cache_paths[] = '^' . preg_quote(rtrim($base_path, '/') . '/product', '#') . '(/|$)';
				}
			}
		}

		$logged_in_cache_paths = (array) apply_filters('wpsh_logged_in_cache_paths', $logged_in_cache_paths);
		$logged_in_cache_paths = array_values(array_filter($logged_in_cache_paths));

		$logged_in_cond = "RewriteCond %{HTTP_COOKIE} !wpsh_admin_bypass=1 [NC]\n";
		if (!empty($logged_in_cache_paths)) {
			$logged_in_cond .= "RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_ [NC,OR]\n";
			$count = count($logged_in_cache_paths);
			foreach ($logged_in_cache_paths as $index => $path_regex) {
				$or_flag = ($index === $count - 1) ? '' : ',OR';
				$logged_in_cond .= "RewriteCond %{REQUEST_URI} " . $path_regex . " [NC" . $or_flag . "]\n";
			}
		} else {
			$logged_in_cond .= "RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_ [NC]\n";
		}

		// Query-string pagination check for the home/posts page (e.g. ?paged=2 or ?page=2).
		$rules .= "RewriteCond %{REQUEST_METHOD} GET [NC]\n";
		$rules .= $logged_in_cond;
		$rules .= $dynamic_cookie_cond;
		$rules .= "RewriteCond %{HTTP:X-WPSH-Skip} !^1$ [NC]\n";
		$rules .= $dynamic_query_cond;
		$rules .= $dynamic_path_cond;
		$rules .= $domain_cond;
		$rules .= "RewriteCond %{REQUEST_URI} ^" . rtrim($base_path, '/') . "/?$ [NC]\n";
		$rules .= "RewriteCond %{QUERY_STRING} (?:^|&)(?:paged|page)=([0-9]+)(?:&|$) [NC]\n";
		$rules .= "RewriteCond %{DOCUMENT_ROOT}" . $static_url_path . "/" . $pagination_base . "/%1/index.html -f\n";
		$rules .= "RewriteRule ^.*$ " . $static_url_path . "/" . $pagination_base . "/%1/index.html? [E=WPSH_STATIC_CACHE:1,L]\n";

		// Query-string pagination check for nested pages (e.g. /listing/?paged=2).
		$rules .= "RewriteCond %{REQUEST_METHOD} GET [NC]\n";
		$rules .= $logged_in_cond;
		$rules .= $dynamic_cookie_cond;
		$rules .= "RewriteCond %{HTTP:X-WPSH-Skip} !^1$ [NC]\n";
		$rules .= $dynamic_query_cond;
		$rules .= $dynamic_path_cond;
		$rules .= $domain_cond;
		$rules .= "RewriteCond %{REQUEST_URI} ^" . rtrim($base_path, '/') . "/(.+?)/?$ [NC]\n";
		$rules .= "RewriteCond %{QUERY_STRING} (?:^|&)(?:paged|page)=([0-9]+)(?:&|$) [NC]\n";
		$rules .= "RewriteCond %{DOCUMENT_ROOT}" . $static_url_path . "/%1/" . $pagination_base . "/%2/index.html -f\n";
		$rules .= "RewriteRule ^.*$ " . $static_url_path . "/%1/" . $pagination_base . "/%2/index.html? [E=WPSH_STATIC_CACHE:1,L]\n";

		// Home page check.
		$rules .= "RewriteCond %{REQUEST_METHOD} GET [NC]\n";
		$rules .= $logged_in_cond;
		$rules .= $dynamic_cookie_cond;
		$rules .= "RewriteCond %{HTTP:X-WPSH-Skip} !^1$ [NC]\n";
		$rules .= $dynamic_query_cond;
		$rules .= $dynamic_path_cond;
		$rules .= "RewriteCond %{QUERY_STRING} ^$\n";
		$rules .= "RewriteCond %{THE_REQUEST} ^[A-Z]+\\s+[^?]+\\s+HTTP/\n";
		$rules .= $domain_cond;
		$rules .= "RewriteCond %{REQUEST_URI} ^" . rtrim($base_path, '/') . "/?$\n";
		$rules .= "RewriteCond %{DOCUMENT_ROOT}" . $static_url_path . "/index.html -f\n";
		$rules .= "RewriteRule ^.*$ " . $static_url_path . "/index.html [E=WPSH_STATIC_CACHE:1,L]\n";

		// General page check.
		$rules .= "RewriteCond %{REQUEST_METHOD} GET [NC]\n";
		$rules .= $logged_in_cond;
		$rules .= $dynamic_cookie_cond;
		$rules .= "RewriteCond %{HTTP:X-WPSH-Skip} !^1$ [NC]\n";
		$rules .= $dynamic_query_cond;
		$rules .= $dynamic_path_cond;
		$rules .= "RewriteCond %{QUERY_STRING} ^$\n";
		$rules .= "RewriteCond %{THE_REQUEST} ^[A-Z]+\\s+[^?]+\\s+HTTP/\n";
		$rules .= $domain_cond;
		$rules .= "RewriteCond %{REQUEST_URI} ^" . rtrim($base_path, '/') . "/(.+?)/?$\n";
		$rules .= "RewriteCond %{DOCUMENT_ROOT}" . $static_url_path . "/%1/index.html -f\n";
		$rules .= "RewriteRule ^.*$ " . $static_url_path . "/%1/index.html [E=WPSH_STATIC_CACHE:1,L]\n";

		$rules .= "</IfModule>\n";

		$rules .= "<IfModule mod_headers.c>\n";
		$rules .= "Header set Cache-Control \"max-age=604800, public\" env=WPSH_STATIC_CACHE\n";
		$rules .= "Header set Cache-Control \"max-age=604800, public\" env=REDIRECT_WPSH_STATIC_CACHE\n";
		$rules .= "</IfModule>\n";

		$rules .= "$marker_end\n";

		if (false !== strpos($content, $marker_start)) {
			$content = preg_replace('/' . preg_quote($marker_start, '/') . '.*?' . preg_quote($marker_end, '/') . '/s', '', $content);
		}

		$content = $rules . "\n" . trim($content);

		return $wp_filesystem->put_contents($htaccess_path, $content) !== false;
	}
}
