<?php
declare(strict_types=1);

namespace Help4BuilderSuite\Modules\Security;

use Help4BuilderSuite\Contracts\ModuleContract;
use Help4BuilderSuite\Support\AdminRequest;
use Help4BuilderSuite\Support\FeatureFlags;
use Help4BuilderSuite\Support\IpTools;
use Help4BuilderSuite\Support\Settings;
use WP_Error;
use WP_User;

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

final class Module implements ModuleContract
{
    private const SALT_ROTATE_EVENT = 'h4bs_rotate_salts_hourly';
    private const SALT_LAST_OPTION = 'h4bs_last_salt_rotation';

    public function key(): string
    {
        return 'security';
    }

    public function boot(): void
    {
        add_action('admin_menu', [$this, 'registerAdminPage']);
        add_action('admin_init', [$this, 'handleAdminActions']);
        add_action('admin_init', [$this, 'ensureSaltRotationSchedule']);
        add_action(self::SALT_ROTATE_EVENT, [$this, 'maybeRotateSalts']);

        add_action('init', [$this, 'applyCoreHardening'], 1);
        add_action('init', [$this, 'enforceCloudflareOrigin'], 0);
        add_action('init', [$this, 'blockBadRequests'], 0);
        add_action('send_headers', [$this, 'sendSecurityHeaders']);
        add_filter('xmlrpc_enabled', [$this, 'xmlRpcEnabled']);
        add_filter('authenticate', [$this, 'throttleAuthentication'], 40, 3);
        add_action('wp_login_failed', [$this, 'recordFailedLogin']);
        add_action('wp_login', [$this, 'clearFailedLogins'], 10, 2);
        add_action('template_redirect', [$this, 'forceHttps']);
        add_action('admin_notices', [$this, 'renderCommerceSslNotice']);

        if (Settings::bool('security_hide_wp_version', true)) {
            add_filter('the_generator', static fn (): string => '');
        }
    }

    public function registerAdminPage(): void
    {
        add_menu_page(
            __('Help4 Security', 'help4-builder-suite'),
            __('Security', 'help4-builder-suite'),
            'manage_options',
            'h4bs-security',
            [$this, 'renderAdminPage'],
            'dashicons-shield',
            59
        );
    }

    public function handleAdminActions(): void
    {
        if (! is_admin() || ! current_user_can('manage_options')) {
            return;
        }

        $post = wp_unslash($_POST);
        $action = isset($post['h4bs_security_action']) ? sanitize_key((string) $post['h4bs_security_action']) : '';
        if ($action === 'save_security_settings') {
            check_admin_referer('h4bs_save_security_settings', 'h4bs_save_security_settings_nonce');
            $this->saveSecuritySettings(wp_unslash($_POST));
            $this->redirectWithNotice('settings_saved');
        }

        if ($action === 'rotate_salts_now') {
            check_admin_referer('h4bs_rotate_salts_now', 'h4bs_rotate_salts_now_nonce');
            $ok = $this->rotateWpConfigSalts();
            if ($ok) {
                update_option(self::SALT_LAST_OPTION, (string) time(), false);
                $this->redirectWithNotice('salts_rotated');
            }
            $this->redirectWithNotice('salts_failed');
        }
    }

    public function renderAdminPage(): void
    {
        if (! current_user_can('manage_options')) {
            return;
        }

        $settings = Settings::all();
        $remoteAddr = IpTools::remoteAddr();
        $cfHeader = isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? sanitize_text_field((string) wp_unslash($_SERVER['HTTP_CF_CONNECTING_IP'])) : '';
        $isCloudflareOrigin = $remoteAddr !== '' && IpTools::inAnyCidr($remoteAddr, $this->cloudflareCidrs());
        $commerceEnabled = $this->isCommerceFeatureEnabled();
        $sslRequiredForCommerce = $this->commerceSslRequired();
        $lastSaltRotate = (int) get_option(self::SALT_LAST_OPTION, 0);
        $configPath = $this->locateWpConfigPath();
        $configPerms = $configPath !== '' && is_file($configPath)
            ? substr(sprintf('%o', (int) @fileperms($configPath)), -4)
            : 'n/a';
        $notice = AdminRequest::queryKey('h4bs_notice');
        ?>
        <div class="wrap h4bs-admin-wrap">
            <h1><?php esc_html_e('Help4 Security + SSL Control Center', 'help4-builder-suite'); ?></h1>
            <p><?php esc_html_e('Security-first controls inspired by enterprise hardening patterns. SSL and commerce requirements are enforced automatically when configured.', 'help4-builder-suite'); ?></p>

            <?php if ($notice === 'settings_saved') : ?>
                <div class="notice notice-success"><p><?php esc_html_e('Security settings saved.', 'help4-builder-suite'); ?></p></div>
            <?php elseif ($notice === 'salts_rotated') : ?>
                <div class="notice notice-success"><p><?php esc_html_e('WordPress salts rotated successfully.', 'help4-builder-suite'); ?></p></div>
            <?php elseif ($notice === 'salts_failed') : ?>
                <div class="notice notice-error"><p><?php esc_html_e('Salt rotation failed. Ensure wp-config.php is writable by PHP and contains salt constants.', 'help4-builder-suite'); ?></p></div>
            <?php endif; ?>

            <table class="widefat striped" style="max-width:1100px;margin-bottom:16px;">
                <tbody>
                    <tr><th><?php esc_html_e('Current REMOTE_ADDR', 'help4-builder-suite'); ?></th><td><?php echo esc_html($remoteAddr !== '' ? $remoteAddr : '(unknown)'); ?></td></tr>
                    <tr><th><?php esc_html_e('CF-Connecting-IP', 'help4-builder-suite'); ?></th><td><?php echo esc_html($cfHeader !== '' ? $cfHeader : '(not present)'); ?></td></tr>
                    <tr><th><?php esc_html_e('Cloudflare Edge Detected', 'help4-builder-suite'); ?></th><td><?php echo $isCloudflareOrigin ? esc_html__('Yes', 'help4-builder-suite') : esc_html__('No', 'help4-builder-suite'); ?></td></tr>
                    <tr><th><?php esc_html_e('Commerce Module Enabled', 'help4-builder-suite'); ?></th><td><?php echo $commerceEnabled ? esc_html__('Yes', 'help4-builder-suite') : esc_html__('No', 'help4-builder-suite'); ?></td></tr>
                    <tr><th><?php esc_html_e('Commerce Requires SSL', 'help4-builder-suite'); ?></th><td><?php echo $sslRequiredForCommerce ? esc_html__('Yes', 'help4-builder-suite') : esc_html__('No', 'help4-builder-suite'); ?></td></tr>
                    <tr><th><?php esc_html_e('wp-config.php Path', 'help4-builder-suite'); ?></th><td><?php echo esc_html($configPath !== '' ? $configPath : '(not found)'); ?></td></tr>
                    <tr><th><?php esc_html_e('wp-config.php Permissions', 'help4-builder-suite'); ?></th><td><code><?php echo esc_html($configPerms); ?></code></td></tr>
                    <tr><th><?php esc_html_e('Last Salt Rotation (UTC)', 'help4-builder-suite'); ?></th><td><?php echo $lastSaltRotate > 0 ? esc_html(gmdate('Y-m-d H:i:s', $lastSaltRotate) . ' UTC') : esc_html__('Never', 'help4-builder-suite'); ?></td></tr>
                </tbody>
            </table>

            <form method="post" style="background:#fff;border:1px solid #dbe7e6;padding:1rem;border-radius:8px;max-width:1100px;">
                <?php wp_nonce_field('h4bs_save_security_settings', 'h4bs_save_security_settings_nonce'); ?>
                <input type="hidden" name="h4bs_security_action" value="save_security_settings">

                <h2><?php esc_html_e('Origin + WAF Hardening', 'help4-builder-suite'); ?></h2>
                <table class="form-table" role="presentation">
                    <tbody>
                        <?php $this->boolField('security_require_cloudflare_origin', __('Require Cloudflare Origin (Block Direct IP Hits)', 'help4-builder-suite'), $settings); ?>
                        <?php $this->boolField('security_trust_cf_connecting_ip', __('Trust CF-Connecting-IP Header', 'help4-builder-suite'), $settings); ?>
                        <?php $this->textField('security_cloudflare_cidrs', __('Cloudflare CIDRs Override (comma separated)', 'help4-builder-suite'), $settings); ?>
                        <?php $this->textField('security_origin_bypass_cidrs', __('Origin Bypass CIDRs (comma separated)', 'help4-builder-suite'), $settings); ?>
                        <?php $this->boolField('security_block_bad_queries', __('Block Malicious Query Strings', 'help4-builder-suite'), $settings); ?>
                    </tbody>
                </table>

                <h2><?php esc_html_e('SSL + HTTPS Enforcement', 'help4-builder-suite'); ?></h2>
                <table class="form-table" role="presentation">
                    <tbody>
                        <?php $this->boolField('ssl_force_https', __('Force HTTPS Redirect', 'help4-builder-suite'), $settings); ?>
                        <?php $this->boolField('ssl_hsts_enabled', __('Enable HSTS Header', 'help4-builder-suite'), $settings); ?>
                        <?php $this->boolField('security_require_ssl_for_commerce', __('Require SSL when Commerce Module is enabled', 'help4-builder-suite'), $settings); ?>
                    </tbody>
                </table>

                <h2><?php esc_html_e('Headers + WordPress Hardening', 'help4-builder-suite'); ?></h2>
                <table class="form-table" role="presentation">
                    <tbody>
                        <?php $this->boolField('security_enable_headers', __('Enable Security Headers', 'help4-builder-suite'), $settings); ?>
                        <?php $this->boolField('security_csp_enabled', __('Enable Content-Security-Policy Header', 'help4-builder-suite'), $settings); ?>
                        <?php $this->textField('security_csp_policy', __('CSP Policy String', 'help4-builder-suite'), $settings); ?>
                        <?php $this->boolField('security_disable_xmlrpc', __('Disable XML-RPC', 'help4-builder-suite'), $settings); ?>
                        <?php $this->boolField('security_hide_wp_version', __('Hide WordPress Version Generator', 'help4-builder-suite'), $settings); ?>
                        <?php $this->numberField('security_max_login_attempts', __('Max Login Attempts', 'help4-builder-suite'), $settings, 3, 25); ?>
                        <?php $this->numberField('security_lock_minutes', __('Login Lockout Minutes', 'help4-builder-suite'), $settings, 1, 1440); ?>
                        <?php $this->boolField('security_enforce_wp_config_permissions', __('Enforce wp-config.php Permission 600', 'help4-builder-suite'), $settings); ?>
                    </tbody>
                </table>

                <h2><?php esc_html_e('Salt Rotation', 'help4-builder-suite'); ?></h2>
                <table class="form-table" role="presentation">
                    <tbody>
                        <?php $this->boolField('security_rotate_salts_enabled', __('Enable Scheduled Salt Rotation', 'help4-builder-suite'), $settings); ?>
                        <?php $this->numberField('security_rotate_salts_interval_hours', __('Salt Rotation Interval (hours)', 'help4-builder-suite'), $settings, 24, 720); ?>
                    </tbody>
                </table>

                <?php submit_button(__('Save Security Settings', 'help4-builder-suite')); ?>
            </form>

            <form method="post" style="margin-top:12px;">
                <?php wp_nonce_field('h4bs_rotate_salts_now', 'h4bs_rotate_salts_now_nonce'); ?>
                <input type="hidden" name="h4bs_security_action" value="rotate_salts_now">
                <?php submit_button(__('Rotate Salts Now', 'help4-builder-suite'), 'secondary', 'submit', false); ?>
            </form>
        </div>
        <?php
    }

    public function applyCoreHardening(): void
    {
        if (Settings::bool('security_enforce_wp_config_permissions', true)) {
            $this->enforceWpConfigPermissions();
        }

        if ($this->commerceSslRequired()) {
            $this->syncCommerceSslForceSetting();
        }
    }

    public function blockBadRequests(): void
    {
        if (! Settings::bool('security_block_bad_queries', true)) {
            return;
        }

        if (is_admin()) {
            return;
        }

        $queryString = isset($_SERVER['QUERY_STRING']) ? sanitize_text_field((string) wp_unslash($_SERVER['QUERY_STRING'])) : '';
        if ($queryString === '') {
            return;
        }
        $decodedQuery = rawurldecode($queryString);

        $patterns = [
            '/(?:union(?:[\s+]+all)?[\s+]+select)/i',
            '/(?:\.\.\/|\.\.\\\\|%2e%2e%2f|%2e%2e%5c)/i',
            '/(?:base64_encode|base64_decode|eval\(|sleep\(|benchmark\()/i',
            '/(?:<script|%3Cscript)/i',
        ];

        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $queryString) === 1 || preg_match($pattern, $decodedQuery) === 1) {
                status_header(403);
                wp_die('Request blocked by Help4 security layer.', 'Blocked', ['response' => 403]);
            }
        }
    }

    public function enforceCloudflareOrigin(): void
    {
        if (! Settings::bool('security_require_cloudflare_origin', false)) {
            return;
        }

        if ((defined('WP_CLI') && WP_CLI) || wp_doing_cron()) {
            return;
        }

        $remoteAddr = IpTools::remoteAddr();
        if ($remoteAddr === '') {
            return;
        }

        $bypassRaw = (string) Settings::get('security_origin_bypass_cidrs', '');
        $bypassCidrs = $this->csvToCidrs($bypassRaw);
        if ($bypassCidrs && IpTools::inAnyCidr($remoteAddr, $bypassCidrs)) {
            return;
        }

        $cloudflareCidrs = $this->cloudflareCidrs();
        if (! IpTools::inAnyCidr($remoteAddr, $cloudflareCidrs)) {
            status_header(403);
            nocache_headers();
            wp_die('Direct origin access is blocked. Use Cloudflare proxy.', 'Origin Blocked', ['response' => 403]);
        }

        if (Settings::bool('security_trust_cf_connecting_ip', true)) {
            $clientIp = isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? sanitize_text_field((string) wp_unslash($_SERVER['HTTP_CF_CONNECTING_IP'])) : '';
            if ($clientIp !== '' && filter_var($clientIp, FILTER_VALIDATE_IP)) {
                $_SERVER['REMOTE_ADDR'] = $clientIp;
            }
        }
    }

    public function sendSecurityHeaders(): void
    {
        if (! Settings::bool('security_enable_headers', true)) {
            return;
        }

        header('X-Frame-Options: SAMEORIGIN');
        header('X-Content-Type-Options: nosniff');
        header('Referrer-Policy: strict-origin-when-cross-origin');
        $permissionsPolicy = (string) apply_filters(
            'h4bs_permissions_policy',
            'geolocation=(), camera=(), microphone=()'
        );
        if ($permissionsPolicy !== '') {
            header('Permissions-Policy: ' . $permissionsPolicy);
        }
        header('X-XSS-Protection: 1; mode=block');
        header('Cross-Origin-Opener-Policy: same-origin');
        header('Cross-Origin-Resource-Policy: same-site');

        if (Settings::bool('security_csp_enabled', false)) {
            $csp = trim((string) Settings::get('security_csp_policy', "default-src 'self' https: data: blob: 'unsafe-inline' 'unsafe-eval'"));
            if ($csp !== '') {
                header('Content-Security-Policy: ' . $csp);
            }
        }

        if (Settings::bool('ssl_hsts_enabled', false) && $this->isSecureConnection()) {
            header('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload');
        }
    }

    public function xmlRpcEnabled(bool $enabled): bool
    {
        if (Settings::bool('security_disable_xmlrpc', true)) {
            return false;
        }
        return $enabled;
    }

    /**
     * @param WP_User|WP_Error|null $user
     * @return WP_User|WP_Error|null
     */
    public function throttleAuthentication($user, string $username, string $password)
    {
        $ip = IpTools::currentIp();
        if ($ip === '') {
            return $user;
        }

        $maxAttempts = max(3, min(25, (int) Settings::get('security_max_login_attempts', '6')));
        $lockMinutes = max(1, min(1440, (int) Settings::get('security_lock_minutes', '15')));
        $lockKey = 'h4bs_login_lock_' . md5($ip);
        $failKey = 'h4bs_login_fail_' . md5($ip);

        $lockedUntil = (int) get_transient($lockKey);
        if ($lockedUntil > time()) {
            return new WP_Error('h4bs_login_locked', sprintf('Too many attempts. Retry in %d minutes.', (int) ceil(($lockedUntil - time()) / 60)));
        }

        $fails = (int) get_transient($failKey);
        if ($fails >= $maxAttempts) {
            $until = time() + ($lockMinutes * 60);
            set_transient($lockKey, (string) $until, $lockMinutes * 60);
            return new WP_Error('h4bs_login_locked', sprintf('Too many attempts. Retry in %d minutes.', $lockMinutes));
        }

        return $user;
    }

    public function recordFailedLogin(string $username): void
    {
        $ip = IpTools::currentIp();
        if ($ip === '') {
            return;
        }

        $maxAttempts = max(3, min(25, (int) Settings::get('security_max_login_attempts', '6')));
        $failKey = 'h4bs_login_fail_' . md5($ip);
        $current = (int) get_transient($failKey);
        $current++;
        set_transient($failKey, (string) $current, HOUR_IN_SECONDS);

        if ($current >= $maxAttempts) {
            $lockMinutes = max(1, min(1440, (int) Settings::get('security_lock_minutes', '15')));
            $lockKey = 'h4bs_login_lock_' . md5($ip);
            $until = time() + ($lockMinutes * 60);
            set_transient($lockKey, (string) $until, $lockMinutes * 60);
        }
    }

    public function clearFailedLogins(string $userLogin, WP_User $user): void
    {
        $ip = IpTools::currentIp();
        if ($ip === '') {
            return;
        }
        delete_transient('h4bs_login_fail_' . md5($ip));
        delete_transient('h4bs_login_lock_' . md5($ip));
    }

    public function forceHttps(): void
    {
        $mustEnforce = Settings::bool('ssl_force_https', false) || $this->commerceSslRequired();
        if (! $mustEnforce) {
            return;
        }

        if (is_admin() || wp_doing_ajax()) {
            return;
        }

        if ($this->isSecureConnection()) {
            return;
        }

        $requestMethod = isset($_SERVER['REQUEST_METHOD']) ? sanitize_key((string) wp_unslash($_SERVER['REQUEST_METHOD'])) : 'GET';
        if (strtoupper($requestMethod) !== 'GET') {
            if ($this->commerceSslRequired()) {
                status_header(503);
                wp_die('Commerce requires SSL/HTTPS. Enable HTTPS before processing commerce traffic.', 'SSL Required', ['response' => 503]);
            }
            return;
        }

        $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field((string) wp_unslash($_SERVER['HTTP_HOST'])) : '';
        $requestUri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field((string) wp_unslash($_SERVER['REQUEST_URI'])) : '/';
        $url = 'https://' . $host . $requestUri;
        wp_safe_redirect($url, 301);
        exit;
    }

    public function ensureSaltRotationSchedule(): void
    {
        if (wp_next_scheduled(self::SALT_ROTATE_EVENT)) {
            return;
        }
        wp_schedule_event(time() + HOUR_IN_SECONDS, 'hourly', self::SALT_ROTATE_EVENT);
    }

    public function maybeRotateSalts(): void
    {
        if (! Settings::bool('security_rotate_salts_enabled', false)) {
            return;
        }

        $intervalHours = max(24, min(720, (int) Settings::get('security_rotate_salts_interval_hours', '168')));
        $lastRotate = (int) get_option(self::SALT_LAST_OPTION, 0);
        if ($lastRotate > 0 && (time() - $lastRotate) < ($intervalHours * HOUR_IN_SECONDS)) {
            return;
        }

        if ($this->rotateWpConfigSalts()) {
            update_option(self::SALT_LAST_OPTION, (string) time(), false);
        }
    }

    public function renderCommerceSslNotice(): void
    {
        if (! current_user_can('manage_options')) {
            return;
        }
        if (! $this->commerceSslRequired()) {
            return;
        }

        $scheme = strtolower((string) wp_parse_url(home_url('/'), PHP_URL_SCHEME));
        if ($scheme === 'https') {
            return;
        }

        printf(
            '<div class="notice notice-error"><p>%s</p></div>',
            esc_html__('Commerce module is enabled and SSL is required. Switch site URLs to HTTPS immediately.', 'help4-builder-suite')
        );
    }

    /**
     * @return array<int, string>
     */
    private function cloudflareCidrs(): array
    {
        $override = trim((string) Settings::get('security_cloudflare_cidrs', ''));
        if ($override !== '') {
            return $this->csvToCidrs($override);
        }

        return [
            '173.245.48.0/20',
            '103.21.244.0/22',
            '103.22.200.0/22',
            '103.31.4.0/22',
            '141.101.64.0/18',
            '108.162.192.0/18',
            '190.93.240.0/20',
            '188.114.96.0/20',
            '197.234.240.0/22',
            '198.41.128.0/17',
            '162.158.0.0/15',
            '104.16.0.0/13',
            '104.24.0.0/14',
            '172.64.0.0/13',
            '131.0.72.0/22',
            '2400:cb00::/32',
            '2606:4700::/32',
            '2803:f800::/32',
            '2405:b500::/32',
            '2405:8100::/32',
            '2a06:98c0::/29',
            '2c0f:f248::/32',
        ];
    }

    /**
     * @return array<int, string>
     */
    private function csvToCidrs(string $raw): array
    {
        if ($raw === '') {
            return [];
        }

        $parts = preg_split('/[\s,]+/', $raw) ?: [];
        $cidrs = [];
        foreach ($parts as $part) {
            $value = trim((string) $part);
            if ($value !== '') {
                $cidrs[] = $value;
            }
        }
        return array_values(array_unique($cidrs));
    }

    private function isSecureConnection(): bool
    {
        if (is_ssl()) {
            return true;
        }
        $forwarded = isset($_SERVER['HTTP_X_FORWARDED_PROTO']) ? strtolower(sanitize_text_field((string) wp_unslash($_SERVER['HTTP_X_FORWARDED_PROTO']))) : '';
        return $forwarded === 'https';
    }

    private function isCommerceFeatureEnabled(): bool
    {
        $flags = new FeatureFlags();
        return $flags->isEnabled('commerce');
    }

    private function commerceSslRequired(): bool
    {
        if (! $this->isCommerceFeatureEnabled()) {
            return false;
        }
        return Settings::bool('security_require_ssl_for_commerce', true);
    }

    private function syncCommerceSslForceSetting(): void
    {
        $settings = Settings::all();
        if (! empty($settings['ssl_force_https'])) {
            return;
        }

        $settings['ssl_force_https'] = '1';
        update_option(Settings::OPTION_KEY, $settings);
    }

    private function enforceWpConfigPermissions(): void
    {
        $path = $this->locateWpConfigPath();
        if ($path === '' || ! is_file($path)) {
            return;
        }
    }

    private function locateWpConfigPath(): string
    {
        $candidates = [
            ABSPATH . 'wp-config.php',
            dirname(ABSPATH) . '/wp-config.php',
        ];

        foreach ($candidates as $candidate) {
            if (is_file($candidate)) {
                return $candidate;
            }
        }

        return '';
    }

    private function rotateWpConfigSalts(): bool
    {
        $path = $this->locateWpConfigPath();
        if ($path === '' || ! is_file($path) || ! is_readable($path) || ! wp_is_writable($path)) {
            return false;
        }

        $contents = file_get_contents($path);
        if (! is_string($contents) || $contents === '') {
            return false;
        }

        $saltConstants = [
            'AUTH_KEY',
            'SECURE_AUTH_KEY',
            'LOGGED_IN_KEY',
            'NONCE_KEY',
            'AUTH_SALT',
            'SECURE_AUTH_SALT',
            'LOGGED_IN_SALT',
            'NONCE_SALT',
        ];

        $replaced = 0;
        foreach ($saltConstants as $constant) {
            $newSalt = wp_generate_password(64, true, true);
            $escaped = str_replace(['\\', "'"], ['\\\\', "\\'"], $newSalt);
            $pattern = "/define\\(\\s*['\\\"]" . preg_quote($constant, '/') . "['\\\"]\\s*,\\s*['\\\"][^'\\\"]*['\\\"]\\s*\\)\\s*;/";
            $replacement = "define('" . $constant . "', '" . $escaped . "');";
            $newContents = preg_replace($pattern, $replacement, $contents, 1, $count);
            if (is_string($newContents) && $count > 0) {
                $contents = $newContents;
                $replaced += $count;
            }
        }

        if ($replaced < 8) {
            return false;
        }

        $written = file_put_contents($path, $contents, LOCK_EX);
        if ($written === false) {
            return false;
        }

        $this->enforceWpConfigPermissions();
        return true;
    }

    /**
     * @param array<string, mixed> $post
     */
    private function saveSecuritySettings(array $post): void
    {
        $settings = Settings::all();

        $boolKeys = [
            'security_require_cloudflare_origin',
            'security_trust_cf_connecting_ip',
            'security_block_bad_queries',
            'ssl_force_https',
            'ssl_hsts_enabled',
            'security_require_ssl_for_commerce',
            'security_enable_headers',
            'security_csp_enabled',
            'security_disable_xmlrpc',
            'security_hide_wp_version',
            'security_enforce_wp_config_permissions',
            'security_rotate_salts_enabled',
        ];

        foreach ($boolKeys as $key) {
            $settings[$key] = ! empty($post[$key]) ? '1' : '0';
        }

        $settings['security_cloudflare_cidrs'] = sanitize_text_field((string) ($post['security_cloudflare_cidrs'] ?? ''));
        $settings['security_origin_bypass_cidrs'] = sanitize_text_field((string) ($post['security_origin_bypass_cidrs'] ?? ''));
        $settings['security_csp_policy'] = sanitize_text_field((string) ($post['security_csp_policy'] ?? "default-src 'self' https: data: blob: 'unsafe-inline' 'unsafe-eval'"));

        $settings['security_max_login_attempts'] = (string) max(3, min(25, (int) ($post['security_max_login_attempts'] ?? $settings['security_max_login_attempts'] ?? 6)));
        $settings['security_lock_minutes'] = (string) max(1, min(1440, (int) ($post['security_lock_minutes'] ?? $settings['security_lock_minutes'] ?? 15)));
        $settings['security_rotate_salts_interval_hours'] = (string) max(24, min(720, (int) ($post['security_rotate_salts_interval_hours'] ?? $settings['security_rotate_salts_interval_hours'] ?? 168)));

        update_option(Settings::OPTION_KEY, $settings);
    }

    private function redirectWithNotice(string $notice): void
    {
        $target = add_query_arg(
            [
                'page' => 'h4bs-security',
                'h4bs_notice' => sanitize_key($notice),
            ],
            admin_url('admin.php')
        );
        wp_safe_redirect($target);
        exit;
    }

    /**
     * @param array<string, mixed> $settings
     */
    private function boolField(string $key, string $label, array $settings): void
    {
        ?>
        <tr>
            <th scope="row"><?php echo esc_html($label); ?></th>
            <td><input type="checkbox" name="<?php echo esc_attr($key); ?>" value="1" <?php checked(! empty($settings[$key])); ?>></td>
        </tr>
        <?php
    }

    /**
     * @param array<string, mixed> $settings
     */
    private function textField(string $key, string $label, array $settings): void
    {
        ?>
        <tr>
            <th scope="row"><?php echo esc_html($label); ?></th>
            <td><input class="regular-text code" type="text" name="<?php echo esc_attr($key); ?>" value="<?php echo esc_attr((string) ($settings[$key] ?? '')); ?>"></td>
        </tr>
        <?php
    }

    /**
     * @param array<string, mixed> $settings
     */
    private function numberField(string $key, string $label, array $settings, int $min, int $max): void
    {
        ?>
        <tr>
            <th scope="row"><?php echo esc_html($label); ?></th>
            <td><input class="small-text" type="number" min="<?php echo esc_attr((string) $min); ?>" max="<?php echo esc_attr((string) $max); ?>" name="<?php echo esc_attr($key); ?>" value="<?php echo esc_attr((string) ($settings[$key] ?? (string) $min)); ?>"></td>
        </tr>
        <?php
    }
}
