<?php

declare(strict_types=1);

namespace SiteFort\Modules\Firewall\ExtendedProtection;

use SiteFort\Includes\DataDirectoryProtection;
use SiteFort\Includes\FilesystemMutation;
use SiteFort\Includes\SiteFortKeyManager;
use SiteFort\Modules\Firewall\Blocklist\BlocklistService;
use SiteFort\Modules\Firewall\FirewallConfigService;
use SiteFort\Modules\Firewall\FirewallSettings;
use SiteFort\Modules\Firewall\IpBlocking;
use SiteFort\Modules\Firewall\SafeNetwork\SafeNetworkRanges;
use SiteFort\Modules\Firewall\SearchTrust\SearchTrustScopeBuilder;

// If this file is called directly, abort.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

if ( ! class_exists( WafRuntimeStore::class, false ) ) {
    require_once __DIR__ . '/WafRuntimeStore.php';
}

class ConfigWriter {
    public static function write( ?array $settings = null, bool $refresh_bootstrap = true ): bool {
        if ( $refresh_bootstrap && is_file( Manager::get_bootstrap_path() ) ) {
            Manager::ensure_bootstrap_current();
        }

        $config = self::build_config( $settings, false, true );
        $policy_signature = (string) ( $config['policy_signature'] ?? '' );
        $runtime_storage_backend = (string) ( $config['runtime_storage_backend'] ?? 'filesystem' );

        $mysqli_runtime = ( $runtime_storage_backend === 'mysqli' );
        $managed_state_written = false;
        if ( $mysqli_runtime ) {
            $managed_state_written = WafRuntimeStore::put( WafRuntimeStore::KEY_BOOTSTRAP_CONFIG, $config );
        } else {
            WafRuntimeStore::delete( WafRuntimeStore::KEY_BOOTSTRAP_CONFIG );
            WafRuntimeStore::delete( WafRuntimeStore::KEY_BLOCKLIST_META );
        }

        $data_dir    = Manager::get_data_dir();
        $config_file = $data_dir . '/' . 'waf-config.php';

        if ( ! DataDirectoryProtection::ensure_runtime_root() ) {
            return self::finish_write( false, $managed_state_written, $policy_signature );
        }

        $content  = "<?php\n";
        $content .= "// Auto-generated by SiteFort. Do not edit.\n";
        $content .= "// Last updated: " . gmdate( 'Y-m-d H:i:s' ) . " UTC\n";
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export -- Pre-WordPress bootstrap consumes this generated PHP array directly.
        $content .= "return " . var_export( $config, true ) . ";\n";

        $temp      = $config_file . '.tmp.' . FilesystemMutation::temporary_suffix();
        $file_mode = DataDirectoryProtection::get_runtime_file_mode();

        if ( ! FilesystemMutation::write_bytes_to_file( $temp, $content, $file_mode ) ) {
            return self::finish_write( false, $managed_state_written, $policy_signature );
        }

        if ( ! FilesystemMutation::apply_file_permissions( $temp, $file_mode ) ) {
            FilesystemMutation::delete_path( $temp );
            return self::finish_write( false, $managed_state_written, $policy_signature );
        }

        if ( ! FilesystemMutation::move_path( $temp, $config_file ) ) {
            FilesystemMutation::delete_path( $temp );
            return self::finish_write( false, $managed_state_written, $policy_signature );
        }

        if ( ! FilesystemMutation::verify_written_contents( $config_file, $content ) ) {
            return self::finish_write( false, $managed_state_written, $policy_signature );
        }

        return self::finish_write( true, $managed_state_written, $policy_signature );
    }

    /**
     * @return array<string,mixed>
     */
    public static function build_config( ?array $settings = null, bool $runtime_context = false, bool $refresh_managed_runtime_artifacts = true ): array {
        global $wpdb;

        $settings = is_array( $settings )
            ? FirewallSettings::sanitize( $settings )
            : ( $runtime_context ? FirewallConfigService::load_runtime_effective_settings() : FirewallConfigService::load_effective_settings() );
        $trusted_proxies = \SiteFort\Modules\Firewall\RuleEngine::normalize_ip_entries(
            \SiteFort\Includes\IpDetector::resolve_trusted_proxies( $settings ),
            false
        );
        $cloudflare_ranges = [];
        if ( class_exists( '\SiteFort\Includes\ProxyPresets' ) ) {
            $cloudflare_ranges = \SiteFort\Includes\ProxyPresets::get_ranges( \SiteFort\Includes\ProxyPresets::PRESET_CLOUDFLARE );
        }

        $ip_blocking = new IpBlocking( $settings );
        $policy = FirewallSettings::compile_policy_from_sanitized(
            $settings,
            [
                'allowlisted_ips' => $ip_blocking->get_allowlisted_entries(),
                'blocked_ips' => $ip_blocking->get_active_banned_entries(),
            ]
        );
        $search_exception_scope = SearchTrustScopeBuilder::build_from_wordpress();
        $runtime_storage_backend = Manager::get_runtime_storage_backend();
        if ( $runtime_storage_backend === 'mysqli' && $refresh_managed_runtime_artifacts ) {
            BlocklistService::ensure_managed_runtime_artifacts_current();
        }
        $verification_secret = SiteFortKeyManager::get_server_verification_secret();

        $config_payload = [
            'engine_file' => trailingslashit( SITEFORT_PLUGIN_DIR ) . 'modules/Firewall/RuleEngine.php',
            'search_trust_core_file' => trailingslashit( SITEFORT_PLUGIN_DIR ) . 'modules/Firewall/SearchTrust/SearchTrustCore.php',
            'search_trust_context_file' => trailingslashit( SITEFORT_PLUGIN_DIR ) . 'modules/Firewall/SearchTrust/SearchTrustContext.php',
            'search_trust_dns_verifier_file' => trailingslashit( SITEFORT_PLUGIN_DIR ) . 'modules/Firewall/SearchTrust/SearchTrustDnsVerifier.php',
            'request_provenance_file' => trailingslashit( SITEFORT_PLUGIN_DIR ) . 'includes/RequestProvenance.php',
            'runtime_storage_backend' => $runtime_storage_backend,
            'db_table_prefix' => isset( $wpdb->prefix ) ? (string) $wpdb->prefix : '',
            'blog_id' => function_exists( 'get_current_blog_id' ) ? (int) get_current_blog_id() : 1,
            'origin_enforcement_mode' => FirewallConfigService::get_origin_enforcement_mode( $settings ),
            'policy' => [
                'allowlisted_ips' => array_values(
                    array_unique(
                        array_merge(
                            (array) $policy['allowlisted_ips'],
                            \SiteFort\Modules\Firewall\FirewallBypass::get_infrastructure_ips(),
                            // Keeps a recently-active admin from being locked out at the WAF layer.
                            \SiteFort\Modules\Firewall\FirewallBypass::get_admin_recovery_ips()
                        )
                    )
                ),
                'safe_service_ranges' => $policy['safe_service_ranges'],
                'trusted_user_agents' => $policy['trusted_user_agents'],
                'blocked_ips' => $policy['blocked_ips'],
                'blocked_user_agents' => $policy['blocked_user_agents'],
                'bot_policy_enabled' => $policy['bot_policy_enabled'],
                'bot_policy_profile' => $policy['bot_policy_profile'],
                'block_ai_training_crawlers' => $policy['block_ai_training_crawlers'],
                'rate_limit_enabled' => $policy['rate_limit_enabled'],
                'rate_limit_requests' => max( 1, (int) ( $settings['rate_limit_requests'] ?? 60 ) ),
                // Sensitive paths are enforced in-WP so probes count toward the
                // ban threshold; resulting bans land in blocked_ips above.
                'country_mode' => $policy['country_mode'],
                'country_codes' => $policy['country_codes'],
            ],
            'community_blocklist' => [
                'enabled' => ! empty( $settings['blocklist_enabled'] ),
                'artifact_signature' => BlocklistService::get_waf_artifact_signature(),
            ],
            'search_exception' => $search_exception_scope,
            'verified_bot_ranges' => SafeNetworkRanges::get_search_engine_ranges_for_config(),
            'ip_detection'    => [
                'mode'            => sanitize_text_field( $settings['proxy_detection'] ?? 'automatic' ),
                'header'          => sanitize_text_field( $settings['proxy_header'] ?? '' ),
                'trusted_proxies' => $trusted_proxies,
                'cloudflare_ranges' => \SiteFort\Modules\Firewall\RuleEngine::normalize_ip_entries( $cloudflare_ranges, false ),
            ],
            'internal_verification' => [
                'version' => '1',
                'window'  => 120,
                'secret'  => $verification_secret,
                'nonce_site_token' => hash( 'sha256', 'waf-verify|' . $verification_secret ),
            ],
            'lockout_message' => sanitize_text_field( $settings['lockout_message'] ?? 'Access denied.' ),
        ];
        $policy_signature = hash( 'sha256', (string) wp_json_encode( $config_payload ) );

        return $config_payload + [
            'policy_signature' => $policy_signature,
            'updated_at'       => time(),
        ];
    }

    private static function finish_write( bool $filesystem_written, bool $managed_state_written, string $policy_signature ): bool {
        if ( ! $filesystem_written && ! $managed_state_written ) {
            DataDirectoryProtection::record_runtime_failure(
                'waf_config_write_failed',
                Manager::get_data_dir() . '/waf-config.php'
            );
            return false;
        }

        DataDirectoryProtection::record_runtime_ok();
        update_option( 'sitefort_waf_policy_signature', $policy_signature, false );

        return true;
    }
}
