<?php
/**
 * Data Collection Module
 * 
 * Handles all REST API endpoints for data collection from HTML bait files.
 * Separated from main plugin for better organization and maintainability.
 * 
 * @package My Trail Camera
 * @subpackage Data_Collection
 * @since 7.7
 */

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

/**
 * MTC Data Collector Class
 * 
 * Manages:
 * - REST API routes (/log)
 * - Data validation and sanitization
 * - Rate limiting and security
 * - Email notifications
 * - Detection data processing
 */
class Mtcam_Data_Collector {

    // Rate limit thresholds
    const GLOBAL_RATE_LIMIT    = 10;   // Max requests per IP within the window
    const GLOBAL_RATE_WINDOW   = 300;  // 5 minutes (seconds)
    const FILE_RATE_LIMIT      = 10;   // Max requests per file+device+IP (one open = up to 5 requests: initial + 3 GPS + focus)
    const FILE_RATE_WINDOW     = 300;  // 5 minutes (seconds)
    const PHONE_RATE_LIMIT     = 3;    // Max phone submissions per IP
    const PHONE_RATE_WINDOW    = 600;  // 10 minutes (seconds)
    const EMAIL_RATE_LIMIT     = 5;    // Max emails per device per hour
    const EMAIL_RATE_WINDOW    = 3600; // 1 hour (seconds)

    private $tracker;
    private $table_name;

    public function __construct($tracker_instance) {
        global $wpdb;

        $this->tracker = $tracker_instance;
        $this->table_name = $wpdb->prefix . 'mtcam_detections';
    }
    
    /**
     * Register REST API routes
     * Called via add_action('rest_api_init')
     */
    public function register_routes() {
        // Public endpoints by design — called by bait HTML files opened on the thief's
        // computer/phone, where no WordPress authentication is available.
        // Security is enforced inside callbacks via: rate limiting (IP + per-file + per-device),
        // device ID validation, HMAC signature verification, and nonce replay protection.
        // GET is required — bait HTML runs from file:// on SD card where POST fails (CORS).
        register_rest_route('mtcam/v1', '/log', array(
            'methods' => array('GET', 'POST'),
            'callback' => array($this, 'log_data'),
            'permission_callback' => '__return_true'
        ));

        // Backward compatibility: old bait HTML files on SD cards use sd-tracker/v1 endpoint.
        // Keep these aliases so already-deployed cameras continue to work.
        register_rest_route('sd-tracker/v1', '/log', array(
            'methods' => array('GET', 'POST'),
            'callback' => array($this, 'log_data'),
            'permission_callback' => '__return_true'
        ));

    }
    
    // ============================================================================
    // REST API ENDPOINTS
    // ============================================================================
    public function log_data($request) {
        $params    = $request->get_params();
        $client_ip = $this->get_client_ip();

        // Validate request: rate limits, device ID, signature, nonce
        $validation = $this->validate_log_request($params, $client_ip);
        if ($validation instanceof WP_REST_Response) {
            return $validation;
        }

        $upper_device_id = $validation['device_id'];
        $cam             = $validation['cam'];
        $file_name       = $validation['file_name'];
        $is_test_camera  = $validation['is_test_camera'];

        // Build sanitized detection record
        $insert_data = $this->build_detection_record($params, $client_ip, $upper_device_id, $file_name);

        // Check for recent detection from same device+file+IP (follow-up GPS/focus update)
        $existing = $this->find_recent_detection($upper_device_id, $file_name, $client_ip);

        if ($existing) {
            // Update GPS on existing detection if the new data is more accurate
            $this->update_detection_gps($existing, $insert_data);
            return new WP_REST_Response(array('status' => 'updated', 'id' => $existing->id), 200);
        }

        mtcam_db_insert($this->table_name, $insert_data);
        $entry_id = mtcam_db_insert_id();

        // Auto-mark camera as stolen/tested
        $is_owner_test = $this->process_auto_mark($upper_device_id, $cam, $insert_data, $params, $entry_id, $is_test_camera);

        // Determine if this is an owner test detection
        $is_test_detection = $is_test_camera || $is_owner_test;

        $this->send_notification($entry_id, $insert_data, $is_test_detection);

        return new WP_REST_Response(array('status' => 'success', 'id' => $entry_id), 200);
    }

    /**
     * Validate incoming log request: rate limits, device existence, signature, nonce.
     *
     * @param array  $params    Request parameters.
     * @param string $client_ip Client IP address.
     * @return WP_REST_Response|array Error response or validated context array.
     */
    private function validate_log_request($params, $client_ip) {
        $device_id = !empty($params['dev']) ? sanitize_text_field($params['dev']) : 'UNKNOWN';
        $file_name = !empty($params['ref']) ? sanitize_text_field($params['ref']) : '?';

        // CRITICAL VALIDATION: Device ID must exist in system
        $upper_device_id = strtoupper($device_id);
        $cameras = mtcam_get_cameras();

        if (!isset($cameras[$upper_device_id])) {
            if (!mtcam_rate_limit($upper_device_id)) {
                return new WP_REST_Response(array('success' => false, 'message' => 'Rate limit exceeded'), 429);
            }
            return new WP_REST_Response(array('success' => false, 'message' => 'Forbidden'), 403);
        }

        $cam = $cameras[$upper_device_id];
        $is_test_camera = mtcam_is_tested($cam);

        // File token verification — new trap files include a unique token
        $file_token = ! empty( $params['ft'] ) ? sanitize_text_field( $params['ft'] ) : '';
        if ( $file_token && function_exists( 'mtcam_verify_file_token' ) ) {
            if ( ! mtcam_verify_file_token( $upper_device_id, $file_token ) ) {
                return new WP_REST_Response( array( 'success' => false, 'message' => 'Invalid token' ), 403 );
            }
        }
        // No token = old trap file → allow (backward compatibility)

        // Global rate limit per IP — max 10 requests / 5 min (skip for test cameras)
        if ( ! $is_test_camera ) {
            $global_rate_key = 'mtcam_global_' . md5($client_ip);
            $global_count = get_transient($global_rate_key);
            if ($global_count && $global_count >= self::GLOBAL_RATE_LIMIT) {
                return new WP_REST_Response(array('success' => false, 'message' => 'Rate limit'), 429);
            }
            set_transient($global_rate_key, ($global_count ?: 0) + 1, self::GLOBAL_RATE_WINDOW);
        }

        // Per-file rate limit (skip for test cameras)
        if (!$is_test_camera) {
            $rate_key = 'mtcam_file_rate_' . md5($upper_device_id . '_' . $file_name . '_' . $client_ip);
            $current_count = get_transient($rate_key);
            if ($current_count !== false && $current_count >= self::FILE_RATE_LIMIT) {
                return new WP_REST_Response(array('success' => true, 'message' => 'Already logged recently'), 200);
            }
            $new_count = ($current_count === false) ? 1 : $current_count + 1;
            set_transient($rate_key, $new_count, self::FILE_RATE_WINDOW);
        }

        // Signature validation
        $secret  = $cam['secret_key'] ?? null;
        $payload = wp_json_encode($params);

        if (!mtcam_verify_signature($payload, $params['timestamp'] ?? '', $params['signature'] ?? '', $secret)) {
            return new WP_REST_Response(array('success' => false, 'message' => 'Invalid signature'), 403);
        }

        if (!mtcam_check_nonce($params['nonce'] ?? '')) {
            return new WP_REST_Response(array('success' => false, 'message' => 'Duplicate request'), 403);
        }

        return array(
            'device_id'      => $upper_device_id,
            'cam'            => $cam,
            'file_name'      => $file_name,
            'is_test_camera' => $is_test_camera,
        );
    }

    /**
     * Sanitize request params into a DB-ready detection record.
     *
     * @param array  $params           Request parameters.
     * @param string $client_ip        Client IP address.
     * @param string $upper_device_id  Uppercase device ID.
     * @param string $file_name        Bait file name.
     * @return array Data ready for $wpdb->insert().
     */
    private function build_detection_record($params, $client_ip, $upper_device_id, $file_name) {
        $file_type = 'other';
        if (stripos($file_name, '.html') !== false) $file_type = 'html';

        $ua = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
        $os_info = $this->detect_os($params['platform'] ?? '?', $params['os'] ?? $ua);
        $sys_user = !empty($params['user']) ? sanitize_text_field($params['user']) : '?';

        $isp_info = '';

        $lat = $params['lat'] ?? '?';
        $lon = $params['lon'] ?? '?';
        $acc = $params['acc'] ?? '?';
        if ($lat == '0' || $lon == '0') { $lat = '?'; $lon = '?'; $acc = __('GPS not available', 'my-trail-camera'); }

        if ($lat !== '?') $lat = substr(sanitize_text_field($lat), 0, 20);
        if ($lon !== '?') $lon = substr(sanitize_text_field($lon), 0, 20);
        if ($acc !== '?' && $acc !== __('GPS not available', 'my-trail-camera')) $acc = substr(sanitize_text_field($acc), 0, 20);

        return array(
            'ip' => $client_ip,
            'lat' => $lat,
            'lon' => $lon,
            'acc' => $acc,
            'device_id' => $upper_device_id,
            'file_name' => $file_name,
            'file_type' => $file_type,
            'sys_user' => $sys_user,
            'os_info' => $os_info,
            'ram_info' => substr(sanitize_text_field($params['ram'] ?? '?'), 0, 20),
            'screen_res' => substr(sanitize_text_field($params['screen'] ?? '?'), 0, 30),
            'timezone' => substr(sanitize_text_field($params['tz'] ?? '?'), 0, 50),
            'cpu_cores' => substr(sanitize_text_field($params['cores'] ?? '?'), 0, 10),
            'isp_info' => $isp_info,
            'ua' => $ua,
            'extra_data' => wp_json_encode($params)
        );
    }

    /**
     * Find a recent detection from the same device+file+IP (within FILE_RATE_WINDOW).
     * Used to merge follow-up GPS/focus updates into the original detection.
     *
     * @param string $device_id Uppercase device ID.
     * @param string $file_name Bait file name.
     * @param string $ip        Client IP address.
     * @return object|null Existing detection row or null.
     */
    private function find_recent_detection($device_id, $file_name, $ip) {
        global $wpdb;
        return mtcam_db_get_row($wpdb->prepare(
            "SELECT id, lat, lon, acc FROM {$this->table_name} WHERE device_id = %s AND file_name = %s AND ip = %s AND time > %s ORDER BY id DESC LIMIT 1",
            $device_id,
            $file_name,
            $ip,
            wp_date('Y-m-d H:i:s', time() - self::FILE_RATE_WINDOW)
        ));
    }

    /**
     * Update GPS coordinates on an existing detection if new data is more accurate.
     *
     * @param object $existing   Existing detection row (id, lat, lon, acc).
     * @param array  $new_data   New detection record with potentially better GPS.
     */
    private function update_detection_gps($existing, $new_data) {
        $new_lat = $new_data['lat'] ?? '?';
        $new_lon = $new_data['lon'] ?? '?';
        $new_acc = $new_data['acc'] ?? '?';

        // Skip if new data has no GPS
        if ($new_lat === '?' || $new_lon === '?') {
            return;
        }

        // Always update if existing has no GPS
        if ($existing->lat === '?' || $existing->lon === '?') {
            mtcam_db_update($this->table_name, array('lat' => $new_lat, 'lon' => $new_lon, 'acc' => $new_acc), array('id' => $existing->id));
            return;
        }

        // Update if new accuracy is better (lower number = more accurate)
        $old_meters = (int) $existing->acc;
        $new_meters = (int) $new_acc;
        if ($new_meters > 0 && ($old_meters <= 0 || $new_meters < $old_meters)) {
            mtcam_db_update($this->table_name, array('lat' => $new_lat, 'lon' => $new_lon, 'acc' => $new_acc), array('id' => $existing->id));
        }
    }

    /**
     * Auto-mark camera as stolen or tested based on detection context.
     *
     * @param string $upper_device_id Uppercase device ID.
     * @param array  $cam             Camera data.
     * @param array  $insert_data     Detection record (already inserted).
     * @param array  $params          Raw request parameters.
     * @param int    $entry_id        Inserted log entry ID.
     * @return bool  True if detection was identified as owner test.
     */
    private function process_auto_mark($upper_device_id, $cam, $insert_data, $params, $entry_id, $is_test_camera = false) {
        $is_my_camera = mtcam_is_my_camera($cam);

        // For MY cameras: respect the global setting
        // For OTHER cameras: ALWAYS auto-mark (ignore global setting)
        $auto_mark_enabled = $this->tracker->get_cached_option('mtcam_auto_mark_stolen', 'yes');
        $should_auto_mark = !$is_my_camera || ($is_my_camera && $auto_mark_enabled === 'yes');

        if (!$should_auto_mark) {
            return false;
        }

        // Check if this is owner testing (fingerprint matching)
        $is_owner_test = false;

        if (!empty($cam['owner_fingerprint'])) {
            $owner_ip = $cam['owner_fingerprint']['ip'] ?? '';
            $owner_ua = $cam['owner_fingerprint']['user_agent'] ?? '';
            $device_ip = $insert_data['ip'] ?? '';
            $device_ua = $params['os'] ?? '';

            $exact_match = ($device_ip === $owner_ip && $device_ua === $owner_ua);

            // Subnet match only meaningful for IPv4 — IPv6 has no '.', and any
            // string without a dot would collapse to '' on both sides and falsely match.
            $subnet_match = false;
            if (
                filter_var($owner_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) &&
                filter_var($device_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)
            ) {
                $owner_subnet  = substr($owner_ip, 0, strrpos($owner_ip, '.'));
                $device_subnet = substr($device_ip, 0, strrpos($device_ip, '.'));
                $subnet_match  = ($owner_subnet === $device_subnet && $device_ua === $owner_ua);
            }

            $is_owner_test = $exact_match || $subnet_match;
        }

        // Only mark as stolen if not already marked AND not owner testing AND not a test camera
        if (!mtcam_is_stolen($cam)) {
            if ($is_owner_test || $is_test_camera) {
                if (!$is_test_camera) {
                    mtcam_update_camera_fields($upper_device_id, array('tested' => 'yes'));
                }
            } else {
                mtcam_update_camera_fields($upper_device_id, array(
                    'stolen_date' => wp_date('Y-m-d'),
                    'active' => 'no'
                ));
            }
        }

        return $is_owner_test;
    }

    // ============================================================================
    // UNIFIED NOTIFICATION
    // ============================================================================

    /**
     * Send email notification
     *
     * @param int          $entry_id Log entry ID.
     * @param array|object $data     Detection data (array from log_data or object from DB).
     * @param bool         $is_test  Whether this is an owner test detection.
     */
    private function send_notification($entry_id, $data, $is_test = false) {
        // Normalize: support both array (from log_data) and object (from DB)
        $d = is_array($data) ? $data : (array) $data;
        $device_id = $d['device_id'] ?? 'UNKNOWN';
        $cam = $this->tracker->get_camera_info($device_id);

        // Email rate limit per device (prevents email flood DoS)
        $email_rate_key = 'mtcam_email_rate_' . md5($device_id);
        $email_count = get_transient($email_rate_key);
        if ($email_count && $email_count >= self::EMAIL_RATE_LIMIT) {
            return;
        }
        set_transient($email_rate_key, ($email_count ?: 0) + 1, self::EMAIL_RATE_WINDOW);

        // Check if notifications are enabled
        $is_my_camera = mtcam_is_my_camera($cam);
        if ($is_my_camera && $this->tracker->get_cached_option('mtcam_notifications', 'yes') !== 'yes') {
            return;
        }

        // Check if GPS is required for notifications
        if ($this->tracker->get_cached_option('mtcam_gps_required', 'no') === 'yes') {
            if (empty($d['lat']) || empty($d['lon']) || $d['lat'] === '?' || $d['lon'] === '?') {
                return;
            }
        }

        $notify_email = mtcam_get_notification_email($cam);
        $emails = array_filter(array_map('trim', explode(',', $notify_email)));
        if (empty($emails)) return;

        switch_to_locale(mtcam_get_email_locale());

        $subject = $this->build_email_subject($device_id, $is_test);
        $msg = $this->build_email_header($device_id, $is_test);
        $msg .= $this->build_recovery_section($entry_id, $d, $device_id, $is_test);
        $msg .= $this->build_details_section($d, $device_id, $cam, $is_test);
        $msg .= "\n" . admin_url('admin.php?page=mtcam-logs') . "\n";

        foreach ($emails as $email) {
            if (is_email($email)) wp_mail($email, $subject, $msg);
        }

        restore_previous_locale();
    }

    /**
     * Build email subject line.
     *
     * @param string      $device_id Device ID.
     * @param bool        $is_test   Whether test detection.
     * @param string|null $phone     Captured phone number (PRO).
     * @return string Subject line.
     */
    private function build_email_subject($device_id, $is_test) {
        if ($is_test) {
            /* translators: %s: Device ID or name */
            return sprintf( __( 'MTC TEST OK - %s bait files are working', 'my-trail-camera' ), $device_id );
        }
        /* translators: %s: Device ID or name */
        return sprintf( __( 'MTC Alert - %s Access!', 'my-trail-camera' ), $device_id );
    }

    /**
     * Build email header/intro section.
     *
     * @param string      $device_id Device ID.
     * @param bool        $is_test   Whether test detection.
     * @param string|null $phone     Captured phone number (PRO).
     * @return string Header text.
     */
    private function build_email_header($device_id, $is_test) {
        $msg = '';
        if ($is_test) {
            $msg .= "════════════════════════════════════════\n";
            $msg .= __('TEST DETECTION — YOUR BAIT FILES WORK!', 'my-trail-camera') . "\n";
            $msg .= "════════════════════════════════════════\n\n";
            /* translators: %s: Device ID or name */
            $msg .= sprintf( __( 'You successfully tested bait files for camera %s.', 'my-trail-camera' ), $device_id ) . "\n";
            $msg .= __('The anti-theft system is active and ready.', 'my-trail-camera') . "\n\n";
        } else {
            $msg .= $this->tracker->get_cached_option('mtcam_email_custom_text', __('MTC security system detected an intrusion!', 'my-trail-camera')) . "\n\n";
        }
        return $msg;
    }

    /**
     * Build recovery chance and threat analysis section.
     *
     * @param int    $entry_id  Log entry ID.
     * @param array  $d         Normalized detection data.
     * @param string $device_id Device ID.
     * @param bool   $is_test   Whether test detection.
     * @return string Recovery section text (empty for tests).
     */
    private function build_recovery_section($entry_id, $d, $device_id, $is_test) {
        if ($is_test) {
            return '';
        }

        $ip        = $d['ip'] ?? '?';
        $file_name = $d['file_name'] ?? '?';

        $log_obj = (object) array(
            'ip' => $ip, 'lat' => $d['lat'] ?? '?', 'lon' => $d['lon'] ?? '?', 'acc' => $d['acc'] ?? '?',
            'isp_info' => $d['isp_info'] ?? '?',
            'os_info' => $d['os_info'] ?? '?',
            'screen_res' => $d['screen_res'] ?? '?',
            'time' => current_time( 'mysql' ),
            'id' => $entry_id ?? 0,
            'device_id' => $device_id,
            'file_name' => $file_name,
        );
        $recovery = $this->tracker->calculate_recovery_chance($log_obj);

        $msg = "════════════════════════════════════════\n";
        /* translators: %d: Recovery probability percentage */
        $msg .= sprintf( __( 'RECOVERY CHANCE: %d%%', 'my-trail-camera' ), $recovery['percentage'] ) . "\n";
        $msg .= "════════════════════════════════════════\n";
        $msg .= "{$recovery['recommendation']['text']}\n\n";

        if (!empty($recovery['recommendation']['actions'])) {
            $msg .= __('RECOMMENDED STEPS:', 'my-trail-camera') . "\n";
            foreach ($recovery['recommendation']['actions'] as $i => $action) {
                $msg .= ($i + 1) . ". {$action}\n";
            }
            $msg .= "\n";
        }

        return $msg;
    }

    /**
     * Build detection details section of the email.
     *
     * @param array       $d         Normalized detection data.
     * @param string      $device_id Device ID.
     * @param array       $cam       Camera data.
     * @param bool        $is_test   Whether test detection.
     * @param string|null $phone     Captured phone number (PRO).
     * @return string Details section text.
     */
    private function build_details_section($d, $device_id, $cam, $is_test) {
        $ip        = $d['ip'] ?? '?';
        $lat       = $d['lat'] ?? '?';
        $lon       = $d['lon'] ?? '?';
        $acc       = $d['acc'] ?? '?';
        $isp       = $d['isp_info'] ?? '?';
        $os_info   = $d['os_info'] ?? '?';
        $sys_user  = $d['sys_user'] ?? '?';
        $screen_res = $d['screen_res'] ?? '?';
        $file_name = $d['file_name'] ?? '?';
        $file_type = $d['file_type'] ?? '?';

        $msg = "════════════════════════════════════════\n";
        $msg .= ($is_test ? __('TEST DETAILS', 'my-trail-camera') : __('DETECTION DETAILS', 'my-trail-camera')) . "\n";
        $msg .= "════════════════════════════════════════\n\n";
        $msg .= "📷 " . __('Camera:', 'my-trail-camera') . " {$device_id}\n";
        $msg .= "📍 " . __('Location:', 'my-trail-camera') . " " . ($cam['location'] ?? '') . "\n";
        $msg .= "📄 " . __('File:', 'my-trail-camera') . " {$file_name} (" . strtoupper($file_type) . ")\n";
        $msg .= "⏰ " . __('Time:', 'my-trail-camera') . " " . current_time('mysql') . "\n";
        $msg .= "🌐 IP: {$ip}\n";
        if (!empty($isp)) {
            $msg .= "📡 ISP: {$isp}\n";
        }
        $msg .= "\n";

        if ($lat != '?' && $lon != '?') {
            $msg .= "📍 GPS: {$lat}, {$lon}\n   " . __('Accuracy:', 'my-trail-camera') . " {$acc}\n   " . __('Map:', 'my-trail-camera') . " https://www.google.com/maps?q={$lat},{$lon}\n\n";
        } else {
            $msg .= "📍 " . __('GPS: Not available', 'my-trail-camera') . "\n\n";
        }

        $msg .= "💻 " . __('Device:', 'my-trail-camera') . " {$os_info}\n   " . __('User:', 'my-trail-camera') . " {$sys_user}\n   " . __('Screen:', 'my-trail-camera') . " {$screen_res}\n\n";

        if ($is_test) {
            $msg .= __('This is a test notification. No action is required.', 'my-trail-camera') . "\n";
        }

        return $msg;
    }
    
    
    // ============================================================================
    // HELPER METHODS
    // ============================================================================
    
    private function detect_os($platform, $os_or_ua) {
        // If full OS info provided
        if (!empty($os_or_ua) && strlen($os_or_ua) > 20) {
            if (strpos($os_or_ua, 'Windows') !== false) return $os_or_ua;
        }
        
        // Otherwise parse UA
        $ua = $os_or_ua;
        if (preg_match('/Windows NT 10.0.*Win64.*x64/i', $ua)) return 'Windows 10/11 (64-bit)';
        if (preg_match('/Windows NT 10.0/i', $ua)) return 'Windows 10/11';
        if (preg_match('/Windows NT 6.3/i', $ua)) return 'Windows 8.1';
        if (preg_match('/Windows NT 6.1/i', $ua)) return 'Windows 7';
        if (preg_match('/Win64|WOW64|x64/i', $ua)) return $platform . ' (64-bit)';
        if (preg_match('/Macintosh.*Mac OS X (\d+[._]\d+)/i', $ua, $m)) return 'macOS ' . str_replace('_', '.', $m[1]);
        if (preg_match('/Android/i', $ua)) return 'Android';
        if (preg_match('/iPhone|iPad|iPod/i', $ua)) return 'iOS';
        if (preg_match('/Linux/i', $ua)) return 'Linux';
        return $platform;
    }
    
    private function get_client_ip() {
        $method = get_option('mtcam_ip_source', 'remote_addr');

        $ip = '';
        if ($method === 'cf_connecting_ip' && !empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
            $ip = sanitize_text_field(wp_unslash($_SERVER['HTTP_CF_CONNECTING_IP']));
        } elseif ($method === 'x_forwarded_for' && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $parts = explode(',', sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR'])));
            $ip = trim($parts[0]);
        }

        if (empty($ip) || !filter_var($ip, FILTER_VALIDATE_IP)) {
            $ip = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : 'unknown';
        }

        return $ip;
    }
    

}
