<?php
/**
 * Settings tab for Polanger Firewall.
 *
 * @package Polanger_Dashboard_Tools
 */

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

/**
 * Renders and saves the Firewall settings tab.
 */
class PDT_Firewall_Page {

  /**
   * Save nonce action.
   */
  const NONCE_ACTION = 'pdt_firewall_save';

  /**
   * Settings service.
   *
   * @var PDT_Firewall_Settings
   */
  private $settings;

  /**
   * Logger service.
   *
   * @var PDT_Firewall_Logger
   */
  private $logger;

  /**
   * Diagnostics service.
   *
   * @var PDT_Firewall_Diagnostics|null
   */
  private $diagnostics;

  /**
   * Constructor.
   *
   * @param PDT_Firewall_Settings         $settings    Settings service.
   * @param PDT_Firewall_Logger           $logger      Logger service.
   * @param PDT_Firewall_Diagnostics|null $diagnostics Diagnostics service.
   */
  public function __construct( $settings, $logger, $diagnostics = null ) {
    $this->settings    = $settings;
    $this->logger      = $logger;
    $this->diagnostics = $diagnostics;
  }

  /**
   * Register hooks.
   *
   * @return void
   */
  public function register_hooks() {
    add_filter( 'pdt_settings_tabs', array( $this, 'register_tab' ) );
    add_filter( 'pdt_settings_tab_icons', array( $this, 'register_tab_icon' ) );
    add_action( 'pdt_settings_render_tab_firewall', array( $this, 'render_tab' ) );
    add_action( 'admin_init', array( $this, 'maybe_handle_save' ), 25 );
    add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
  }

  /**
   * Register the Firewall tab.
   *
   * @param array $tabs Existing tabs.
   * @return array
   */
  public function register_tab( $tabs ) {
    if ( ! is_array( $tabs ) ) {
      $tabs = array();
    }

    $new = array();

    foreach ( $tabs as $tab_id => $label ) {
      $new[ $tab_id ] = $label;

      if ( 'recaptcha' === $tab_id && ! isset( $new['firewall'] ) ) {
        $new['firewall'] = __( 'Firewall', 'polanger-admin-menu-manager' );
      }
    }

    if ( ! isset( $new['firewall'] ) ) {
      $new['firewall'] = __( 'Firewall', 'polanger-admin-menu-manager' );
    }

    return $new;
  }

  /**
   * Register tab icon.
   *
   * @param array $icons Existing icons.
   * @return array
   */
  public function register_tab_icon( $icons ) {
    if ( ! is_array( $icons ) ) {
      $icons = array();
    }

    $icons['firewall'] = 'dashicons-shield-alt';

    return $icons;
  }

  /**
   * Save settings.
   *
   * @return void
   */
  public function maybe_handle_save() {
    if ( ! $this->is_firewall_tab() ) {
      return;
    }

    if ( empty( $_POST['pdt_firewall_save'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
      return;
    }

    $can_manage = function_exists( 'pdt_current_user_can_manage' ) ? pdt_current_user_can_manage() : current_user_can( 'manage_options' );
    if ( ! $can_manage ) {
      return;
    }

    if ( ! isset( $_POST['pdt_firewall_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pdt_firewall_nonce'] ) ), self::NONCE_ACTION ) ) {
      return;
    }

    if ( ! empty( $_POST['pdt_firewall_event_retention_save'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
      $retention = isset( $_POST['pdt_firewall_event_retention'] ) ? sanitize_key( wp_unslash( $_POST['pdt_firewall_event_retention'] ) ) : '';
      $this->settings->save_event_retention( $retention );
      $this->logger->prune_saved_events();
      $state = 'event-retention-saved';
    } elseif ( ! empty( $_POST['pdt_firewall_clear_events'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
      $this->logger->clear();
      $state = 'events-cleared';
    } else {
      $raw = isset( $_POST['pdt_firewall'] ) && is_array( $_POST['pdt_firewall'] )
        ? wp_unslash( $_POST['pdt_firewall'] ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
        : array();
      $touched = isset( $_POST['pdt_firewall_touched'] ) && is_array( $_POST['pdt_firewall_touched'] )
        ? wp_unslash( $_POST['pdt_firewall_touched'] ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
        : array();

      $this->settings->save_settings( $raw, $touched );
      $state = 'saved';
    }

    wp_safe_redirect(
      add_query_arg(
        array(
          'page'               => 'polanger-settings',
          'tab'                => 'firewall',
          'pdt_firewall_state' => $state,
        ),
        admin_url( 'admin.php' )
      )
    );
    exit;
  }

  /**
   * Enqueue admin CSS.
   *
   * @return void
   */
  public function enqueue_assets() {
    if ( ! $this->is_firewall_tab() ) {
      return;
    }

    $css_version = file_exists( PDT_FIREWALL_DIR . 'assets/css/firewall-admin.css' ) ? PDT_FIREWALL_VERSION . '-' . filemtime( PDT_FIREWALL_DIR . 'assets/css/firewall-admin.css' ) : PDT_FIREWALL_VERSION;
    $js_version  = file_exists( PDT_FIREWALL_DIR . 'assets/js/firewall-admin.js' ) ? PDT_FIREWALL_VERSION . '-' . filemtime( PDT_FIREWALL_DIR . 'assets/js/firewall-admin.js' ) : PDT_FIREWALL_VERSION;

    wp_enqueue_style(
      'pdt-firewall-admin',
      PDT_FIREWALL_URL . 'assets/css/firewall-admin.css',
      array(),
      $css_version
    );

    wp_enqueue_script(
      'pdt-firewall-admin',
      PDT_FIREWALL_URL . 'assets/js/firewall-admin.js',
      array(),
      $js_version,
      true
    );

    wp_localize_script(
      'pdt-firewall-admin',
      'PDTFirewallAdmin',
      array(
        'modeProfiles' => $this->settings->get_mode_profiles(),
      )
    );
  }

  /**
   * Render tab body.
   *
   * @param bool $is_readonly Whether Settings page is read-only.
   * @return void
   */
  public function render_tab( $is_readonly = false ) {
    $settings = $this->settings->get_settings();
    $modes    = $this->settings->get_modes();
    $events   = $this->logger->get_events();
    $state    = isset( $_GET['pdt_firewall_state'] ) ? sanitize_key( wp_unslash( $_GET['pdt_firewall_state'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    $retention_choices = $this->settings->get_event_retention_choices();
    $event_retention   = isset( $settings['event_retention'] ) && isset( $retention_choices[ $settings['event_retention'] ] ) ? $settings['event_retention'] : 'monthly';
    ?>
    <div class="pdt-settings-section pdt-firewall-section">
      <div class="pdt-firewall-hero">
        <div>
          <span class="pdt-firewall-eyebrow"><?php esc_html_e( 'Request Firewall', 'polanger-admin-menu-manager' ); ?></span>
          <h2><?php esc_html_e( 'Polanger Firewall', 'polanger-admin-menu-manager' ); ?></h2>
          <p>
            <?php esc_html_e( 'A lightweight WordPress-aware firewall for auth abuse, XML-RPC exposure, anonymous REST pressure, common bot probes, and temporary IP cooldowns.', 'polanger-admin-menu-manager' ); ?>
          </p>
        </div>
        <div class="pdt-firewall-hero__status">
          <span class="<?php echo ! empty( $settings['enabled'] ) ? 'is-on' : 'is-off'; ?>">
            <?php echo ! empty( $settings['enabled'] ) ? esc_html__( 'Enabled', 'polanger-admin-menu-manager' ) : esc_html__( 'Disabled', 'polanger-admin-menu-manager' ); ?>
          </span>
          <strong>
            <?php
            $mode_label = isset( $modes[ $settings['mode'] ] ) ? $modes[ $settings['mode'] ]['label'] : $modes['balanced']['label'];
            echo esc_html( $mode_label );
            ?>
          </strong>
        </div>
      </div>

      <?php if ( 'saved' === $state ) : ?>
        <div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Firewall settings saved.', 'polanger-admin-menu-manager' ); ?></p></div>
      <?php elseif ( 'events-cleared' === $state ) : ?>
        <div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Firewall event history cleared.', 'polanger-admin-menu-manager' ); ?></p></div>
      <?php elseif ( 'event-retention-saved' === $state ) : ?>
        <div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Firewall event cleanup schedule saved.', 'polanger-admin-menu-manager' ); ?></p></div>
      <?php endif; ?>

      <div class="pdt-firewall-safe-note">
        <span class="dashicons dashicons-info-outline"></span>
        <p>
          <?php esc_html_e( 'Safety first: Monitor Only never blocks, allowlisted IPs always bypass, and logged-in administrators can bypass enforcement when admin bypass is enabled.', 'polanger-admin-menu-manager' ); ?>
        </p>
      </div>

      <form method="post" class="pdt-settings-form pdt-firewall-form">
        <?php wp_nonce_field( self::NONCE_ACTION, 'pdt_firewall_nonce' ); ?>
        <input type="hidden" name="pdt_firewall_save" value="1" />

        <div class="pdt-firewall-card">
          <div class="pdt-firewall-card__head">
            <div>
              <h3><?php esc_html_e( 'General Protection', 'polanger-admin-menu-manager' ); ?></h3>
              <p><?php esc_html_e( 'Turn the firewall on and choose how aggressively it should behave.', 'polanger-admin-menu-manager' ); ?></p>
            </div>
          </div>

          <label class="pdt-firewall-toggle">
            <input type="checkbox" name="pdt_firewall[enabled]" value="1" <?php checked( ! empty( $settings['enabled'] ) ); ?> <?php disabled( $is_readonly ); ?> />
            <span></span>
            <strong><?php esc_html_e( 'Enable Polanger Firewall', 'polanger-admin-menu-manager' ); ?></strong>
          </label>

          <div class="pdt-firewall-mode-grid">
            <?php foreach ( $modes as $mode_key => $mode ) : ?>
              <label class="pdt-firewall-mode-card">
                <input type="radio" name="pdt_firewall[mode]" value="<?php echo esc_attr( $mode_key ); ?>" <?php checked( $settings['mode'], $mode_key ); ?> <?php disabled( $is_readonly ); ?> />
                <span>
                  <strong><?php echo esc_html( $mode['label'] ); ?></strong>
                  <small><?php echo esc_html( $mode['description'] ); ?></small>
                </span>
              </label>
            <?php endforeach; ?>
          </div>
        </div>

        <div class="pdt-firewall-grid">
          <div class="pdt-firewall-card">
            <h3><?php esc_html_e( 'Core Guards', 'polanger-admin-menu-manager' ); ?></h3>
            <?php
            $this->render_checkbox( 'auth_rate_limit_enabled', __( 'Auth rate limiting', 'polanger-admin-menu-manager' ), __( 'Protects native WordPress login, registration, and lost-password requests.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'xmlrpc_protection_enabled', __( 'XML-RPC protection', 'polanger-admin-menu-manager' ), __( 'Limits legacy XML-RPC attack surface such as authenticated methods and multicall abuse.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'rest_protection_enabled', __( 'REST API hardening', 'polanger-admin-menu-manager' ), __( 'Keeps REST enabled while reducing anonymous write attempts, user listing, and request floods.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'probe_protection_enabled', __( 'Bot and probe blocking', 'polanger-admin-menu-manager' ), __( 'Blocks common scans for .env, wp-config, backup archives, shells, phpMyAdmin, and traversal attempts.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'request_scoring_enabled', __( 'Request scoring engine', 'polanger-admin-menu-manager' ), __( 'Combines suspicious signals such as bad user agents, injection patterns, long URLs, and sensitive paths before deciding.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'user_enumeration_guard_enabled', __( 'User enumeration guard', 'polanger-admin-menu-manager' ), __( 'Reduces author ID and REST user listing probes without touching normal logins.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'app_passwords_guard_enabled', __( 'Application Password guard', 'polanger-admin-menu-manager' ), __( 'Controls WordPress Application Password API access without interfering with normal browser login or 2FA.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'security_headers_enabled', __( 'Security response headers', 'polanger-admin-menu-manager' ), __( 'Adds browser-side hardening headers chosen by the selected firewall mode.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            ?>
          </div>

          <div class="pdt-firewall-card">
            <h3><?php esc_html_e( 'Lockout Safety', 'polanger-admin-menu-manager' ); ?></h3>
            <?php
            $this->render_checkbox( 'admin_bypass', __( 'Allow logged-in administrators to bypass', 'polanger-admin-menu-manager' ), __( 'Recommended. Prevents accidental admin lockout while you tune the firewall.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'temp_block_enabled', __( 'Temporary IP cooldowns', 'polanger-admin-menu-manager' ), __( 'Repeated probe hits can temporarily block the IP without permanently denying access.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'log_events', __( 'Log important firewall events', 'polanger-admin-menu-manager' ), __( 'Stores a small rolling event buffer only when something important happens.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            ?>
          </div>
        </div>

        <details class="pdt-firewall-card pdt-firewall-details">
          <summary>
            <span>
              <strong><?php esc_html_e( 'Optional Rate Limit Tuning', 'polanger-admin-menu-manager' ); ?></strong>
              <small><?php esc_html_e( 'Mode presets already choose safe limits. Open this only when a site needs custom thresholds.', 'polanger-admin-menu-manager' ); ?></small>
            </span>
            <span class="dashicons dashicons-arrow-down-alt2"></span>
          </summary>
          <p><?php esc_html_e( 'These limits are intentionally simple. Login identity limits are tied to the current IP to reduce account lockout abuse.', 'polanger-admin-menu-manager' ); ?></p>
          <div class="pdt-firewall-number-grid">
            <?php
            $this->render_number( 'login_max_attempts', __( 'Login attempts', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 1, 50 );
            $this->render_number( 'login_ip_max_attempts', __( 'IP attempts', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 1, 200 );
            $this->render_number( 'lostpassword_max_attempts', __( 'Password reset attempts', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 1, 50 );
            $this->render_number( 'register_max_attempts', __( 'Registration attempts', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 1, 50 );
            $this->render_number( 'auth_window_minutes', __( 'Auth window minutes', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 1, 1440 );
            $this->render_number( 'rest_rate_limit', __( 'Anonymous REST requests', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 10, 2000 );
            $this->render_number( 'rest_rate_window_minutes', __( 'REST window minutes', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 1, 1440 );
            $this->render_number( 'probe_strikes', __( 'Probe strikes before cooldown', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 1, 50 );
            $this->render_number( 'probe_window_minutes', __( 'Probe window minutes', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 1, 1440 );
            $this->render_number( 'temp_block_minutes', __( 'Temporary block minutes', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 1, 1440 );
            ?>
          </div>
        </details>

        <div class="pdt-firewall-grid">
          <details class="pdt-firewall-card pdt-firewall-details">
            <summary>
              <span>
                <strong><?php esc_html_e( 'Optional REST and XML-RPC Details', 'polanger-admin-menu-manager' ); ?></strong>
                <small><?php esc_html_e( 'Balanced keeps compatibility high; Strict enables stronger anonymous REST restrictions.', 'polanger-admin-menu-manager' ); ?></small>
              </span>
              <span class="dashicons dashicons-arrow-down-alt2"></span>
            </summary>
            <?php
            $this->render_checkbox( 'rest_block_anonymous_writes', __( 'Block anonymous REST write requests', 'polanger-admin-menu-manager' ), __( 'Blocks unauthenticated POST, PUT, PATCH, and DELETE REST requests.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'rest_block_user_enumeration', __( 'Block anonymous REST user listing', 'polanger-admin-menu-manager' ), __( 'Prevents anonymous requests from listing /wp/v2/users.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'xmlrpc_disable_auth', __( 'Disable XML-RPC authenticated methods', 'polanger-admin-menu-manager' ), __( 'Keeps XML-RPC safer without promising to remove every pingback-related method.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'xmlrpc_disable_multicall', __( 'Disable XML-RPC multicall', 'polanger-admin-menu-manager' ), __( 'Reduces password guessing and request amplification through system.multicall.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            $this->render_checkbox( 'xmlrpc_disable_pingbacks', __( 'Disable XML-RPC pingbacks', 'polanger-admin-menu-manager' ), __( 'Optional. Enable only if your site does not rely on pingbacks.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            ?>
            <label>
              <strong><?php esc_html_e( 'Allowed anonymous REST write route prefixes', 'polanger-admin-menu-manager' ); ?></strong>
              <textarea name="pdt_firewall[rest_write_route_allowlist]" rows="4" <?php disabled( $is_readonly ); ?>><?php echo esc_textarea( $settings['rest_write_route_allowlist'] ); ?></textarea>
            </label>
            <p class="description"><?php esc_html_e( 'One route prefix per line. Useful for safe public endpoints such as WooCommerce cart APIs or contact form submissions.', 'polanger-admin-menu-manager' ); ?></p>
          </details>

          <details class="pdt-firewall-card pdt-firewall-details">
            <summary>
              <span>
                <strong><?php esc_html_e( 'Optional IP Rules', 'polanger-admin-menu-manager' ); ?></strong>
                <small><?php esc_html_e( 'Use allowlists, denylists, and trusted proxy headers only when the site needs explicit IP handling.', 'polanger-admin-menu-manager' ); ?></small>
              </span>
              <span class="dashicons dashicons-arrow-down-alt2"></span>
            </summary>
            <label>
              <strong><?php esc_html_e( 'Allowlist IPs / CIDR', 'polanger-admin-menu-manager' ); ?></strong>
              <textarea name="pdt_firewall[allowlist_ips]" rows="5" <?php disabled( $is_readonly ); ?>><?php echo esc_textarea( $settings['allowlist_ips'] ); ?></textarea>
            </label>
            <p class="description"><?php esc_html_e( 'One IP or CIDR per line. Allowlisted IPs bypass firewall enforcement.', 'polanger-admin-menu-manager' ); ?></p>

            <label>
              <strong><?php esc_html_e( 'Denylist IPs / CIDR', 'polanger-admin-menu-manager' ); ?></strong>
              <textarea name="pdt_firewall[denylist_ips]" rows="5" <?php disabled( $is_readonly ); ?>><?php echo esc_textarea( $settings['denylist_ips'] ); ?></textarea>
            </label>
            <p class="description"><?php esc_html_e( 'Use carefully. Denylisted IPs are blocked unless the firewall is in Monitor Only mode.', 'polanger-admin-menu-manager' ); ?></p>

            <?php
            $this->render_checkbox( 'trusted_proxy_headers_enabled', __( 'Use trusted proxy client IP headers', 'polanger-admin-menu-manager' ), __( 'Only enable this when your site is behind Cloudflare, a load balancer, or a known reverse proxy.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
            ?>
            <label>
              <strong><?php esc_html_e( 'Trusted proxy header', 'polanger-admin-menu-manager' ); ?></strong>
              <select name="pdt_firewall[proxy_ip_header]" <?php disabled( $is_readonly ); ?>>
                <?php foreach ( $this->settings->get_proxy_header_choices() as $header_key => $header_label ) : ?>
                  <option value="<?php echo esc_attr( $header_key ); ?>" <?php selected( $settings['proxy_ip_header'], $header_key ); ?>><?php echo esc_html( $header_label ); ?></option>
                <?php endforeach; ?>
              </select>
            </label>
            <p class="description"><?php esc_html_e( 'The header is trusted only when the direct REMOTE_ADDR matches a trusted proxy IP below.', 'polanger-admin-menu-manager' ); ?></p>

            <label>
              <strong><?php esc_html_e( 'Trusted proxy IPs / CIDR', 'polanger-admin-menu-manager' ); ?></strong>
              <textarea name="pdt_firewall[trusted_proxy_ips]" rows="4" <?php disabled( $is_readonly ); ?>><?php echo esc_textarea( $settings['trusted_proxy_ips'] ); ?></textarea>
            </label>
            <p class="description"><?php esc_html_e( 'Add only proxy or load balancer IPs here, never visitor IPs. Empty means proxy headers are ignored.', 'polanger-admin-menu-manager' ); ?></p>
          </details>
        </div>

        <details class="pdt-firewall-card pdt-firewall-details">
          <summary>
            <span>
              <strong><?php esc_html_e( 'Optional Probe Detection Tuning', 'polanger-admin-menu-manager' ); ?></strong>
              <small><?php esc_html_e( 'The selected mode already sets these values. Open this only to tune unusual traffic patterns.', 'polanger-admin-menu-manager' ); ?></small>
            </span>
            <span class="dashicons dashicons-arrow-down-alt2"></span>
          </summary>
          <p><?php esc_html_e( 'These limits catch unusually long URLs and query strings before they reach expensive WordPress screens.', 'polanger-admin-menu-manager' ); ?></p>
          <div class="pdt-firewall-number-grid">
            <?php
            $this->render_number( 'max_url_length', __( 'Maximum URL length', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 256, 10000 );
            $this->render_number( 'max_query_length', __( 'Maximum query length', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 128, 10000 );
            ?>
          </div>
        </details>

        <details class="pdt-firewall-card pdt-firewall-details">
          <summary>
            <span>
              <strong><?php esc_html_e( 'Optional Advanced Mode Details', 'polanger-admin-menu-manager' ); ?></strong>
              <small><?php esc_html_e( 'Most sites can ignore this section. These controls are here for special compatibility and hardening needs.', 'polanger-admin-menu-manager' ); ?></small>
            </span>
            <span class="dashicons dashicons-arrow-down-alt2"></span>
          </summary>
          <p><?php esc_html_e( 'Most sites can ignore this section. Monitor Only, Balanced, and Strict set these automatically; change them only when a site needs special handling.', 'polanger-admin-menu-manager' ); ?></p>
          <div class="pdt-firewall-grid">
            <div>
              <?php
              $this->render_checkbox( 'security_headers_nosniff', __( 'Send nosniff header', 'polanger-admin-menu-manager' ), __( 'Helps browsers avoid MIME-type confusion.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
              $this->render_checkbox( 'security_headers_frame_options', __( 'Send frame protection header', 'polanger-admin-menu-manager' ), __( 'Reduces clickjacking risk by allowing same-origin framing only.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
              $this->render_checkbox( 'security_headers_referrer', __( 'Send referrer policy header', 'polanger-admin-menu-manager' ), __( 'Limits how much URL information is shared through referrers.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
              $this->render_checkbox( 'security_headers_permissions', __( 'Send permissions policy header', 'polanger-admin-menu-manager' ), __( 'Disables unused browser features such as camera, microphone, and geolocation by default.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
              $this->render_checkbox( 'security_headers_csp_frame', __( 'Send CSP frame-ancestors header', 'polanger-admin-menu-manager' ), __( 'Strict mode can add frame-ancestors self for stronger clickjacking protection.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
              ?>
            </div>
            <div>
              <?php
              $this->render_checkbox( 'app_passwords_log_usage', __( 'Log Application Password usage', 'polanger-admin-menu-manager' ), __( 'Records successful and failed Application Password API authentication events.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
              $this->render_checkbox( 'app_passwords_admin_only', __( 'Limit Application Passwords to administrators', 'polanger-admin-menu-manager' ), __( 'Strict mode enables this to reduce API credential exposure for lower-privilege accounts.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
              $this->render_checkbox( 'user_enumeration_author_query', __( 'Block numeric author ID probes', 'polanger-admin-menu-manager' ), __( 'Blocks requests such as ?author=1 that try to discover usernames.', 'polanger-admin-menu-manager' ), $settings, $is_readonly );
              ?>
              <div class="pdt-firewall-number-grid">
                <?php
                $this->render_number( 'request_score_threshold', __( 'Request score threshold', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 1, 20 );
                $this->render_number( 'request_score_temp_threshold', __( 'Temporary block score', 'polanger-admin-menu-manager' ), $settings, $is_readonly, 1, 30 );
                ?>
              </div>
            </div>
          </div>
        </details>

        <p class="pdt-firewall-submit">
          <button type="submit" class="button button-primary" <?php disabled( $is_readonly ); ?>>
            <?php esc_html_e( 'Save Firewall Settings', 'polanger-admin-menu-manager' ); ?>
          </button>
        </p>
      </form>

      <?php
      if ( $this->diagnostics && method_exists( $this->diagnostics, 'render_card' ) ) {
        $this->diagnostics->render_card( $is_readonly );
      }
      ?>

      <div class="pdt-firewall-card pdt-firewall-events">
        <div class="pdt-firewall-card__head">
          <div>
            <h3><?php esc_html_e( 'Recent Firewall Events', 'polanger-admin-menu-manager' ); ?></h3>
            <p><?php esc_html_e( 'A small rolling buffer of blocked or monitored events. This is not a full request log.', 'polanger-admin-menu-manager' ); ?></p>
          </div>
          <div class="pdt-firewall-events__actions">
            <form method="post">
              <?php wp_nonce_field( self::NONCE_ACTION, 'pdt_firewall_nonce' ); ?>
              <input type="hidden" name="pdt_firewall_save" value="1" />
              <input type="hidden" name="pdt_firewall_clear_events" value="1" />
              <button type="submit" class="button button-secondary" <?php disabled( $is_readonly || empty( $events ) ); ?>>
                <?php esc_html_e( 'Clear Events', 'polanger-admin-menu-manager' ); ?>
              </button>
            </form>
            <button
              type="button"
              class="button button-secondary pdt-firewall-icon-button"
              data-pdt-firewall-modal-open="pdt-firewall-event-retention-modal"
              <?php disabled( $is_readonly ); ?>
            >
              <span class="dashicons dashicons-admin-generic" aria-hidden="true"></span>
              <span class="screen-reader-text"><?php esc_html_e( 'Event cleanup settings', 'polanger-admin-menu-manager' ); ?></span>
            </button>
          </div>
        </div>

        <?php if ( empty( $events ) ) : ?>
          <p class="pdt-firewall-empty"><?php esc_html_e( 'No firewall events recorded yet.', 'polanger-admin-menu-manager' ); ?></p>
        <?php else : ?>
          <div class="pdt-firewall-table-wrap">
            <table class="widefat striped">
              <thead>
                <tr>
                  <th><?php esc_html_e( 'Time', 'polanger-admin-menu-manager' ); ?></th>
                  <th><?php esc_html_e( 'Decision', 'polanger-admin-menu-manager' ); ?></th>
                  <th><?php esc_html_e( 'Reason', 'polanger-admin-menu-manager' ); ?></th>
                  <th><?php esc_html_e( 'IP', 'polanger-admin-menu-manager' ); ?></th>
                  <th><?php esc_html_e( 'Request', 'polanger-admin-menu-manager' ); ?></th>
                </tr>
              </thead>
              <tbody>
                <?php foreach ( $events as $event_index => $event ) : ?>
                  <tr class="<?php echo $event_index >= 20 ? 'pdt-firewall-event-row is-hidden' : 'pdt-firewall-event-row'; ?>">
                    <td><?php echo esc_html( $event['time'] ); ?></td>
                    <td><span class="pdt-firewall-pill"><?php echo esc_html( $event['decision'] ); ?></span></td>
                    <td><?php echo esc_html( $event['reason'] ); ?></td>
                    <td><?php echo esc_html( $event['ip'] ); ?></td>
                    <td>
                      <code><?php echo esc_html( $event['method'] ); ?></code>
                      <?php echo esc_html( wp_html_excerpt( $event['uri'], 120, '...' ) ); ?>
                    </td>
                  </tr>
                <?php endforeach; ?>
              </tbody>
            </table>
          </div>
          <div class="pdt-firewall-events__load">
            <button
              type="button"
              class="button button-secondary pdt-firewall-load-more"
              data-batch-size="20"
              data-label-more="<?php esc_attr_e( 'Load More', 'polanger-admin-menu-manager' ); ?>"
              data-label-done="<?php esc_attr_e( 'No more events', 'polanger-admin-menu-manager' ); ?>"
              <?php disabled( count( $events ) <= 20 ); ?>
            >
              <?php echo count( $events ) > 20 ? esc_html__( 'Load More', 'polanger-admin-menu-manager' ) : esc_html__( 'No more events', 'polanger-admin-menu-manager' ); ?>
            </button>
          </div>
        <?php endif; ?>
      </div>

      <div class="pdt-firewall-modal" id="pdt-firewall-event-retention-modal" hidden>
        <div class="pdt-firewall-modal__backdrop" data-pdt-firewall-modal-close></div>
        <div
          class="pdt-firewall-modal__dialog"
          role="dialog"
          aria-modal="true"
          aria-labelledby="pdt-firewall-event-retention-title"
        >
          <form method="post">
            <?php wp_nonce_field( self::NONCE_ACTION, 'pdt_firewall_nonce' ); ?>
            <input type="hidden" name="pdt_firewall_save" value="1" />
            <input type="hidden" name="pdt_firewall_event_retention_save" value="1" />

            <div class="pdt-firewall-modal__head">
              <div>
                <span class="pdt-firewall-modal__eyebrow"><?php esc_html_e( 'Event Cleanup', 'polanger-admin-menu-manager' ); ?></span>
                <h3 id="pdt-firewall-event-retention-title"><?php esc_html_e( 'Automatic firewall log cleanup', 'polanger-admin-menu-manager' ); ?></h3>
                <p><?php esc_html_e( 'Choose how long recent firewall events should be kept. Older entries are cleaned automatically when events are recorded or this screen is opened.', 'polanger-admin-menu-manager' ); ?></p>
              </div>
              <button type="button" class="button-link pdt-firewall-modal__close" data-pdt-firewall-modal-close>
                <span class="dashicons dashicons-no-alt" aria-hidden="true"></span>
                <span class="screen-reader-text"><?php esc_html_e( 'Close', 'polanger-admin-menu-manager' ); ?></span>
              </button>
            </div>

            <div class="pdt-firewall-retention-grid">
              <?php foreach ( $retention_choices as $retention_key => $retention_choice ) : ?>
                <label class="pdt-firewall-retention-option">
                  <input type="radio" name="pdt_firewall_event_retention" value="<?php echo esc_attr( $retention_key ); ?>" <?php checked( $event_retention, $retention_key ); ?> />
                  <span>
                    <strong><?php echo esc_html( $retention_choice['label'] ); ?></strong>
                    <small><?php echo esc_html( $retention_choice['description'] ); ?></small>
                  </span>
                </label>
              <?php endforeach; ?>
            </div>

            <div class="pdt-firewall-modal__actions">
              <button type="button" class="button button-secondary" data-pdt-firewall-modal-close>
                <?php esc_html_e( 'Cancel', 'polanger-admin-menu-manager' ); ?>
              </button>
              <button type="submit" class="button button-primary">
                <?php esc_html_e( 'Save Cleanup Schedule', 'polanger-admin-menu-manager' ); ?>
              </button>
            </div>
          </form>
        </div>
      </div>
    </div>
    <?php
  }

  /**
   * Render checkbox row.
   *
   * @param string $key         Setting key.
   * @param string $label       Label.
   * @param string $description Description.
   * @param array  $settings    Settings.
   * @param bool   $is_readonly Read-only flag.
   * @return void
   */
  private function render_checkbox( $key, $label, $description, $settings, $is_readonly ) {
    ?>
    <label class="pdt-firewall-check">
      <input type="checkbox" name="pdt_firewall[<?php echo esc_attr( $key ); ?>]" value="1" <?php checked( ! empty( $settings[ $key ] ) ); ?> <?php disabled( $is_readonly ); ?> />
      <span>
        <strong><?php echo esc_html( $label ); ?></strong>
        <small><?php echo esc_html( $description ); ?></small>
      </span>
    </label>
    <?php
  }

  /**
   * Render number field.
   *
   * @param string $key         Setting key.
   * @param string $label       Label.
   * @param array  $settings    Settings.
   * @param bool   $is_readonly Read-only flag.
   * @param int    $min         Min.
   * @param int    $max         Max.
   * @return void
   */
  private function render_number( $key, $label, $settings, $is_readonly, $min, $max ) {
    ?>
    <label>
      <span><?php echo esc_html( $label ); ?></span>
      <input
        type="number"
        min="<?php echo esc_attr( $min ); ?>"
        max="<?php echo esc_attr( $max ); ?>"
        name="pdt_firewall[<?php echo esc_attr( $key ); ?>]"
        value="<?php echo esc_attr( isset( $settings[ $key ] ) ? $settings[ $key ] : '' ); ?>"
        <?php disabled( $is_readonly ); ?>
      />
    </label>
    <?php
  }

  /**
   * Whether current admin request is the Firewall tab.
   *
   * @return bool
   */
  private function is_firewall_tab() {
    if ( ! is_admin() ) {
      return false;
    }

    $page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    $tab  = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

    return 'polanger-settings' === $page && 'firewall' === $tab;
  }
}
