<?php

declare(strict_types=1);

namespace WPUltimateSecurity\UltimateSecurity\Modules\Setup;

use WPUltimateSecurity\UltimateSecurity\Modules\Base\AbstractModule;
use WPUltimateSecurity\UltimateSecurity\Support\Helpers\Utils;
use WPUltimateSecurity\UltimateSecurity\Modules\Setup\SecurityProfiles;

/**
 * Security Wizard Module
 *
 * Handles the security wizard functionality including:
 * - Security scanning
 * - Profile application
 * - Undo capability
 * - Wizard state management
 */
class SecurityWizard extends AbstractModule
{
    public const MODULE_ID = 'system.security_wizard';

    protected const VERSION = '2.0.0';

    /**
     * Get unique module identifier.
     */
    public static function getId(): string
    {
        return self::MODULE_ID;
    }

    /**
     * Get human-readable module name.
     */
    public static function getName(): string
    {
        return __('Security Wizard', 'ultimate-security');
    }

    /**
     * Get module domain.
     */
    public static function getDomain(): string
    {
        return 'system';
    }
    /**
     * Option keys for wizard state
     */
    private const OPTION_WIZARD_COMPLETED = 'ultimate_security_wizard_completed';
    private const OPTION_WIZARD_PROFILE = 'ultimate_security_wizard_profile';
    private const OPTION_WIZARD_CHANGES = 'ultimate_security_wizard_changes';
    private const OPTION_WIZARD_TIMESTAMP = 'ultimate_security_wizard_timestamp';

    /**
     * Boot the module after registration.
     */
    public static function boot(): void
    {
        add_action('admin_notices', [self::class, 'maybeShowWizardNotice']);
        add_action('wp_ajax_ultimate_security_dismiss_wizard', [self::class, 'handleDismissWizard']);
    }

    /**
     * Handle AJAX request to dismiss wizard notice
     */
    public static function handleDismissWizard(): void
    {
        check_ajax_referer('dismiss_wizard');

        if (!current_user_can('manage_options')) {
            wp_die('Unauthorized', 403);
        }

        update_option('ultimate_security_wizard_dismissed', true);
        wp_send_json_success();
    }

    /**
     * Show wizard notice for first-time users
     */
    public static function maybeShowWizardNotice(): void
    {
        // Only show on plugin admin pages
        $screen = get_current_screen();
        if (!$screen || strpos($screen->id, 'ultimate-security') === false) {
            return;
        }

        // Don't show if wizard already completed or dismissed
        if (self::isWizardCompleted() || get_option('ultimate_security_wizard_dismissed', false)) {
            return;
        }

        // Check if this is a new installation (no custom settings saved)
        $options = Utils::ultimate_security_get_option();
        if (!empty($options) && isset($options['_wizard_skipped'])) {
            return;
        }

        echo '<div class="notice notice-info is-dismissible ultimate-security-wizard-notice">';
        echo '<p><strong>' . esc_html__('Welcome to Ultimate Security!', 'ultimate-security') . '</strong></p>';
        echo '<p>' . esc_html__('Get started quickly with our Security Wizard. It will scan your site and recommend the best security settings.', 'ultimate-security') . '</p>';
        echo '<p>';
        echo '<a href="' . esc_url(admin_url('admin.php?page=ultimate-security#/wizard')) . '" class="button button-primary">';
        echo esc_html__('Launch Security Wizard', 'ultimate-security');
        echo '</a> ';
        echo '<a href="#" class="button" id="ultimate-security-dismiss-wizard">';
        echo esc_html__('Skip for now', 'ultimate-security');
        echo '</a>';
        echo '</p>';
        echo '</div>';

        // Add inline script to handle dismiss
        echo "<script>
            document.getElementById('ultimate-security-dismiss-wizard')?.addEventListener('click', function(e) {
                e.preventDefault();
                fetch(ajaxurl + '?action=ultimate_security_dismiss_wizard&_wpnonce=' + '" . wp_create_nonce('dismiss_wizard') . "');
                this.closest('.notice').remove();
            });
        </script>";
    }

    /**
     * Check if wizard has been completed
     *
     * @return bool
     */
    public static function isWizardCompleted(): bool
    {
        return (bool) get_option(self::OPTION_WIZARD_COMPLETED, false);
    }

    /**
     * Get the wizard status
     *
     * @return array
     */
    public static function getWizardStatus(): array
    {
        return [
            'completed' => self::isWizardCompleted(),
            'profile' => get_option(self::OPTION_WIZARD_PROFILE, null),
            'timestamp' => get_option(self::OPTION_WIZARD_TIMESTAMP, null),
            'can_undo' => self::canUndo(),
        ];
    }

    /**
     * Run a quick security scan
     *
     * @return array Scan results
     */
    public static function runSecurityScan(): array
    {
        $results = [
            'score' => 0,
            'max_score' => 100,
            'checks' => [],
            'summary' => [
                'passed' => 0,
                'warnings' => 0,
                'critical' => 0,
            ],
        ];

        // Define security checks
        $checks = [
            self::checkSSL(),
            self::checkWordPressVersion(),
            self::checkDebugMode(),
            self::checkFileEditing(),
            self::checkPasswordStrength(),
            self::checkLoginProtection(),
            self::checkTwoFactor(),
            // REMOVED: self::checkSecurityHeaders() - now server-level feature
            self::checkFilePermissions(),
            self::checkDatabasePrefix(),
        ];

        foreach ($checks as $check) {
            $results['checks'][] = $check;
            $results['score'] += $check['points'];

            switch ($check['status']) {
                case 'passed':
                    $results['summary']['passed']++;
                    break;
                case 'warning':
                    $results['summary']['warnings']++;
                    break;
                case 'critical':
                    $results['summary']['critical']++;
                    break;
            }
        }

        // Calculate percentage
        $results['percentage'] = round(($results['score'] / $results['max_score']) * 100);
        $results['grade'] = self::calculateGrade($results['percentage']);

        return $results;
    }

    /**
     * Apply a security profile
     *
     * @param string $profileLevel Profile level to apply
     * @return array Result of the operation
     */
    public static function applyProfile(string $profileLevel): array
    {
        $profile = SecurityProfiles::getProfile($profileLevel);
        if (!$profile) {
            return [
                'success' => false,
                'message' => __('Invalid profile level specified.', 'ultimate-security'),
            ];
        }

        // Get current options to store for undo
        $currentOptions = Utils::ultimate_security_get_option();

        // Store the current state for undo capability
        $changesToStore = self::extractChangedSettings($currentOptions, $profile);

        // Merge profile settings with current options
        $newOptions = array_replace_recursive($currentOptions, $profile);
        $newOptions['security_level'] = sanitize_text_field(wp_unslash($profileLevel));
        // Save the new options
        Utils::ultimate_security_update_option($newOptions);
        $updated = true;

        if ($updated) {
            // Store wizard state
            update_option(self::OPTION_WIZARD_COMPLETED, true);
            update_option(self::OPTION_WIZARD_PROFILE, $profileLevel);
            update_option(self::OPTION_WIZARD_CHANGES, $changesToStore);
            update_option(self::OPTION_WIZARD_TIMESTAMP, current_time('mysql'));

            $profileMeta = SecurityProfiles::getProfileMeta();
            $profileName = $profileMeta[$profileLevel]['name'] ?? $profileLevel;

            return [
                'success' => true,
                'message' => sprintf(
                    /* translators: %s: profile name */
                    __('%s profile has been applied successfully.', 'ultimate-security'),
                    $profileName
                ),
                'profile' => $profileLevel,
                'changes_count' => count($changesToStore),
            ];
        }

        return [
            'success' => false,
            'message' => __('Failed to save settings. Please try again.', 'ultimate-security'),
        ];
    }

    /**
     * Undo wizard changes
     *
     * @return array Result of the operation
     */
    public static function undoChanges(): array
    {
        $storedChanges = get_option(self::OPTION_WIZARD_CHANGES, []);

        if (empty($storedChanges)) {
            return [
                'success' => false,
                'message' => __('No changes to undo.', 'ultimate-security'),
            ];
        }

        $currentOptions = Utils::ultimate_security_get_option();

        // Restore previous values
        foreach ($storedChanges as $change) {
            $keys = explode('.', $change['key']);
            self::setNestedValue($currentOptions, $keys, $change['old_value']);
        }

        Utils::ultimate_security_update_option($currentOptions);
        $updated = true;

        if ($updated) {
            // Clear wizard state
            delete_option(self::OPTION_WIZARD_CHANGES);
            update_option(self::OPTION_WIZARD_COMPLETED, false);
            delete_option(self::OPTION_WIZARD_PROFILE);

            return [
                'success' => true,
                'message' => __('Wizard changes have been reverted successfully.', 'ultimate-security'),
                'restored_count' => count($storedChanges),
            ];
        }

        return [
            'success' => false,
            'message' => __('Failed to revert changes. Please try again.', 'ultimate-security'),
        ];
    }

    /**
     * Check if undo is available
     *
     * @return bool
     */
    public static function canUndo(): bool
    {
        $changes = get_option(self::OPTION_WIZARD_CHANGES, []);
        return !empty($changes);
    }

    /**
     * Get profile comparison with current settings
     *
     * @param string $profileLevel Profile level to compare
     * @return array Comparison data
     */
    public static function getProfileComparison(string $profileLevel): array
    {
        $profile = SecurityProfiles::getProfile($profileLevel);
        if (!$profile) {
            return [];
        }

        $currentOptions = Utils::ultimate_security_get_option();
        $comparison = [];

        self::compareSettings($currentOptions, $profile, '', $comparison);

        return $comparison;
    }

    /**
     * Mark wizard as completed without applying a profile
     */
    public static function skipWizard(): void
    {
        update_option('ultimate_security_wizard_dismissed', true);
    }

    // ==================== Private Helper Methods ====================

    /**
     * Calculate grade from percentage
     *
     * @param float|int $percentage Score percentage
     * @return string Grade (A-F)
     */
    private static function calculateGrade($percentage): string
    {
        if ($percentage >= 90) return 'A';
        if ($percentage >= 80) return 'B';
        if ($percentage >= 70) return 'C';
        if ($percentage >= 60) return 'D';
        return 'F';
    }

    /**
     * Extract settings that will be changed
     *
     * @param array $current Current settings
     * @param array $new New settings from profile
     * @return array List of changes
     */
    private static function extractChangedSettings(array $current, array $new, string $prefix = ''): array
    {
        $changes = [];

        foreach ($new as $key => $value) {
            $fullKey = $prefix ? "{$prefix}.{$key}" : $key;

            if (is_array($value) && !self::isLeafArray($value)) {
                $currentNested = isset($current[$key]) && is_array($current[$key]) ? $current[$key] : [];
                $nestedChanges = self::extractChangedSettings($currentNested, $value, $fullKey);
                $changes = array_merge($changes, $nestedChanges);
            } else {
                $oldValue = $current[$key] ?? null;
                if ($oldValue !== $value) {
                    $changes[] = [
                        'key' => $fullKey,
                        'old_value' => $oldValue,
                        'new_value' => $value,
                    ];
                }
            }
        }

        return $changes;
    }

    /**
     * Check if array is a leaf value (list of values vs nested settings)
     *
     * @param array $value Value to check
     * @return bool
     */
    private static function isLeafArray(array $value): bool
    {
        foreach (array_keys($value) as $key) {
            if (!is_numeric($key)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Set a nested value in an array using dot notation keys
     *
     * @param array $array Array to modify
     * @param array $keys Key path
     * @param mixed $value Value to set
     */
    private static function setNestedValue(array &$array, array $keys, $value): void
    {
        $key = array_shift($keys);

        if (empty($keys)) {
            if ($value === null) {
                unset($array[$key]);
            } else {
                $array[$key] = $value;
            }
        } else {
            if (!isset($array[$key]) || !is_array($array[$key])) {
                $array[$key] = [];
            }
            self::setNestedValue($array[$key], $keys, $value);
        }
    }

    /**
     * Compare current settings with profile settings
     *
     * @param array $current Current settings
     * @param array $profile Profile settings
     * @param string $prefix Key prefix
     * @param array $comparison Comparison results
     */
    private static function compareSettings(array $current, array $profile, string $prefix, array &$comparison): void
    {
        foreach ($profile as $key => $value) {
            $fullKey = $prefix ? "{$prefix}.{$key}" : $key;

            if (is_array($value) && !self::isLeafArray($value)) {
                $currentNested = isset($current[$key]) && is_array($current[$key]) ? $current[$key] : [];
                self::compareSettings($currentNested, $value, $fullKey, $comparison);
            } else {
                $currentValue = $current[$key] ?? null;
                $comparison[] = [
                    'key' => $fullKey,
                    'label' => self::getSettingLabel($fullKey),
                    'current' => $currentValue,
                    'new' => $value,
                    'will_change' => $currentValue !== $value,
                ];
            }
        }
    }

    /**
     * Get human-readable label for a setting key
     *
     * @param string $key Setting key
     * @return string
     */
    private static function getSettingLabel(string $key): string
    {
        $labels = [
            'security.password_policies.activate_password_policies' => __('Password Policies', 'ultimate-security'),
            'security.password_policies.min_length' => __('Minimum Password Length', 'ultimate-security'),
            'security.password_policies.ui_rules_mix_case' => __('Require Mixed Case', 'ultimate-security'),
            'security.password_policies.ui_rules_numeric' => __('Require Numbers', 'ultimate-security'),
            'security.password_policies.ui_rules_special_chars' => __('Require Special Characters', 'ultimate-security'),
            'security.login_limit.enable' => __('Login Attempt Limits', 'ultimate-security'),
            'security.login_limit.login_attempts' => __('Max Login Attempts', 'ultimate-security'),
            'security.login_limit.lockout_duration' => __('Lockout Duration (minutes)', 'ultimate-security'),
            'security.2fa_authentication.email_authentication.2fa_email_verification_enable' => __('Email 2FA', 'ultimate-security'),
            'security.2fa_authentication.application_authentication.2fa_application_authentication_enable' => __('App-based 2FA', 'ultimate-security'),
            'security.recommendations.hide_wordpress_version' => __('Hide WordPress Version', 'ultimate-security'),
            'security.recommendations.disable_file_editors' => __('Disable File Editors', 'ultimate-security'),
            'security.url_guard.overview.enable' => __('URL Guard', 'ultimate-security'),
            'security_headers.enable' => __('Security Headers', 'ultimate-security'),
            'rate_limiter.rate_limiting_enabled' => __('API Rate Limiting', 'ultimate-security'),
        ];

        return $labels[$key] ?? ucwords(str_replace(['_', '.'], ' ', basename($key)));
    }

    // ==================== Security Check Methods ====================

    /**
     * Check SSL certificate
     *
     * @return array
     */
    private static function checkSSL(): array
    {
        $isSSL = is_ssl();
        return [
            'id' => 'ssl',
            'name' => __('SSL/HTTPS', 'ultimate-security'),
            'description' => $isSSL
                ? __('Your site is using a secure HTTPS connection.', 'ultimate-security')
                : __('Your site is not using HTTPS. This is critical for security.', 'ultimate-security'),
            'status' => $isSSL ? 'passed' : 'critical',
            'points' => $isSSL ? 15 : 0,
            'max_points' => 15,
        ];
    }

    /**
     * Check WordPress version
     *
     * @return array
     */
    private static function checkWordPressVersion(): array
    {
        global $wp_version;
        require_once ABSPATH . 'wp-admin/includes/update.php';

        $updates = get_core_updates();
        $isUpToDate = empty($updates) || (isset($updates[0]->response) && $updates[0]->response === 'latest');

        return [
            'id' => 'wp_version',
            'name' => __('WordPress Version', 'ultimate-security'),
            'description' => $isUpToDate
                ? sprintf(__('WordPress %s is up to date.', 'ultimate-security'), $wp_version)
                : __('A newer version of WordPress is available.', 'ultimate-security'),
            'status' => $isUpToDate ? 'passed' : 'warning',
            'points' => $isUpToDate ? 10 : 5,
            'max_points' => 10,
        ];
    }

    /**
     * Check debug mode
     *
     * @return array
     */
    private static function checkDebugMode(): array
    {
        $debugEnabled = defined('WP_DEBUG') && WP_DEBUG;
        $debugDisplayEnabled = defined('WP_DEBUG_DISPLAY') && WP_DEBUG_DISPLAY;

        $passed = !$debugEnabled || !$debugDisplayEnabled;

        return [
            'id' => 'debug_mode',
            'name' => __('Debug Mode', 'ultimate-security'),
            'description' => $passed
                ? __('Debug mode is properly configured.', 'ultimate-security')
                : __('Debug mode is enabled with display. This can expose sensitive information.', 'ultimate-security'),
            'status' => $passed ? 'passed' : 'warning',
            'points' => $passed ? 5 : 0,
            'max_points' => 5,
        ];
    }

    /**
     * Check file editing
     *
     * @return array
     */
    private static function checkFileEditing(): array
    {
        $disabled = defined('DISALLOW_FILE_EDIT') && DISALLOW_FILE_EDIT;

        return [
            'id' => 'file_editing',
            'name' => __('File Editing', 'ultimate-security'),
            'description' => $disabled
                ? __('Theme and plugin file editing is disabled.', 'ultimate-security')
                : __('File editing is enabled. Consider disabling it for security.', 'ultimate-security'),
            'status' => $disabled ? 'passed' : 'warning',
            'points' => $disabled ? 10 : 5,
            'max_points' => 10,
        ];
    }

    /**
     * Check password strength settings
     *
     * @return array
     */
    private static function checkPasswordStrength(): array
    {
        $options = Utils::ultimate_security_get_option();
        $policies = $options['security']['password_policies'] ?? [];

        $enabled = ($policies['activate_password_policies'] ?? '0') === '1';
        $minLength = (int) ($policies['min_length'] ?? 0);
        $hasMixCase = ($policies['ui_rules_mix_case'] ?? '0') === '1';
        $hasNumeric = ($policies['ui_rules_numeric'] ?? '0') === '1';

        $score = 0;
        if ($enabled) $score += 3;
        if ($minLength >= 12) $score += 4;
        elseif ($minLength >= 8) $score += 2;
        if ($hasMixCase) $score += 2;
        if ($hasNumeric) $score += 1;

        $maxScore = 10;
        $percentage = ($score / $maxScore) * 100;

        return [
            'id' => 'password_strength',
            'name' => __('Password Policies', 'ultimate-security'),
            'description' => $percentage >= 70
                ? __('Strong password policies are in place.', 'ultimate-security')
                : __('Password policies could be strengthened.', 'ultimate-security'),
            'status' => $percentage >= 70 ? 'passed' : ($percentage >= 40 ? 'warning' : 'critical'),
            'points' => $score,
            'max_points' => $maxScore,
        ];
    }

    /**
     * Check login protection
     *
     * @return array
     */
    private static function checkLoginProtection(): array
    {
        $options = Utils::ultimate_security_get_option();
        $loginLimit = $options['security']['login_limit'] ?? [];

        $enabled = ($loginLimit['enable'] ?? '0') === '1';
        $attempts = (int) ($loginLimit['login_attempts'] ?? 5);

        $passed = $enabled && $attempts <= 5;

        return [
            'id' => 'login_protection',
            'name' => __('Brute Force Protection', 'ultimate-security'),
            'description' => $passed
                ? __('Login attempt limits are properly configured.', 'ultimate-security')
                : __('Consider enabling login attempt limits to prevent brute force attacks.', 'ultimate-security'),
            'status' => $passed ? 'passed' : 'warning',
            'points' => $passed ? 15 : 5,
            'max_points' => 15,
        ];
    }

    /**
     * Check two-factor authentication
     *
     * @return array
     */
    private static function checkTwoFactor(): array
    {
        $options = Utils::ultimate_security_get_option();
        $twoFA = $options['security']['2fa_authentication'] ?? [];

        $emailEnabled = ($twoFA['email_authentication']['2fa_email_verification_enable'] ?? '0') === '1';
        $appEnabled = ($twoFA['application_authentication']['2fa_application_authentication_enable'] ?? '0') === '1';

        $enabled = $emailEnabled || $appEnabled;

        return [
            'id' => 'two_factor',
            'name' => __('Two-Factor Authentication', 'ultimate-security'),
            'description' => $enabled
                ? __('Two-factor authentication is enabled.', 'ultimate-security')
                : __('Enable two-factor authentication for enhanced account security.', 'ultimate-security'),
            'status' => $enabled ? 'passed' : 'warning',
            'points' => $enabled ? 15 : 0,
            'max_points' => 15,
        ];
    }

    // REMOVED: checkSecurityHeaders() - Security headers are now a server-level feature
    // See docs/SERVER_LEVEL_FEATURES.md for implementation guidance

    /**
     * Check file permissions
     *
     * @return array
     */
    private static function checkFilePermissions(): array
    {
        $wpConfigPerms = fileperms(ABSPATH . 'wp-config.php') & 0777;
        $htaccessPerms = file_exists(ABSPATH . '.htaccess')
            ? fileperms(ABSPATH . '.htaccess') & 0777
            : null;

        // wp-config.php should be 600 or 640 or 644 (not world-writable)
        $wpConfigOk = ($wpConfigPerms & 0002) === 0; // Not world-writable
        $htaccessOk = $htaccessPerms === null || ($htaccessPerms & 0002) === 0;

        $passed = $wpConfigOk && $htaccessOk;

        return [
            'id' => 'file_permissions',
            'name' => __('File Permissions', 'ultimate-security'),
            'description' => $passed
                ? __('Critical file permissions are secure.', 'ultimate-security')
                : __('Some file permissions may be too permissive.', 'ultimate-security'),
            'status' => $passed ? 'passed' : 'warning',
            'points' => $passed ? 5 : 2,
            'max_points' => 5,
        ];
    }

    /**
     * Check database prefix
     *
     * @return array
     */
    private static function checkDatabasePrefix(): array
    {
        global $wpdb;
        $isDefault = $wpdb->prefix === 'wp_';

        return [
            'id' => 'db_prefix',
            'name' => __('Database Prefix', 'ultimate-security'),
            'description' => !$isDefault
                ? __('Custom database prefix is being used.', 'ultimate-security')
                : __('Default "wp_" database prefix detected. Consider using a custom prefix.', 'ultimate-security'),
            'status' => !$isDefault ? 'passed' : 'warning',
            'points' => !$isDefault ? 5 : 2,
            'max_points' => 5,
        ];
    }
}
