<?php

namespace WPUltimateSecurity\UltimateSecurity\Presentation\API\Controllers;

use WPUltimateSecurity\UltimateSecurity\Support\Helpers\ApiKeyCryptoService;
use WPUltimateSecurity\UltimateSecurity\Support\Helpers\Utils;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WPUltimateSecurity\UltimateSecurity\Infrastructure\Settings\SettingsRepository;

/**
 * Options Controller
 *
 * Handles all options-related API endpoints.
 * Uses SettingsRepository for centralized settings access with caching.
 *
 * API key encryption is handled by ApiKeyCryptoService:
 * - Encryption happens in update_options() before saving to database
 * - Decryption happens in SettingsRepository when loading settings
 */
class OptionsController
{
    /**
     * Get plugin options
     *
     * Uses SettingsRepository for cached access with automatic fallback to defaults.
     *
     * @param WP_REST_Request $request
     * @return WP_REST_Response|WP_Error
     */
    public static function get_options(WP_REST_Request $request)
    {
        $settings = SettingsRepository::getInstance();

        // Check if section parameter is provided
        $section = $request->get_param('section');

        if ($section) {
            // Use SettingsRepository's dot notation support to get the section
            // The repository automatically merges with defaults
            $result = $settings->get($section);

            if ($result === null) {
                // Section not found in settings or defaults
                return new WP_Error(
                    'section_not_found',
                    sprintf(
                        /* translators: %s: section name */
                        __('Section %s not found in options', 'ultimate-security'),
                        $section
                    ),
                    ['status' => 404]
                );
            }


            $output = $result;

            return new WP_REST_Response($output, 200);
        }

        $output = $settings->all();
        return new WP_REST_Response($output, 200);
    }

    /**
     * Update plugin options
     *
     * Uses SettingsRepository for cache management while preserving
     * the existing deep merge logic for backward compatibility.
     *
     * @param WP_REST_Request $request
     * @return WP_REST_Response
     */
    public static function update_options(WP_REST_Request $request)
    {
        $settings = SettingsRepository::getInstance();

        // Get current options from the repository (includes defaults merged)
        // We use raw() to get only what's stored in database for merging
        $current_options = $settings->raw();

        // Get options data from request and sanitize
        $updated_options = map_deep(wp_unslash($request->get_param('ultimate_security_options')), 'wp_kses_post');

        if ($updated_options) {
            // Merge the updated options with current options
            $merged_options = self::deep_merge_options($current_options, $updated_options);

            // Encrypt API keys before saving to database
            $merged_options = ApiKeyCryptoService::encrypt($merged_options);

            // Save the updated options back to the database
            Utils::ultimate_security_update_option($merged_options);

            // Refresh the SettingsRepository cache so subsequent reads get fresh data
            $settings->refresh();

            // Log settings change if EventLogger is available (Pro feature)
            self::log_settings_change($updated_options);

            // Fire action hook for settings update
            do_action('ultimate_security_options_updated', $updated_options);

            $response = [
                'success' => true,
                'msg' => esc_html__('Settings saved successfully.', 'ultimate-security'),
                'updated_options' => $request->get_param('ultimate_security_options')
            ];
        } else {
            $response = [
                'success' => false,
                'msg' => esc_html__('No data received.', 'ultimate-security')
            ];
        }

        $status_code = !empty($response['success']) ? 200 : 400;
        return new WP_REST_Response($response, $status_code);
    }

    /**
     * Deep merge options arrays
     *
     * Recursively merges update values into original, with special handling for:
     * - Empty arrays (replace completely)
     * - Sequential/indexed arrays (replace completely)
     * - Associative arrays (merge recursively)
     *
     * @param array $original The original options array
     * @param array $update   The updated options to merge in
     * @return array The merged options array
     */
    private static function deep_merge_options(array $original, array $update): array
    {
        foreach ($update as $key => $value) {
            // Special case for empty arrays - they should replace existing arrays
            if (is_array($value) && empty($value)) {
                $original[$key] = $value;
                continue;
            }

            // If the value is an array and the key exists in original and is also an array
            if (is_array($value) && isset($original[$key]) && is_array($original[$key])) {
                // Check if this is a sequential/indexed array (like a tag list) rather than an associative array
                if (array_keys($value) === range(0, count($value) - 1)) {
                    // For indexed arrays (like tag lists), replace completely instead of merging
                    $original[$key] = $value;
                } else {
                    // For associative arrays (nested settings), continue recursive merge
                    $original[$key] = self::deep_merge_options($original[$key], $value);
                }
            } else {
                // Otherwise just assign the new value
                $original[$key] = $value;
            }
        }

        return $original;
    }

    /**
     * Save recovery URL
     *
     * Uses SettingsRepository with dot notation for clean nested key access.
     *
     * @param WP_REST_Request $request
     * @return WP_REST_Response|WP_Error
     */
    public static function save_recovery_url(WP_REST_Request $request)
    {
        $recovery_url = sanitize_url($request->get_param('recovery_url'));
        $nonce = $request->get_param('_wpnonce');

        if (!isset($nonce) || !wp_verify_nonce($nonce, 'ultimate_security_nonce')) {
            return new WP_Error('invalid_nonce', __('Invalid nonce', 'ultimate-security'), array('status' => 403));
        }

        if (empty($recovery_url)) {
            return new WP_Error('empty_url', __('Recovery URL cannot be empty.', 'ultimate-security'), array('status' => 400));
        }

        // Use SettingsRepository with dot notation to update the recovery_url
        $settings = SettingsRepository::getInstance();
        $updated = $settings->set('security.login_limit.recovery_url', $recovery_url);

        if ($updated) {
            return rest_ensure_response(array(
                'success' => true,
                'message' => __('Recovery URL saved successfully.', 'ultimate-security'),
                'recovery_url' => $recovery_url
            ));
        } else {
            return new WP_Error('save_failed', __('Failed to save recovery URL.', 'ultimate-security'), array('status' => 500));
        }
    }

    /**
     * Get encryption database keys setting
     *
     * @param WP_REST_Request $request
     * @return mixed
     */
    public static function get_encryption_database_keys(WP_REST_Request $request)
    {
        $ultimate_security_enable_encryption_database_keys = get_option('ultimate-security-enable-encryption-database-keys', 0);
        return $ultimate_security_enable_encryption_database_keys;
    }

    /**
     * Log settings change to EventLogger (Pro feature)
     *
     * @param array $updated_options The options that were updated
     */
    private static function log_settings_change($updated_options)
    {
        // Check if Pro EventLogger is available
        if (!class_exists('WPUltimateSecurity\UltimateSecurityPro\Modules\Monitoring\EventLogger')) {
            return;
        }

        // Extract the section names from the updated options
        $sections = self::extract_setting_sections($updated_options);

        if (empty($sections)) {
            return;
        }

        // Create a human-readable message
        $message = sprintf(
            __('Security settings updated: %s', 'ultimate-security'),
            implode(', ', $sections)
        );

        // Log to EventLogger
        \WPUltimateSecurity\UltimateSecurityPro\Modules\Monitoring\EventLogger::log_event(
            'settings_change',
            $message,
            [
                'sections' => $sections,
                'settings_count' => count($sections),
            ],
            'medium'
        );
    }

    /**
     * Extract section names from updated options
     *
     * @param array $options
     * @param string $prefix
     * @return array
     */
    private static function extract_setting_sections($options, $prefix = '')
    {
        $sections = [];

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

            if (is_array($value) && !empty($value)) {
                // Check if this looks like a settings section (has nested arrays)
                $has_nested = false;
                foreach ($value as $v) {
                    if (is_array($v)) {
                        $has_nested = true;
                        break;
                    }
                }

                if ($has_nested) {
                    // Recurse into nested sections
                    $nested = self::extract_setting_sections($value, $full_key);
                    $sections = array_merge($sections, $nested);
                } else {
                    // This is a leaf section
                    $sections[] = self::format_section_name($full_key);
                }
            } else {
                // Single value change
                $sections[] = self::format_section_name($full_key);
            }
        }

        // Remove duplicates and return top-level sections only for readability
        $top_sections = [];
        foreach ($sections as $section) {
            $parts = explode(' > ', $section);
            $top = $parts[0];
            if (!in_array($top, $top_sections)) {
                $top_sections[] = $top;
            }
        }

        return $top_sections;
    }

    /**
     * Format section name for display
     *
     * @param string $key
     * @return string
     */
    private static function format_section_name($key)
    {
        // Map internal keys to human-readable names
        $key_map = [
            'security' => __('Security', 'ultimate-security'),
            'login' => __('Login', 'ultimate-security'),
            'login_limit' => __('Login Limits', 'ultimate-security'),
            'captcha' => __('CAPTCHA', 'ultimate-security'),
            'two_factor' => __('Two-Factor Auth', 'ultimate-security'),
            'password' => __('Password Policy', 'ultimate-security'),
            'protection' => __('Protection', 'ultimate-security'),
            'hardening' => __('Hardening', 'ultimate-security'),
            'firewall' => __('Firewall', 'ultimate-security'),
            'bot' => __('Bot Protection', 'ultimate-security'),
            'rate_limit' => __('Rate Limiting', 'ultimate-security'),
            'url_guard' => __('URL Guard', 'ultimate-security'),
            'salt_change' => __('Salt Keys', 'ultimate-security'),
            'notifications' => __('Notifications', 'ultimate-security'),
            'webhooks' => __('Webhooks', 'ultimate-security'),
        ];

        $parts = explode('.', $key);
        $formatted = [];

        foreach ($parts as $part) {
            $formatted[] = $key_map[$part] ?? ucfirst(str_replace('_', ' ', $part));
        }

        return implode(' > ', $formatted);
    }
}
