<?php
/**
 * LinkCentral Visitor Manager
 *
 * Handles visitor identification, caching, and per-link click tracking.
 */
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

class LinkCentral_Visitor_Manager {
    private $request_cache = array();

    /**
     * Get or generate a unique visitor ID cookie.
     */
    public function get_or_set_visitor_id() {
        // Check cache first
        if (isset($this->request_cache['visitor_id'])) {
            return $this->request_cache['visitor_id'];
        }

        $track_unique_visitors = get_option('linkcentral_track_unique_visitors', true);

        $visitor_id = '';
        if ($track_unique_visitors) {
            // Set persistent cookie for visitor tracking
            $cookie_name = 'lclink_visitor';
            $cookie_expiration = time() + (30 * 24 * 60 * 60); // 30 days

            if (isset($_COOKIE[$cookie_name])) {
                $visitor_id = sanitize_text_field(wp_unslash($_COOKIE[$cookie_name]));
            } else {
                $visitor_id = wp_generate_uuid4();
                setcookie($cookie_name, $visitor_id, $cookie_expiration, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);
            }
        } else {
            // When visitor tracking is disabled, use session-only cookie
            // This keeps the duplicate click tracking functionality working but does not track visitors for 30 days
            $temp_cookie_name = 'lclink_visitor_temp';

            if (isset($_COOKIE[$temp_cookie_name])) {
                $visitor_id = sanitize_text_field(wp_unslash($_COOKIE[$temp_cookie_name]));
            } else {
                $visitor_id = wp_generate_uuid4();
                // Set cookie to expire at end of browser session (0 expiration)
                setcookie($temp_cookie_name, $visitor_id, 0, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);
            }
        }

        // Cache the result
        $this->request_cache['visitor_id'] = $visitor_id;
        return $visitor_id;
    }

    /**
     * Check if this is the first click for a specific link from this visitor.
     * Uses per-link cookies for accurate per-link unique tracking.
     *
     * @param int $link_id The link ID to check
     * @return bool True if this is the first click on this link
     */
    public function is_first_click_for_link($link_id) {
        $cookie_name = 'lclink_click_' . $link_id;
        $cookie_expiration = time() + (30 * 24 * 60 * 60); // 30 days

        if (!isset($_COOKIE[$cookie_name])) {
            // First click on this link - set cookie
            setcookie($cookie_name, '1', $cookie_expiration, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);
            return true;
        }

        // Already clicked this link before
        return false;
    }

    /**
     * Check and prevent duplicate clicks within a short time window.
     * Only active when click tracking is enabled.
     *
     * @param int $link_id The ID of the link
     * @param string $action Optional. The action to perform ('check', 'set'). Default 'check'.
     * @return bool True if duplicate found (for 'check') or success (for 'set')
     */
    public function prevent_duplicate_click($link_id, $action = 'check') {
        // Skip duplicate prevention when reporting is disabled
        if (get_option('linkcentral_disable_reporting', false)) {
            return ($action === 'check') ? false : true;
        }

        $visitor_id = $this->get_or_set_visitor_id();
        $user_agent = $this->get_user_agent();
        $transient_key = 'lc_link_' . md5($link_id . '_' . $visitor_id . '_' . $user_agent);

        if ($action === 'check') {
            $cached_data = get_transient($transient_key);
            return ($cached_data !== false && isset($cached_data['clicked']) && $cached_data['clicked'] === 1);
        } elseif ($action === 'set') {
            $cached_data = get_transient($transient_key);
            $data_to_store = is_array($cached_data) ? array_merge($cached_data, ['clicked' => 1]) : ['clicked' => 1];
            return set_transient($transient_key, $data_to_store, 8);
        }

        return false;
    }

    /**
     * Get cached destination URL from visitor-specific transient.
     * Only used when reporting is enabled.
     *
     * @param int $link_id The ID of the link
     * @return string|null The cached URL or null if not cached
     */
    public function get_cached_destination_url($link_id) {
        // Skip URL caching when reporting is disabled
        if (get_option('linkcentral_disable_reporting', false)) {
            return null;
        }

        $visitor_id = $this->get_or_set_visitor_id();
        $user_agent = $this->get_user_agent();
        $transient_key = 'lc_link_' . md5($link_id . '_' . $visitor_id . '_' . $user_agent);

        // Get cached URL from transient
        $cached_data = get_transient($transient_key);
        return ($cached_data !== false && isset($cached_data['destination_url'])) ? $cached_data['destination_url'] : null;
    }

    /**
     * Set destination URL in visitor-specific transient (combined with click tracking).
     * Only used when reporting is enabled.
     *
     * @param int $link_id The ID of the link
     * @param string $destination_url The destination URL to cache
     * @return bool Success of the set operation
     */
    public function set_cached_destination_url($link_id, $destination_url) {
        // Skip URL caching when reporting is disabled
        if (get_option('linkcentral_disable_reporting', false)) {
            return true; // Simulate success
        }

        $visitor_id = $this->get_or_set_visitor_id();
        $user_agent = $this->get_user_agent();
        $transient_key = 'lc_link_' . md5($link_id . '_' . $visitor_id . '_' . $user_agent);

        // Store URL in transient (combined with click data)
        $cached_data = get_transient($transient_key);
        $data_to_store = is_array($cached_data) ? array_merge($cached_data, ['destination_url' => $destination_url]) : ['destination_url' => $destination_url];
        return set_transient($transient_key, $data_to_store, 8);
    }

    /**
     * Get the IP address of the current user, accounting for various proxy situations.
     */
    public function get_ip_address() {
        // Check cache first
        if (isset($this->request_cache['ip_address'])) {
            return $this->request_cache['ip_address'];
        }

        $ip_headers = array(
            'HTTP_CF_CONNECTING_IP', // Cloudflare
            'HTTP_X_REAL_IP',        // Trusted proxy servers
            'HTTP_CLIENT_IP',        // Some proxy servers (less reliable)
            'HTTP_X_FORWARDED_FOR',  // Can contain multiple IPs (check the first valid one)
            'REMOTE_ADDR'            // Direct IP (most reliable fallback)
        );

        $ip = '';
        foreach ($ip_headers as $header) {
            if (!empty($_SERVER[$header])) {
                // If header contains multiple IPs (in case of HTTP_X_FORWARDED_FOR)
                $ip_list = explode(',', sanitize_text_field(wp_unslash($_SERVER[$header])));
                $ip = trim($ip_list[0]);

                // Validate the IP address (both IPv4 and IPv6)
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)) {
                    break;
                }
            }
        }

        // If no valid IP was found, set to empty string
        if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)) {
            $ip = '';
        }

        // Cache the result
        $this->request_cache['ip_address'] = $ip;
        return $ip;
    }

    /**
     * Get the user agent
     */
    public function get_user_agent() {
        // Check cache first
        if (isset($this->request_cache['user_agent'])) {
            return $this->request_cache['user_agent'];
        }

        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';

        // Cache the result
        $this->request_cache['user_agent'] = $user_agent;
        return $user_agent;
    }

    /**
     * Get cached data for a given key
     */
    public function get_cached($key) {
        return isset($this->request_cache[$key]) ? $this->request_cache[$key] : null;
    }

    /**
     * Set cached data for a given key
     */
    public function set_cached($key, $value) {
        $this->request_cache[$key] = $value;
    }
}
