<?php
/**
 * AJAX Handlers
 *
 * @package Spin_Wheel
 */

// Exit if accessed directly
if (!defined('ABSPATH')) {
    exit;
}

/**
 * SWP_Ajax Class
 */
// Direct queries below target the plugin's own swp_entries / swp_winners tables
// (prepared, internal identifiers) with no applicable caching layer.
// phpcs:disable WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL, WordPress.DB.PreparedSQLPlaceholders, PluginCheck.Security.DirectDB, WordPress.NamingConventions.PrefixAllGlobals
class SWP_Ajax {
    
    /**
     * Instance
     *
     * @var SWP_Ajax
     */
    private static $instance = null;
    
    /**
     * Get instance
     *
     * @return SWP_Ajax
     */
    public static function instance() {
        if (is_null(self::$instance)) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    /**
     * Constructor
     */
    private function __construct() {
        // Public AJAX actions
        add_action('wp_ajax_swp_spin_wheel', [$this, 'handle_spin']);
        add_action('wp_ajax_nopriv_swp_spin_wheel', [$this, 'handle_spin']);
        
        // Note: OTP verification (swp_send_otp, swp_verify_otp) is provided by
        // the Spin Wheel Pro add-on.

        // Admin AJAX actions
        // Note: Event Mode winner-selection actions (swp_select_winner,
        // swp_revert_winner, swp_bulk_select_winners) are provided by the
        // Spin Wheel Pro add-on.
        add_action('wp_ajax_swp_export_entries', [$this, 'export_entries']);
        add_action('wp_ajax_swp_get_pages', [$this, 'get_pages']);
    }
    
    /**
     * Handle wheel spin
     */
    public function handle_spin() {
        // Verify nonce
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'swp_spin_nonce')) {
            wp_send_json_error(['message' => __('Security check failed', 'spin-wheel')]);
        }
        
        // Get data
        $wheel_id   = isset($_POST['wheel_id']) ? intval($_POST['wheel_id']) : 0;
        $user_name  = isset($_POST['user_name']) ? sanitize_text_field(wp_unslash($_POST['user_name'])) : '';
        $user_email = isset($_POST['user_email']) ? sanitize_email(wp_unslash($_POST['user_email'])) : '';
        if (!$wheel_id) {
            wp_send_json_error(['message' => __('Required fields are missing', 'spin-wheel')]);
        }
        
        $form_settings = swp_normalize_form_settings(get_post_meta($wheel_id, '_swp_form_settings', true));
        
        $accept_terms = isset($_POST['swp_accept_terms']) && sanitize_text_field( wp_unslash( $_POST['swp_accept_terms'] ) ) === '1';
        if (!empty($form_settings['enable_terms_checkbox']) && !$accept_terms) {
            wp_send_json_error(['message' => __('You must accept the terms and conditions.', 'spin-wheel')]);
        }
        
        if (!empty($form_settings['show_name'])) {
            if (!empty($form_settings['require_name']) && $user_name === '') {
                wp_send_json_error(['message' => __('Required fields are missing', 'spin-wheel')]);
            }
        } else {
            $user_name = '';
        }
        
        if ($user_name === '') {
            $user_name = apply_filters('swp_default_guest_name', __('Guest', 'spin-wheel'), $wheel_id);
        }
        
        if (!empty($form_settings['show_email'])) {
            if (!empty($form_settings['require_email']) && $user_email === '') {
                wp_send_json_error(['message' => __('Required fields are missing', 'spin-wheel')]);
            }
            if ($user_email !== '' && !is_email($user_email)) {
                wp_send_json_error(['message' => __('Invalid email address', 'spin-wheel')]);
            }
        } else {
            $user_email = '';
        }
        
        if ($user_email === '' || !is_email($user_email)) {
            $user_email = swp_get_anonymous_guest_email($wheel_id, $this->get_ip_address());
        }
        
        /**
         * Phone capture, validation, and storage are Pro capabilities; the free
         * plugin records no phone number of its own. Pro reads and validates the
         * submitted value (nonce already verified above) and returns it.
         *
         * @param string $phone         Phone number to store (default empty).
         * @param array  $form_settings Normalized per-wheel form settings.
         * @param int    $wheel_id      Wheel post ID.
         */
        $user_phone = apply_filters('swp_process_spin_phone', '', $form_settings, $wheel_id);
        
        // Anti-abuse checks (client IP + Pro guards).
        $settings = get_option('swp_settings', []);
        $ip_address = $this->get_ip_address();

        /**
         * Allow Pro add-ons to block a spin (rate limiting, VPN/proxy, etc.) by returning a
         * non-empty message. The free plugin ships no guards of its own.
         *
         * @param string $message    Block message; empty string means "allow".
         * @param string $ip_address Client IP address.
         * @param array  $settings   Global plugin settings.
         */
        $spin_guard_message = apply_filters('swp_spin_guard', '', $ip_address, $settings);
        if (is_string($spin_guard_message) && $spin_guard_message !== '') {
            wp_send_json_error(['message' => $spin_guard_message]);
        }
        
        // Get wheel settings
        $settings = get_post_meta($wheel_id, '_swp_settings', true);
        $one_entry_per_user = isset($settings['one_entry_per_user']) ? $settings['one_entry_per_user'] : 1;
        
        // Initialize database instance
        $db = SWP_Database::instance();
        
        // Check if already participated (only if restriction is enabled)
        if ($one_entry_per_user) {
            if ($db->has_participated($wheel_id, $user_email)) {
                wp_send_json_error(['message' => __('You have already participated in this wheel', 'spin-wheel')]);
            }
        }
        
        // Check participant limit
        $participant_limit = isset($settings['participant_limit']) ? intval($settings['participant_limit']) : 0;
        if ($participant_limit > 0) {
            $current_count = $db->count_entries($wheel_id);
            if ($current_count >= $participant_limit) {
                /* translators: %d: maximum participants */
                wp_send_json_error(['message' => sprintf(__('Sorry, this wheel has reached its maximum limit of %d participants', 'spin-wheel'), $participant_limit)]);
            }
        }
        
        // Get wheel type and mode
        $wheel_type = get_post_meta($wheel_id, '_swp_wheel_type', true);
        $prizes     = get_post_meta($wheel_id, '_swp_prizes', true);
        $wheel_mode = get_post_meta($wheel_id, '_swp_wheel_mode', true); // 'instant' or 'event'
        
        if (!$prizes || !is_array($prizes)) {
            wp_send_json_error(['message' => __('Wheel configuration error', 'spin-wheel')]);
        }

        // SERVER-SIDE PRIZE SELECTION
        $prize_index = $this->select_prize_server_side($prizes);
        $prize = $prizes[$prize_index];
        
        if (!$prize) {
            wp_send_json_error(['message' => __('Invalid prize', 'spin-wheel')]);
        }
        
        // Add index to prize array for frontend use
        $prize['index'] = $prize_index;
        
        // Determine if winner (for instant mode)
        $is_winner = 0;
        if ($wheel_mode === 'instant') {
            $is_winner = isset($prize['is_winning']) && $prize['is_winning'] ? 1 : 0;
        }
        
        // Save entry
        $entry_data = [
            'wheel_id'    => $wheel_id,
            'wheel_type'  => $wheel_type,
            'user_name'   => $user_name,
            'user_email'  => $user_email,
            'user_phone'  => $user_phone,
            'prize_label' => $prize['label'],
            'prize_code'  => isset($prize['code']) ? $prize['code'] : '',
            'ip_address'  => $this->get_ip_address(),
            'user_agent'  => isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '',
            'is_winner'   => $is_winner,
            /**
             * Filter the entry's stored spin_data. Pro add-ons use this to merge
             * extra captured data (e.g. UTM / ad-click parameters). The free
             * plugin stores only the prize index and timestamp.
             *
             * @param array $spin_data Base spin_data.
             * @param int   $wheel_id  Wheel post ID.
             */
            'spin_data'   => apply_filters(
                'swp_entry_spin_data',
                [
                    'prize_index' => $prize_index,
                    'timestamp'   => current_time('mysql'),
                ],
                $wheel_id
            ),
        ];
        
        $entry_id = $db->save_entry($entry_data);
        
        if (!$entry_id) {
            wp_send_json_error(['message' => __('Failed to save entry', 'spin-wheel')]);
        }

        /**
         * Fires after a wheel entry is stored (Pro CRM, analytics, etc.).
         *
         * @param int   $entry_id   New entry ID.
         * @param array $entry_data Data passed to SWP_Database::save_entry (wheel_id, user fields, prize, is_winner, spin_data, …).
         * @param int   $wheel_id   Wheel post ID.
         */
        do_action('swp_entry_created', $entry_id, $entry_data, $wheel_id);
        
        // Send email if winner (instant mode) and email notifications are enabled
        if ($is_winner && $wheel_mode === 'instant') {
            // Check wheel-specific settings first, then fall back to global settings
            $wheel_settings = get_post_meta($wheel_id, '_swp_settings', true) ?: [];
            $global_settings = get_option('swp_settings', []);
            
            // Wheel-specific setting takes priority, fallback to global setting (default: enabled)
            if (isset($wheel_settings['enable_winner_notifications'])) {
                $enable_winner_notifications = $wheel_settings['enable_winner_notifications'];
            } else {
                $enable_winner_notifications = isset($global_settings['enable_winner_notifications']) ? $global_settings['enable_winner_notifications'] : 1;
            }
            
            if ($enable_winner_notifications) {
                SWP_Email::instance()->send_winner_email($wheel_id, $entry_id);
            }
        }
        
        // Response
        $response = [
            'success'     => true,
            'entry_id'    => $entry_id,
            'prize'       => $prize,
            'prize_index' => $prize_index,
            'is_winner'   => $is_winner,
            'wheel_mode'  => $wheel_mode,
        ];
        
        wp_send_json_success($response);
    }

    private function select_prize_server_side($prizes) {
        // Build weighted array based on probability
        $weighted_prizes = [];
        foreach ($prizes as $index => $prize) {
            $probability = isset($prize['probability']) ? intval($prize['probability']) : 1;
            for ($i = 0; $i < $probability; $i++) {
                $weighted_prizes[] = $index;
            }
        }

        // Cryptographically secure random selection
        $random_key = random_int(0, count($weighted_prizes) - 1);
        return $weighted_prizes[$random_key];
    }
    
    /**
     * Export entries (admin only)
     */
    public function export_entries() {
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_die(esc_html__('Permission denied', 'spin-wheel'));
        }
        
        // Verify nonce
        if (!isset($_GET['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['nonce'])), 'swp_export_nonce')) {
            wp_die(esc_html__('Security check failed', 'spin-wheel'));
        }
        
        $wheel_id = isset($_GET['wheel_id']) ? intval($_GET['wheel_id']) : 0;
        
        if (!$wheel_id) {
            wp_die(esc_html__('Invalid wheel', 'spin-wheel'));
        }
        
        $db = SWP_Database::instance();
        $entries = $db->get_all_entries($wheel_id);
        
        // Set headers for CSV download
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename=wheel-entries-' . $wheel_id . '-' . gmdate('Y-m-d') . '.csv');
        
        // Create CSV
        $output = fopen('php://output', 'w'); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
        
        // Headers
        fputcsv($output, [
            'ID', 'Name', 'Email', 'Phone', 'Campaign (UTM)', 'Prize', 'Code', 'Is Winner', 'Created At',
        ]);
        
        // Data
        foreach ($entries as $entry) {
            fputcsv($output, [
                $entry->id,
                swp_csv_safe_cell($entry->user_name),
                swp_csv_safe_cell($entry->user_email),
                swp_csv_safe_cell($entry->user_phone),
                swp_csv_safe_cell(swp_entry_format_utm_csv($entry)),
                swp_csv_safe_cell($entry->prize_label),
                swp_csv_safe_cell($entry->prize_code),
                $entry->is_winner ? 'Yes' : 'No',
                $entry->created_at,
            ]);
        }
        
        fclose($output); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
        exit;
    }
    
    /**
     * Get pages for advanced conditions
     */
    public function get_pages() {
        // Check nonce
        check_ajax_referer('swp_admin_nonce', 'nonce');
        
        // Check permissions
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(['message' => __('Permission denied', 'spin-wheel')]);
        }
        
        // Get all pages
        $pages = get_pages([
            'sort_column' => 'post_title',
            'sort_order'  => 'ASC',
            'number'      => 200, // Limit to 200 pages for performance
        ]);
        
        $pages_array = [];
        foreach ($pages as $page) {
            $pages_array[] = [
                'id'    => $page->ID,
                'title' => $page->post_title,
            ];
        }
        
        wp_send_json_success([
            'pages' => $pages_array
        ]);
    }
    
    /**
     * Get the user's IP address
     * 
     * @return string User's IP address
     */
    protected function get_ip_address() {
        foreach ( array( 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR' ) as $key ) {
            if ( empty( $_SERVER[ $key ] ) ) {
                continue;
            }
            $ip = sanitize_text_field( wp_unslash( $_SERVER[ $key ] ) );
            if ( strpos( $ip, ',' ) !== false ) {
                $ip = trim( explode( ',', $ip )[0] );
            }
            if ( filter_var( $ip, FILTER_VALIDATE_IP ) ) {
                return $ip;
            }
        }
        return '';
    }
    
}

// phpcs:enable WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL, WordPress.DB.PreparedSQLPlaceholders, PluginCheck.Security.DirectDB, WordPress.NamingConventions.PrefixAllGlobals
