<?php
/**
 * Nginx Hardening Admin Notice
 *
 * Detects publicly accessible cache directories on Nginx servers
 * and guides administrators to properly secure them.
 *
 * @package Mamba\Modules\Caching\Admin
 * @since   1.0.0
 */

namespace Mamba\Modules\Caching\Admin;

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

/**
 * Class NginxHardening
 *
 * Detects publicly accessible cache directory on Nginx and guides admins to harden.
 * Notice only shown for manage_options capability and when likely Nginx.
 *
 * @since 1.0.0
 */
final class NginxHardening {
    private const TRANSIENT_KEY = 'mamba_nginx_public_cache';
    private const ACK_OPTION = 'mamba_nginx_hardening_ack';

    public function register(): void {
        add_action('admin_init', [$this, 'adminInit']);
        add_action('admin_notices', [$this, 'maybeShowNotice']);
    }

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

        // Handle "Mark as done" acknowledgment
        if (!empty($_POST['mamba_nginx_hardening_ack']) && isset($_POST['_wpnonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_wpnonce'])), 'mamba_nginx_hardening_ack')) {
            update_option(self::ACK_OPTION, 1);
            // Redirect to avoid resubmission
            wp_safe_redirect(remove_query_arg([]));
            exit;
        }

        // Skip detection if acked
        if ((int) get_option(self::ACK_OPTION, 0) === 1) return;

        // Only detect for likely Nginx servers
        if (!$this->isLikelyNginx()) return;

        // Respect transient (12h)
        $cached = get_transient(self::TRANSIENT_KEY);
        if ($cached !== false) return;

        // Ensure marker exists
        $this->ensureMarkerFile();

        // Perform local loopback check
        $isPublic = $this->checkPublicAccess();
        
        // Cache result for 12 hours
        set_transient(self::TRANSIENT_KEY, [
            'is_public' => $isPublic === true,
            'time' => time(),
        ], 12 * HOUR_IN_SECONDS);
    }

    public function maybeShowNotice(): void {
        if (!current_user_can('manage_options')) return;
        if ((int) get_option(self::ACK_OPTION, 0) === 1) return;
        if (!$this->isLikelyNginx()) return;
        
        // Only show on Mamba dashboard tab
        $currentTab = isset($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : 'dashboard';
        if (!isset($_GET['page']) || sanitize_key(wp_unslash($_GET['page'])) !== 'mamba' || $currentTab !== 'dashboard') return;

        $cached = get_transient(self::TRANSIENT_KEY);
        if (!is_array($cached) || empty($cached['is_public'])) return;

        $snippet = "location ^~ /wp-content/cache/mamba/ { deny all; return 403; }";
        ?>
        <div class="notice notice-warning is-dismissible" style="background: linear-gradient(135deg, #fffbeb 0%, #fed7aa 100%); border: 1px solid #fbbf24; border-left: 4px solid #E68057; border-radius: 8px; padding: 16px 20px; margin: 15px 0; box-shadow: 0 2px 8px rgba(230, 128, 87, 0.1); animation: successSlideIn 0.3s ease-out;">
            <div style="display: flex; align-items: flex-start; gap: 12px;">
                <div style="flex-shrink: 0; width: 20px; height: 20px; background: linear-gradient(135deg, #E68057 0%, #E68057 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-top: 1px;">
                    <span style="color: white; font-size: 12px; font-weight: bold;">!</span>
                </div>
                
                <div style="flex: 1; min-width: 0;">
                    <div style="margin-bottom: 12px;">
                        <span style="color: #92400e; font-size: 11px; font-weight: 500;">We detected Nginx server. To secure your cache directory, add this to your nginx.conf:</span>
                    </div>
                    
                    <div style="background: #ffffff; border: 1px solid #fbbf24; border-radius: 6px; padding: 12px; margin-bottom: 12px; position: relative; font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace; font-size: 11px; line-height: 1.4; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);">
                        <code id="mamba-nginx-snippet-code" style="color: #1e293b; display: block; padding-right: 45px; line-height: 1.6;"><?php echo esc_html($snippet); ?></code>
                        <button type="button" class="button button-small" data-mamba-copy="mamba-nginx-snippet-code" style="position: absolute; top: 50%; transform: translateY(-50%); right: 8px; font-size: 9px; padding: 2px 6px; height: 18px; line-height: 1; background: #E68057; border-color: #E68057; color: white; border-radius: 3px; font-weight: 500;">
                            <span class="copy-text">Copy</span>
                            <span class="copied-text" style="display: none;">✓ Copied</span>
                        </button>
                    </div>
                    
                    <div style="display: flex; align-items: center; gap: 12px;">
                        <form method="post" style="margin: 0;">
                            <?php wp_nonce_field('mamba_nginx_hardening_ack'); ?>
                            <input type="hidden" name="mamba_nginx_hardening_ack" value="1" />
                            <button type="submit" class="button button-primary" style="font-size: 11px; padding: 6px 12px; height: 26px; line-height: 1; background: #E68057; border-color: #E68057; color: white; border-radius: 4px; font-weight: 500;">
                                Mark as Resolved
                            </button>
                        </form>
                        <span style="color: #92400e; font-size: 10px; font-weight: 500;">
                            After adding, restart Nginx to apply changes. Ask your hosting provider for help if you are unsure
                        </span>
                    </div>
                </div>
            </div>
        </div>
        <?php
    }

    private function isLikelyNginx(): bool {
        $serverSoftware = isset($_SERVER['SERVER_SOFTWARE']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_SOFTWARE'])) : '';
        if (stripos($serverSoftware, 'nginx') !== false) return true;
        // Heuristic: presence of X-Accel headers suggests Nginx
        if (!empty($_SERVER['HTTP_X_ACCEL_REDIRECT']) || !empty($_SERVER['HTTP_X_ACCEL_EXPIRES'])) return true;
        // Conservative default to avoid false positives
        return false;
    }

    private function ensureMarkerFile(): void {
        $dir = WP_CONTENT_DIR . '/cache/mamba';
        if (!is_dir($dir)) {
            wp_mkdir_p($dir);
        }
        $file = $dir . '/.mamba-ping';
        if (!file_exists($file)) {
            @file_put_contents($file, (string) time());
        }
    }

    private function checkPublicAccess(): ?bool {
        $url = content_url('cache/mamba/.mamba-ping');
        // Add cache-buster to avoid cached 200s
        $url = add_query_arg(['mamba_check' => '1', 'ts' => (string) time()], $url);
        $args = [
            'timeout' => 5,
            'redirection' => 0,
            'headers' => [ 'Accept' => 'text/plain' ],
        ];
        $resp = wp_remote_head($url, $args);
        if (is_wp_error($resp) || (int) wp_remote_retrieve_response_code($resp) === 405) {
            // Fallback to GET if HEAD not allowed
            $resp = wp_remote_get($url, $args);
        }
        if (is_wp_error($resp)) return null;
        $code = (int) wp_remote_retrieve_response_code($resp);
        if ($code === 200) return true; // publicly readable
        if (in_array($code, [401, 403, 404], true)) return false;
        return null;
    }
}


