<?php
defined( 'ABSPATH' ) || die();

use RSSSL\Security\RSSSL_Htaccess_File_Manager;
use RSSSL\Pro\Security\WordPress\Rsssl_Geo_Block;

/**
 * Class to handle the creation and include of the firewall
 */
class rsssl_firewall_manager {
	/**
	 * Marker string for .htaccess rules related to auto prepend file.
	 */
	private const HTACCESS_MARKER_PREPEND = 'Really Simple Auto Prepend File';
	/**
	 * Firewall object
	 */
	private static rsssl_firewall_manager $this;

	/**
	 * The htaccess file manager
	 */
	public RSSSL_Htaccess_File_Manager $htAccessFile;
	/**
	 * File
	 *
	 * @var string
	 */
	private $file;
	/**
	 * If we can use a dynamic path
	 *
	 * @var bool
	 */
	private $dynamic_path;
	/**
	 * Path to the firewall.php file, filterable.
	 *
	 * @var string
	 */
	private string $firewall_file_path;
	/**
	 * Rules to add to the firewall.
	 *
	 * @var string
	 */
	private $rules;
	/**
	 * The WP_Filesystem instance, used for file operations.
	 *
	 */
	private $wp_filesystem;

	public function __construct(RSSSL_Htaccess_File_Manager $htaccessFile) {

		if ( isset( self::$this ) ) {
			wp_die();
		}
		self::$this = $this;

		// Store the injected htaccess file manager
		$this->htAccessFile = $htaccessFile;

		// Set dynamic path detection dynamically to handle environment changes
		$this->dynamic_path = $this->get_dynamic_path();

		// Determine firewall.php path, allowing custom content dir or fallback.
		if ( $this->dynamic_path ) {
			$wpContentPath = ABSPATH . 'wp-content/';
		} else {
			$wpContentPath = WP_CONTENT_DIR . '/';
		}

		$this->firewall_file_path = apply_filters(
			'rsssl_firewall_file_path',
			$wpContentPath . 'firewall.php'
		);

		// Set the file path dynamically so we can detect WP_CONTENT_DIR changes
		$this->file = $this->get_advanced_headers_path();

		// trigger this action to force rules update
		add_action( 'rsssl_update_rules', array( $this, 'install' ), 10 );
		add_action( 'rsssl_after_saved_fields', array( $this, 'install' ), 100 );
		add_action( 'rsssl_deactivate', array( $this, 'uninstall' ), 20 );

		// Proactively check for environment changes on admin loads
		add_action( 'admin_init', array( $this, 'maybe_regenerate_firewall' ), 5 );

		add_filter( 'rsssl_notices', array( $this, 'notices' ) );

		//handle activation and deactivation of wp rocket
		add_action( 'rocket_activation', array( $this, 'remove_prepend_file_in_htaccess' ) );
		add_action( 'rocket_deactivation', array( $this, 'include_prepend_file_in_htaccess' ) );

		if ( ! defined( 'RSSSL_IS_WP_ENGINE' ) ) {
			define( 'RSSSL_IS_WP_ENGINE', isset( $_SERVER['IS_WPE'] ) );
		}
		if ( ! defined( 'RSSSL_IS_FLYWHEEL' ) ) {
			define( 'RSSSL_IS_FLYWHEEL', isset( $_SERVER['SERVER_SOFTWARE'] ) && strpos( $_SERVER['SERVER_SOFTWARE'], 'Flywheel/' ) === 0 );
		}
		if ( ! defined( 'RSSSL_IS_PRESSABLE' ) ) {
			define( 'RSSSL_IS_PRESSABLE', ( defined( 'IS_ATOMIC' ) && IS_ATOMIC ) || ( defined( 'IS_PRESSABLE' ) && IS_PRESSABLE ) );
		}
	}

	/**
	 * Main installer for the firewall file
	 *
	 * @return void
	 */
	public function install(): void {
		// Don't regenerate files during deactivation
		if ( doing_action( 'rsssl_deactivate' ) ) {
			return;
		}

		if ( ! rsssl_admin_logged_in() ) {
			return;
		}

		if ( wp_doing_ajax() ) {
			return;
		}

		if ( empty( $this->rules ) ) {
			$this->rules = apply_filters( 'rsssl_firewall_rules', '' );
		}

		// no rules? remove the file.
		if ( empty( trim( $this->rules ) ) ) {
			$this->remove_prepend_file_in_htaccess();
			$this->remove_prepend_file_in_wp_config();
			return;
		}

		// update the file to be included.
		$this->update_firewall( $this->rules );

		$this->include_prepend_file_in_wp_config();
		if ( $this->uses_htaccess() ) {
			// only include in the admin_init, to prevent issues with the htaccess file not being writable.
			if( current_filter() !== 'plugins_loaded' ) {
				$this->include_prepend_file_in_htaccess();
			}
		}

		if ( $this->has_user_ini_file() ) {
			$this->include_prepend_file_in_user_ini();
		}
	}

	/**
	 * Remove file and file inclusions
	 *
	 * @return void
	 */
	public function uninstall(): void {
		if ( ! rsssl_user_can_manage() ) {
			return;
		}

		if ( wp_doing_ajax() ) {
			return;
		}

		$this->remove_prepend_file_in_htaccess();
		$this->remove_prepend_file_in_wp_config();
		$this->remove_auto_prepend_file_in_user_ini();

		$this->empty_file();
		$this->delete_test_file();

		// Delete firewall.php file using the existing handler
		if ( class_exists( '\RSSSL\Pro\Security\WordPress\Firewall\Rsssl_Firewall_File_Handler' ) ) {
			$firewall_handler = new \RSSSL\Pro\Security\WordPress\Firewall\Rsssl_Firewall_File_Handler();
			$firewall_handler->delete();
		}
	}

	/**
	 * Proactively check for environment changes on admin loads
	 * This ensures firewall regeneration after site clones/migrations
	 *
	 * @return void
	 */
	public function maybe_regenerate_firewall(): void {

		if ( ! rsssl_user_can_manage() ) {
			return;
		}

		// Only check if we have firewall rules that need to be active
		if ( ! $this->has_rules() ) {
			return;
		}

		// Only run the check if environment has changed
		if ( $this->should_regenerate_firewall() ) {
			// Trigger the full installation process for firewall.php
			$this->install();
			// Also generate the Geo Block firewall settings
			$fireWallSettingIsEnabled = rsssl_get_option( 'enable_firewall', false );
			if ( $fireWallSettingIsEnabled ) {
				$geoBlock = Rsssl_Geo_Block::get_instance();
				$geoBlock->generate_firewall_rules();
			}
		}
	}

	/**
	 * Check if our firewall file exists
	 *
	 * @param string $file // filename, including path
	 *
	 * @return bool
	 */
	private function file_exists( string $file ): bool {
		$wp_filesystem = $this->get_file_system();

		// Use WP Filesystem if available, otherwise fall back to direct operations
		return $wp_filesystem ? $wp_filesystem->is_file( $file ) : file_exists( $file );
	}

	/**
	 * Get the WP_Filesystem instance with lazy loading
	 *
	 * @return false|WP_Filesystem_Base
	 */
	private function get_file_system() {
		// Return cached instance if available
		if ( $this->wp_filesystem !== null ) {
			return $this->wp_filesystem;
		}

		if ( ! function_exists( 'WP_Filesystem' ) ) {
			include_once ABSPATH . 'wp-admin/includes/file.php';
		}
		if ( false === ( $creds = request_filesystem_credentials( site_url(), '', false, false, null ) ) ) {
			$this->wp_filesystem = false;
			return false; // stop processing here.
		}
		global $wp_filesystem;
		if ( ! WP_Filesystem( $creds ) ) {
			// request_filesystem_credentials(site_url(), '', true, false, null);//phpcs:ingore
			$this->wp_filesystem = false;
			return false;
		}

		// Cache the instance
		$this->wp_filesystem = $wp_filesystem;
		return $wp_filesystem;
	}

	/**
	 * Update the file that contains the firewall rules, advanced-headers.php
	 *
	 * @param string $rules //rules to add to the firewall.
	 *
	 * @return void
	 */
	public function update_firewall( string $rules ): void
	{
		if ( ! rsssl_admin_logged_in() ) {
			return;
		}

		$contents  = '<?php' . "\n";
		$contents .= '/**' . "\n";
		$contents .= '* This file is created by Really Simple Security' . "\n";
		$contents .= '*/' . "\n\n";
		$contents .= 'if (defined("SHORTINIT") && SHORTINIT) return;' . "\n\n";
		$contents .= '$base_path = dirname(__FILE__);' . "\n";
		$contents .= 'if( file_exists( $base_path . "/rsssl-safe-mode.lock" ) ) {' . "\n";
		$contents .= '    if ( ! defined( "RSSSL_SAFE_MODE" ) ) {' . "\n";
		$contents .= '        define( "RSSSL_SAFE_MODE", true );' . "\n";
		$contents .= '    }' . "\n";
		$contents .= '    return;' . "\n";
		$contents .= '}' . "\n\n";
		// allow disabling of headers for detection purposes.
		$contents .= 'if ( isset($_GET["rsssl_header_test"]) && (int) $_GET["rsssl_header_test"] ===  ' . $this->get_headers_nonce() . ' ) return;' . "\n\n";
		//if already included at some point, don't execute again.
		$contents .= 'if ( defined("RSSSL_HEADERS_ACTIVE" ) ) return;' . "\n";
		$contents .= 'define( "RSSSL_HEADERS_ACTIVE", true );' . "\n";

		// If the main firewall (firewall.php) is enabled, add the include directive for it.
		if ( rsssl_get_option( 'enable_firewall', false ) ) {
			$firewallFilePath = $this->firewall_file_path;
			$contents        .= 'if ( file_exists( "' . $firewallFilePath . '" ) ) {' . "\n";
			$contents        .= '    require_once "' . $firewallFilePath . '";' . "\n";
			$contents        .= '}' . "\n\n";
		}

		$contents .= "//RULES START\n" . $rules;

		$this->put_contents( $this->file, $contents );
	}

	/**
	 * Save data
	 *
	 * @param string $file //filename, including path.
	 * @param string $contents //data to save.
	 *
	 * @return void
	 */
	private function put_contents( $file, $contents ): void {
		if ( ! rsssl_admin_logged_in() ) {
			return;
		}

		// Check if file is writable (or doesn't exist yet, which is fine)
		if ( $this->file_exists( $file ) && ! $this->is_writable( $file ) ) {
			return;
		}

		$wp_filesystem = $this->get_file_system();

		if ( $wp_filesystem === false ) {
			file_put_contents( $file, $contents );//phpcs:ignore
			return;
		}

		$wp_filesystem->put_contents( $file, $contents );

		// Only chmod files other than .htaccess and wp-config.php
		if ( strpos($file, 'htaccess') === false && strpos($file, 'wp-config.php') === false ) {
			$wp_filesystem->chmod( $file, 0644 );
		}
	}

	/**
	 * Get the contents of a file
	 *
	 * @param string $file //filename, including path.
	 *
	 * @return string
	 */
	private function get_contents( string $file ): string {
		// Validate that file path is not empty
		if ( empty( $file ) ) {
			return '';
		}

		$wp_filesystem = $this->get_file_system();

		if ( $wp_filesystem === false ) {
			return file_exists( $file ) ? file_get_contents( $file ) : '';//phpcs:ignore
		}

		$result = $wp_filesystem->get_contents( $file );
		return $result ? $result : '';
	}

	/**
	 * Empty the advanced-headers.php file instead of deleting it.
	 * This prevents 500 errors when user.ini is cached and still references this file.
	 *
	 * @return void
	 */
	private function empty_file(): void {
		if ( ! rsssl_user_can_manage() ) {
			return;
		}

		$contents = <<<PHP
<?php
// This file was created by Really Simple Security
// It is no longer used and safe to delete
PHP;

		$this->put_contents( $this->file, $contents );
	}

	/**
	 * Get the path to the advanced-headers-test.php file.
	 *
	 * @return string
	 */
	private function get_test_file_path(): string {
		return WP_CONTENT_DIR . '/advanced-headers-test.php';
	}

	/**
	 * Delete the advanced-headers-test.php file if it exists.
	 *
	 * @return void
	 */
	private function delete_test_file(): void {
		if ( ! rsssl_user_can_manage() ) {
			return;
		}

		$test_file = $this->get_test_file_path();
		if ( ! file_exists( $test_file ) ) {
			return;
		}

		$wp_filesystem = $this->get_file_system();
		if ( $wp_filesystem === false ) {
			unlink( $test_file );//phpcs:ignore
			return;
		}

		$wp_filesystem->delete( $test_file );
	}

	/**
	 * @return bool
	 *
	 * Check if installation uses htaccess.conf (Bitnami)
	 */
	private function uses_htaccess_conf() {
		$htaccess_conf_file = dirname( ABSPATH ) . '/conf/htaccess.conf';
		//conf/htaccess.conf can be outside of open basedir, return false if so
		$open_basedir = ini_get( 'open_basedir' );
		if ( ! empty( $open_basedir ) ) {
			return false;
		}
		return is_file( $htaccess_conf_file );
	}

	/**
	 * Get the .htaccess path
	 *
	 * @return string
	 */
	private function htaccess_path(): string {

		if ( $this->uses_htaccess_conf() ) {
			$htaccess_file = realpath( dirname( ABSPATH ) . '/conf/htaccess.conf' );
		} else {
			$htaccess_file = $this->get_home_path() . '.htaccess';
		}

		return $htaccess_file;

	}

	/**
	 * Get the home path
	 *
	 * @return string
	 */
	public function get_home_path(): string {
		if ( ! function_exists( 'get_home_path' ) ) {
			include_once ABSPATH . 'wp-admin/includes/file.php';
		}
		if ( defined('RSSSL_IS_FLYWHEEL') && RSSSL_IS_FLYWHEEL && isset( $_SERVER['DOCUMENT_ROOT'] ) ) {
			return trailingslashit( $this->sanitize_path( wp_unslash( $_SERVER['DOCUMENT_ROOT'] ) ) );
		}
		return get_home_path();
	}

	/**
	 * Sanitize a path
	 *
	 * @param string $path //string to sanitize.
	 *
	 * @return string
	 */
	private function sanitize_path( $path ): string {
		// prevent path traversal.
		return str_replace( '../', '/', realpath( sanitize_text_field( $path ) ) );
	}

	/**
	 * Check if this server uses .htaccess. Not by checking the server header, but simply by checking
	 * if the htaccess file exists.
	 *
	 * @return bool
	 */
	private function uses_htaccess(): bool {
		return $this->file_exists( $this->htaccess_path() );
	}

	/**
	 * Include the prepend file in the .htaccess
	 *
	 * @return void
	 */
	public function include_prepend_file_in_htaccess(): void
	{
		if ( ! $this->file_exists( $this->file ) ) {
			return;
		}

		// check if the wp-config contains the if constant condition, to prevent duplicate loading. If not, try upgrading. If that fails, skip.
		if ( ! $this->wp_config_contains_latest() ) {
			return;
		}

		$htaccess_file = $this->htaccess_path();
		if ( !$this->file_exists($htaccess_file) || !$this->is_writable($htaccess_file) ) {
			return;
		}

		$htaccess_manager = new RSSSL_Htaccess_File_Manager();
		$rules_string = $this->get_htaccess_rules();

		$rule_definition = [
			'marker' => self::HTACCESS_MARKER_PREPEND,
			'lines' => empty(trim($rules_string)) ? [] : explode("\n", $rules_string),
		];
		$htaccess_manager->write_rule($rule_definition, 'include prepend file in htaccess');
	}

	/**
	 * Get the .htaccess rules for the prepend file
	 * Add user.ini blocking rules if user.ini filename exist.
	 *
	 * @return string //the string containing the lines of rules
	 */
	private function get_htaccess_rules() : string
	{
		if ( defined('RSSSL_HTACCESS_SKIP_AUTO_PREPEND') && RSSSL_HTACCESS_SKIP_AUTO_PREPEND ) {
			return '';
		}
		if (isset(RSSSL()->server) ) {
			$config = RSSSL()->server->auto_prepend_config();
		} else {
			$config = get_option('rsssl_auto_prepend_config');
			if (empty($config)) {
				return '';
			}
		}
		$file = addcslashes($this->file, "'");
		switch ($config) {
			case 'litespeed':
				$rules = array(
					'<IfModule LiteSpeed>',
					'php_value auto_prepend_file ' . $file ,
					'</IfModule>',
					'<IfModule lsapi_module>',
					'php_value auto_prepend_file ' . $file,
					'</IfModule>',
				);
				break;
			case 'apache-mod_php':
			default:
				$rules = array(
					'<IfModule mod_php7.c>',
					'php_value auto_prepend_file ' . $file ,
					'</IfModule>',
					'<IfModule mod_php.c>',
					'php_value auto_prepend_file ' . $file,
					'</IfModule>',
				);
		}

		$userIni = ini_get('user_ini.filename');
		if ($userIni) {
			array_push(
				$rules,
				sprintf( '<Files "%s">',
					addcslashes( $userIni, '"' ) ),
				'<IfModule mod_authz_core.c>',
				'Require all denied', '</IfModule>',
				'<IfModule !mod_authz_core.c>',
				'Order deny,allow', 'Deny from all',
				'</IfModule>',
				'</Files>'
			);
		}

		return implode( "\n", $rules );
	}

	/**
	 * Include the file in the wp-config
	 *
	 * @return void
	 */
	private function include_prepend_file_in_wp_config(): void {
		if ( ! rsssl_user_can_manage() ) {
			return;
		}
		$file = $this->wpconfig_path();
		if ( empty( $file ) ) {
			update_option( 'rsssl_firewall_error', 'wpconfig-notfound', false );
			return;
		}
		$content = $this->get_contents( $file );
		if ( strpos( $content, 'advanced-headers.php' ) === false ) {
			$rule = $this->get_wp_config_rule();

			// if RSSSL comment is found, insert after.
			$rsssl_comment = '//END Really Simple Security Server variable fix';
			if ( strpos( $content, $rsssl_comment ) !== false ) {
				$pos     = strrpos( $content, $rsssl_comment );
				$updated = substr_replace( $content, $rsssl_comment . "\n" . $rule . "\n", $pos, strlen( $rsssl_comment ) );
			} else {
				$updated = preg_replace( '/<\?php/', "<?php\n" . $rule . "\n", $content, 1 );
			}

			if ( strpos( $updated, "\n\n\n" ) !== false ) {
				$updated = str_replace( "\n\n\n", "\n\n", $updated );
			}

			$this->put_contents( $file, $updated );
		}

		// save errors.
		if ( $this->is_writable( WP_CONTENT_DIR ) && ( $this->is_writable( $file ) || strpos( $content, 'advanced-headers.php' ) !== false ) ) {
			update_option( 'rsssl_firewall_error', false, false );
		} elseif ( ! $this->is_writable( $file ) ) {
			update_option( 'rsssl_firewall_error', 'wpconfig-notwritable', false );
		} elseif ( ! $this->is_writable( WP_CONTENT_DIR ) ) {
			update_option( 'rsssl_firewall_error', 'advanced-headers-notwritable', false );
		}
	}

	/**
	 * Clear the rules
	 *
	 * @return void
	 */
	public function remove_prepend_file_in_htaccess(): void
	{
		if ( ! rsssl_user_can_manage() ) {
			return;
		}

		// Initialize htAccessFile if not set
		if ( ! isset($this->htAccessFile) ) {
			$this->htAccessFile = new RSSSL_Htaccess_File_Manager();
		}

		// Determine the correct .htaccess file path this instance of firewall manager should use.
		$specific_htaccess_path = $this->htaccess_path();

		// Ensure the injected htaccess_file_manager service instance is configured to use this specific path.
		$this->htAccessFile->set_htaccess_file_path($specific_htaccess_path);

		// Call clear_rule on the htaccess_file_manager service.
		// The service itself is responsible for handling file existence and writability.
		$this->htAccessFile->clear_rule(self::HTACCESS_MARKER_PREPEND, 'testregel');
	}

	/**
	 * Remove the prepend file from the config
	 *
	 * @return void
	 */
	private function remove_prepend_file_in_wp_config(): void {
		if ( ! rsssl_user_can_manage() ) {
			return;
		}

		$file = $this->wpconfig_path();
		if ( empty( $file ) ) {
			return;
		}
		if ( $this->is_writable( $file ) ) {
			$content = $this->get_contents( $file );
			$rule    = $this->get_wp_config_rule();
			if ( strpos( $content, $rule ) !== false ) {
				$content = str_replace( $rule, '', $content );
				if ( strpos( $content, "\n\n\n" ) !== false ) {
					$content = str_replace( "\n\n\n", "\n\n", $content );
				}
				$this->put_contents( $file, $content );
			}
		}
	}

	/**
	 * Wrapper function
	 *
	 * @param string $file // filename, including path.
	 *
	 * @return bool
	 */
	private function is_writable( $file ): bool {
		$wp_filesystem = $this->get_file_system();

		// Use WP Filesystem if available, otherwise fall back to direct operations
		return $wp_filesystem ? $wp_filesystem->is_writable( $file ) : is_writable( $file );//phpcs:ignore
	}

	/**
	 * This class has it's own settings page, to ensure it can always be called
	 *
	 * @return bool
	 */
	public function is_settings_page() {
		if ( rsssl_is_logged_in_rest() ) {
			return true;
		}

		if ( isset( $_GET['page'] ) && 'really-simple-security' === $_GET['page'] ) {//phpcs:ignore
			return true;
		}

		return false;
	}

	/**
	 * Generate and return a random nonce
	 *
	 * @return int
	 */
	public function get_headers_nonce() {
		if ( ! get_site_option( 'rsssl_header_detection_nonce' ) ) {
			update_site_option( 'rsssl_header_detection_nonce', wp_rand( 1000, 999999999 ) );
		}
		return (int) get_site_option( 'rsssl_header_detection_nonce' );
	}

	/**
	 * Check if any rules were added
	 *
	 * @return bool
	 */
	public function has_rules() {

		if ( empty( $this->rules ) ) {
			$this->rules = apply_filters( 'rsssl_firewall_rules', '' );
		}
		return ! empty( trim( $this->rules ) );
	}

	/**
	 * Get the status for the firewall rules writing
	 *
	 * @return false|string
	 */
	public function firewall_write_error() {
		return get_site_option( 'rsssl_firewall_error' );
	}

	/**
	 * Get the status for the firewall
	 *
	 * @return bool
	 */
	public function firewall_active_error() {
		if ( ! $this->has_rules() ) {
			return false;
		}
		return ! defined( 'RSSSL_HEADERS_ACTIVE' );
	}

	/**
	 * Show some notices
	 *
	 * @param array $notices //array of notices.
	 *
	 * @return array
	 */
	public function notices( $notices ) {
		$notices['firewall-error']  = array(
			'callback'          => 'RSSSL_SECURITY()->firewall_manager->firewall_write_error',
			'score'             => 5,
			'output'            => array(
				'wpconfig-notwritable'         => array(
					'title'       => __( 'Firewall', 'really-simple-ssl' ),
					'msg'         => __( 'A firewall rule was enabled, but the wp-config.php is not writable.', 'really-simple-ssl' ) . ' ' . __( 'Please set the wp-config.php to writable until the rule has been written.', 'really-simple-ssl' ),
					'icon'        => 'open',
					'dismissible' => true,
				),
				'advanced-headers-notwritable' => array(
					'title'       => __( 'Firewall', 'really-simple-ssl' ),
					'msg'         => __( 'A firewall rule was enabled, but /the wp-content/ folder is not writable.', 'really-simple-ssl' ) . ' ' . __( 'Please set the wp-content folder to writable:', 'really-simple-ssl' ),
					'icon'        => 'open',
					'dismissible' => true,
				),
			),
			'show_with_options' => array(
				'disable_http_methods',
			),
		);
		$notices['firewall-active'] = array(
			'condition'         => array( 'RSSSL_SECURITY()->firewall_manager->firewall_active_error' ),
			'callback'          => '_true_',
			'score'             => 5,
			'output'            => array(
				'true' => array(
					'title'       => __( 'Firewall', 'really-simple-ssl' ),
					'msg'         => __( 'A firewall rule was enabled, but the firewall does not seem to get loaded correctly.', 'really-simple-ssl' ) . ' ' . __( 'Please check if the advanced-headers.php file is included in the wp-config.php, and exists in the wp-content folder.', 'really-simple-ssl' ),
					'icon'        => 'open',
					'dismissible' => true,
				),
			),
			'show_with_options' => array(
				'disable_http_methods',
			),
		);
		return $notices;
	}

	/**
	 * // As WP_CONTENT_DIR is not defined at this point in the wp-config, we can't use that.
	 * // for those setups where the WP_CONTENT_DIR is not in the default location, we hardcode the path.
	 *
	 * @return string
	 */
	public function get_wp_config_rule() {
		if ( $this->dynamic_path ) {
			$rule  = 'if (!defined("RSSSL_HEADERS_ACTIVE") && file_exists( ABSPATH . "wp-content/advanced-headers.php")) {' . "\n";
			$rule .= "\t" . 'require_once ABSPATH . "wp-content/advanced-headers.php";' . "\n" . '}';
		} else {
			$rule  = 'if (!defined("RSSSL_HEADERS_ACTIVE") && file_exists(\'' . WP_CONTENT_DIR . '/advanced-headers.php\')) {' . "\n";
			$rule .= "\t" . 'require_once \'' . WP_CONTENT_DIR . '/advanced-headers.php\';' . "\n" . '}';
		}
		return $rule;
	}

	/**
	 * Check if the wp-config contains the if constant condition, to prevent duplicate loading. If not, try upgrading. If that fails, skip.
	 * Wrapper function added for clearer purpose in code
	 *
	 * @return bool
	 */
	private function wp_config_contains_latest(): bool {
		return $this->update_wp_config_rule();
	}

	/**
	 * Called in upgrade.php, to upgrade older rules to the latest.
	 * Returns true if the wpconfig contains the upgraded lines
	 *
	 * @return bool
	 */
	public function update_wp_config_rule(): bool {
		$file = $this->wpconfig_path();
		if ( ! $file ) {
			return false;
		}

		$content = $this->get_contents( $file );
		$find    = '(file_exists( ABSPATH . "wp-content/advanced-headers.php"))';
		if ( false !== strpos( $content, $find ) ) {
			if ( ! $this->is_writable( $file ) ) {
				return false;
			}
			$replace = '(!defined("RSSSL_HEADERS_ACTIVE") && file_exists( ABSPATH . "wp-content/advanced-headers.php"))';
			$content = str_replace( $find, $replace, $content );
			$this->put_contents( $file, $content );
		}
		return true;
	}

	/**
	 * Admin is not always loaded here, so we define our own function
	 *
	 * @return string|null
	 */
	public function wpconfig_path() {

		// Allow the wp-config.php path to be overridden via a filter.
		$filtered_path = apply_filters( 'rsssl_wpconfig_path', '' );

		// If a filtered path is provided, validate it.
		if ( ! empty( $filtered_path ) ) {
			$directory = dirname( $filtered_path );

			// Ensure the directory exists before checking for the file.
			if ( is_dir( $directory ) && $this->file_exists( $filtered_path ) ) {
				return $filtered_path;
			}
		}

		// Limit number of iterations to 5.
		$i             = 0;
		$maxiterations = 5;
		$dir           = ABSPATH;
		do {
			++ $i;
			if ( $this->file_exists( $dir . 'wp-config.php' ) ) {
				return $dir . 'wp-config.php';
			}
		} while ( ( $dir = realpath( "$dir/.." ) ) && ( $i < $maxiterations ) );//phpcs:ignore

		return '';
	}

	/**
	 * Clear the headers
	 *
	 * @return void
	 */
	public function remove_advanced_headers() {
		$this->uninstall();
	}

	/**
	 * Check if the firewall file should be regenerated
	 * This detects environment changes like WP Engine clones
	 * Also returns true if the file does not exist yet
	 *
	 * @return bool
	 */
	private function should_regenerate_firewall(): bool {

		if ( ! $this->file_exists( $this->file ) ) {
			return true;
		}

		// Check if we have stored environment signature
		$stored_signature = get_option( 'rsssl_firewall_environment_signature' );
		$current_signature = $this->get_environment_signature();

		// If no stored signature, store it and regenerate
		if ( ! $stored_signature ) {
			update_option( 'rsssl_firewall_environment_signature', $current_signature, false );
			return true;
		}

		// If signature changed, update it and regenerate
		if ( $stored_signature !== $current_signature ) {
			update_option( 'rsssl_firewall_environment_signature', $current_signature, false );
			return true;
		}

		return false;
	}

	/**
	 * Generate a signature of the current environment
	 * Used to detect when the site has been cloned or migrated
	 *
	 * @return string
	 */
	private function get_environment_signature(): string {
		$signature_parts = array(
			WP_CONTENT_DIR,
			ABSPATH,
			get_home_url(),
			get_site_url(),
		);

		return md5( implode( '|', $signature_parts ) );
	}

	/**
	 * Get the advanced headers file path
	 * Always uses WP_CONTENT_DIR which is dynamically set by WordPress
	 *
	 * @return string
	 */
	private function get_advanced_headers_path(): string {
		return WP_CONTENT_DIR . '/advanced-headers.php';
	}

	/**
	 * Check if we can use a dynamic path for the advanced headers file
	 * @return string
	 */
	private function get_dynamic_path(): string {
		return WP_CONTENT_DIR === ABSPATH . 'wp-content';
	}

	/**
	 * Check if a user.ini file exists or is in user.
	 *
	 * @return bool
	 */
	private function has_user_ini_file():bool {
		$userIni = ini_get('user_ini.filename');
		if ( $userIni ) {
			return true;
		}
		return false;
	}

	/**
	 * Add auto prepend file to user.ini
	 *
	 * @return void
	 */
	private function include_prepend_file_in_user_ini():void{
		if ( ! rsssl_user_can_manage() ) {
			return;
		}

		if ( defined('RSSSL_HTACCESS_SKIP_AUTO_PREPEND') && RSSSL_HTACCESS_SKIP_AUTO_PREPEND ) {
			return;
		}

		$config = RSSSL()->server->auto_prepend_config();
		if ( !$this->has_user_ini_file() ) {
			return;
		}
		$autoPrependIni = '';
		$userIniPath = $this->get_user_ini_path();
		// .user.ini configuration
		switch ($config) {
			case 'cgi':
			case 'nginx':
			case 'apache-suphp':
			case 'litespeed':
			case 'iis':
				$autoPrependIni = sprintf("; BEGIN Really Simple Auto Prepend File
auto_prepend_file = '%s'
; END Really Simple Auto Prepend File", addcslashes($this->file, "'"));
				break;
		}

		if ( !empty($autoPrependIni) ) {
			// Modify .user.ini
			$userIniContent = $this->get_contents($userIniPath);
			if ( $userIniContent ) {
				$userIniContent = str_replace('auto_prepend_file', ';auto_prepend_file', $userIniContent);
				$regex = '/; BEGIN Really Simple Auto Prepend File.*?; END Really Simple Auto Prepend File/is';
				if (preg_match($regex, $userIniContent, $matches)) {
					$userIniContent = preg_replace($regex, $autoPrependIni, $userIniContent);
				} else {
					$userIniContent .= "\n" . $autoPrependIni;
				}
			} else {
				$userIniContent = $autoPrependIni;
			}

			$this->put_contents($userIniPath, $userIniContent);
		}
	}

	/**
	 * Get the user.ini path
	 *
	 * @return false|string
	 */
	public function get_user_ini_path() {
		$userIni = ini_get('user_ini.filename');
		if ($userIni) {
			return $this->get_home_path() . $userIni;
		}
		return false;
	}

	/**
	 * Remove the added auto prepend file
	 *
	 * @return void
	 */
	private function remove_auto_prepend_file_in_user_ini() {
		if ( ! rsssl_user_can_manage() ) {
			return;
		}

		if ( ! $this->has_user_ini_file() ) {
			return;
		}

		$userIniPath = $this->get_user_ini_path();
		if ($userIniPath === null) {
			return;
		}
		$userIniContent = $this->get_contents( $userIniPath );
		$userIniContent = preg_replace( '/; BEGIN Really Simple Auto Prepend File.*?; END Really Simple Auto Prepend File/is', '', $userIniContent );
		$userIniContent = str_replace( 'auto_prepend_file', ';auto_prepend_file', $userIniContent );
		$this->put_contents( $userIniPath, $userIniContent );
	}

}