<?php
/**
 * AuctionForge Site Statistics - Server-side Tracker
 * Handles AJAX requests from the frontend tracker JS.
 * Manages visitor identification, session management, GeoIP, bot detection.
 */

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

class AF_Stats_Tracker {

    private static $bot_patterns = array(
        'bot', 'crawl', 'spider', 'slurp', 'mediapartners', 'googlebot',
        'bingbot', 'yandex', 'baidu', 'duckduckbot', 'facebookexternalhit',
        'twitterbot', 'rogerbot', 'linkedinbot', 'embedly', 'quora link preview',
        'showyoubot', 'outbrain', 'pinterest', 'applebot', 'semrushbot',
        'ahrefsbot', 'mj12bot', 'dotbot', 'petalbot', 'bytespider',
        'uptimerobot', 'pingdom', 'lighthouse', 'gtmetrix', 'pagespeed',
        'headlesschrome', 'phantomjs', 'wget', 'curl', 'python-requests',
        'go-http-client', 'java/', 'httpclient', 'nutch', 'scrapy',
    );

    /**
     * Check if current request is from a bot
     */
    public static function is_bot($user_agent = '') {
        if (empty($user_agent)) {
            $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
        }
        $ua_lower = strtolower($user_agent);
        foreach (self::$bot_patterns as $pattern) {
            if (strpos($ua_lower, $pattern) !== false) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if tracking should be skipped for current request
     */
    public static function should_skip() {
        // Check if stats are enabled
        if (get_option('af_stats_enabled', 'on') !== 'on') {
            return true;
        }

        // Check excluded IPs
        $excluded_ips = array_filter(array_map('trim', explode("\n", get_option('af_stats_exclude_ips', ''))));
        $client_ip = self::get_client_ip();
        if (in_array($client_ip, $excluded_ips)) {
            return true;
        }

        // Check excluded roles
        if (is_user_logged_in()) {
            if (get_option('af_stats_track_logged_in', 'on') !== 'on') {
                return true;
            }
            $excluded_roles = array_filter(array_map('trim', explode(',', get_option('af_stats_exclude_roles', 'administrator'))));
            $user = wp_get_current_user();
            foreach ($excluded_roles as $role) {
                if (in_array($role, (array)$user->roles)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Get client IP (Cloudflare aware)
     */
    public static function get_client_ip() {
        if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
            return sanitize_text_field($_SERVER['HTTP_CF_CONNECTING_IP']);
        }
        if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            return sanitize_text_field(trim($ips[0]));
        }
        if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
            return sanitize_text_field($_SERVER['HTTP_X_REAL_IP']);
        }
        return sanitize_text_field(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0');
    }

    /**
     * Anonymize IP if setting is enabled
     */
    public static function maybe_anonymize_ip($ip) {
        if (get_option('af_stats_anonymize_ip', 'off') === 'on') {
            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
                return preg_replace('/\.\d+$/', '.0', $ip);
            } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
                return preg_replace('/:[^:]+$/', ':0000', $ip);
            }
        }
        return $ip;
    }

    /**
     * Parse user agent into browser, OS, device type
     */
    public static function parse_user_agent($ua = '') {
        if (empty($ua)) {
            $ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
        }

        $result = array(
            'browser' => 'Unknown',
            'browser_version' => '',
            'os' => 'Unknown',
            'os_version' => '',
            'device_type' => 'desktop',
        );

        if (empty($ua)) return $result;

        // Device type
        if (preg_match('/Mobile|Android.*Mobile|iPhone|iPod|Windows Phone|BlackBerry/i', $ua)) {
            $result['device_type'] = 'mobile';
        } elseif (preg_match('/iPad|Android(?!.*Mobile)|Tablet/i', $ua)) {
            $result['device_type'] = 'tablet';
        }

        // Browser
        if (preg_match('/Edg[e\/](\d+[\.\d]*)/i', $ua, $m)) {
            $result['browser'] = 'Edge'; $result['browser_version'] = $m[1];
        } elseif (preg_match('/OPR\/(\d+[\.\d]*)/i', $ua, $m)) {
            $result['browser'] = 'Opera'; $result['browser_version'] = $m[1];
        } elseif (preg_match('/Chrome\/(\d+[\.\d]*)/i', $ua, $m)) {
            $result['browser'] = 'Chrome'; $result['browser_version'] = $m[1];
        } elseif (preg_match('/Firefox\/(\d+[\.\d]*)/i', $ua, $m)) {
            $result['browser'] = 'Firefox'; $result['browser_version'] = $m[1];
        } elseif (preg_match('/Safari\/(\d+[\.\d]*)/i', $ua, $m) && preg_match('/Version\/(\d+[\.\d]*)/i', $ua, $v)) {
            $result['browser'] = 'Safari'; $result['browser_version'] = $v[1];
        } elseif (preg_match('/MSIE (\d+[\.\d]*)|Trident.*rv:(\d+[\.\d]*)/i', $ua, $m)) {
            $result['browser'] = 'IE'; $result['browser_version'] = !empty($m[1]) ? $m[1] : $m[2];
        }

        // OS
        if (preg_match('/Windows NT (\d+[\.\d]*)/i', $ua, $m)) {
            $result['os'] = 'Windows';
            $versions = array('10.0' => '10/11', '6.3' => '8.1', '6.2' => '8', '6.1' => '7', '6.0' => 'Vista');
            $result['os_version'] = isset($versions[$m[1]]) ? $versions[$m[1]] : $m[1];
        } elseif (preg_match('/Mac OS X (\d+[._\d]*)/i', $ua, $m)) {
            $result['os'] = 'macOS'; $result['os_version'] = str_replace('_', '.', $m[1]);
        } elseif (preg_match('/iPhone OS (\d+[._\d]*)/i', $ua, $m)) {
            $result['os'] = 'iOS'; $result['os_version'] = str_replace('_', '.', $m[1]);
        } elseif (preg_match('/Android (\d+[\.\d]*)/i', $ua, $m)) {
            $result['os'] = 'Android'; $result['os_version'] = $m[1];
        } elseif (preg_match('/Linux/i', $ua)) {
            $result['os'] = 'Linux'; $result['os_version'] = '';
        } elseif (preg_match('/CrOS/i', $ua)) {
            $result['os'] = 'ChromeOS'; $result['os_version'] = '';
        }

        return $result;
    }

    /**
     * Classify traffic channel from referrer and UTM params
     */
    public static function classify_traffic($referrer_domain, $utm_source = '', $utm_medium = '') {
        if (!empty($utm_medium)) {
            $medium = strtolower($utm_medium);
            if (in_array($medium, array('cpc', 'ppc', 'paid', 'paidsearch'))) return 'paid_search';
            if (in_array($medium, array('social', 'social-media'))) return 'social';
            if ($medium === 'email') return 'email';
            if ($medium === 'affiliate') return 'affiliate';
            if ($medium === 'display') return 'display';
            if ($medium === 'referral') return 'referral';
        }

        if (empty($referrer_domain)) return 'direct';

        $search_engines = array('google', 'bing', 'yahoo', 'duckduckgo', 'baidu', 'yandex', 'ecosia', 'ask.com');
        $social_networks = array('facebook', 'twitter', 'instagram', 'linkedin', 'pinterest', 'reddit', 'tiktok', 'youtube', 'whatsapp', 't.co', 'fb.com');

        $domain_lower = strtolower($referrer_domain);
        foreach ($search_engines as $se) {
            if (strpos($domain_lower, $se) !== false) return 'organic_search';
        }
        foreach ($social_networks as $sn) {
            if (strpos($domain_lower, $sn) !== false) return 'social';
        }

        // Check if referrer is the same site
        $site_domain = strtolower(parse_url(home_url(), PHP_URL_HOST));
        if ($domain_lower === $site_domain || strpos($domain_lower, $site_domain) !== false) {
            return 'internal';
        }

        return 'referral';
    }

    /**
     * GeoIP lookup using ip-api.com (free, 45 requests/minute)
     * Caches results in transients for 24 hours
     */
    public static function geoip_lookup($ip) {
        $default = array('country' => '', 'city' => '', 'region' => '');

        if (empty($ip) || $ip === '127.0.0.1' || $ip === '0.0.0.0' || strpos($ip, '192.168.') === 0 || strpos($ip, '10.') === 0) {
            return $default;
        }

        $cache_key = 'af_geo_' . md5($ip);
        $cached = get_transient($cache_key);
        if ($cached !== false) {
            return $cached;
        }

        $response = wp_remote_get("http://ip-api.com/json/{$ip}?fields=countryCode,city,regionName", array(
            'timeout' => 3,
            'sslverify' => false,
        ));

        if (is_wp_error($response)) {
            return $default;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        if (empty($body) || !isset($body['countryCode'])) {
            return $default;
        }

        $result = array(
            'country' => sanitize_text_field($body['countryCode']),
            'city' => sanitize_text_field(isset($body['city']) ? $body['city'] : ''),
            'region' => sanitize_text_field(isset($body['regionName']) ? $body['regionName'] : ''),
        );

        set_transient($cache_key, $result, DAY_IN_SECONDS);
        return $result;
    }

    /**
     * Get or create visitor record
     */
    public static function get_or_create_visitor($visitor_hash, $ua_data, $geo, $ip, $screen_w = 0, $screen_h = 0) {
        global $wpdb;
        $table = $wpdb->prefix . 'af_stats_visitors';

        $existing = $wpdb->get_row($wpdb->prepare(
            "SELECT id FROM $table WHERE visitor_hash = %s", $visitor_hash
        ));

        if ($existing) {
            $wpdb->query($wpdb->prepare(
                "UPDATE $table SET last_seen = NOW(), total_visits = total_visits + 0,
                 ip_address = %s, country = %s, city = %s, region = %s
                 WHERE visitor_hash = %s",
                $ip, $geo['country'], $geo['city'], $geo['region'], $visitor_hash
            ));
            return $existing->id;
        }

        $wpdb->insert($table, array(
            'visitor_hash' => $visitor_hash,
            'ip_address' => $ip,
            'country' => $geo['country'],
            'city' => $geo['city'],
            'region' => $geo['region'],
            'browser' => $ua_data['browser'],
            'browser_version' => $ua_data['browser_version'],
            'os' => $ua_data['os'],
            'os_version' => $ua_data['os_version'],
            'device_type' => $ua_data['device_type'],
            'screen_width' => intval($screen_w),
            'screen_height' => intval($screen_h),
            'first_seen' => current_time('mysql'),
            'last_seen' => current_time('mysql'),
            'total_visits' => 0,
            'total_pageviews' => 0,
        ));

        return $wpdb->insert_id;
    }

    /**
     * Get or create session record
     */
    public static function get_or_create_session($session_hash, $visitor_id, $visitor_hash, $data) {
        global $wpdb;
        $table = $wpdb->prefix . 'af_stats_sessions';

        $existing = $wpdb->get_row($wpdb->prepare(
            "SELECT id FROM $table WHERE session_hash = %s", $session_hash
        ));

        if ($existing) {
            return $existing->id;
        }

        $referrer_domain = '';
        if (!empty($data['referrer'])) {
            $parsed = parse_url($data['referrer']);
            $referrer_domain = isset($parsed['host']) ? $parsed['host'] : '';
        }

        $traffic_channel = self::classify_traffic(
            $referrer_domain,
            isset($data['utm_source']) ? $data['utm_source'] : '',
            isset($data['utm_medium']) ? $data['utm_medium'] : ''
        );

        $wpdb->insert($table, array(
            'session_hash' => $session_hash,
            'visitor_id' => $visitor_id,
            'visitor_hash' => $visitor_hash,
            'ip_address' => isset($data['ip']) ? $data['ip'] : '',
            'entry_url' => isset($data['url']) ? $data['url'] : '',
            'exit_url' => isset($data['url']) ? $data['url'] : '',
            'referrer_url' => isset($data['referrer']) ? $data['referrer'] : '',
            'referrer_domain' => $referrer_domain,
            'utm_source' => isset($data['utm_source']) ? $data['utm_source'] : '',
            'utm_medium' => isset($data['utm_medium']) ? $data['utm_medium'] : '',
            'utm_campaign' => isset($data['utm_campaign']) ? $data['utm_campaign'] : '',
            'utm_term' => isset($data['utm_term']) ? $data['utm_term'] : '',
            'utm_content' => isset($data['utm_content']) ? $data['utm_content'] : '',
            'traffic_channel' => $traffic_channel,
            'country' => isset($data['country']) ? $data['country'] : '',
            'device_type' => isset($data['device_type']) ? $data['device_type'] : 'desktop',
            'browser' => isset($data['browser']) ? $data['browser'] : '',
            'pageview_count' => 0,
            'duration' => 0,
            'is_bounce' => 1,
            'started_at' => current_time('mysql'),
            'ended_at' => current_time('mysql'),
        ));

        // Increment visitor total_visits
        $wpdb->query($wpdb->prepare(
            "UPDATE {$wpdb->prefix}af_stats_visitors SET total_visits = total_visits + 1, last_seen = NOW() WHERE id = %d",
            $visitor_id
        ));

        return $wpdb->insert_id;
    }

    /**
     * Record a pageview
     */
    public static function record_pageview($session_id, $visitor_id, $visitor_hash, $session_hash, $data) {
        global $wpdb;

        $url_path = '';
        if (!empty($data['url'])) {
            $parsed = parse_url($data['url']);
            $url_path = isset($parsed['path']) ? $parsed['path'] : '/';
        }

        $wpdb->insert($wpdb->prefix . 'af_stats_pageviews', array(
            'session_id' => $session_id,
            'visitor_id' => $visitor_id,
            'visitor_hash' => $visitor_hash,
            'session_hash' => $session_hash,
            'url' => isset($data['url']) ? $data['url'] : '',
            'url_path' => $url_path,
            'page_title' => isset($data['title']) ? $data['title'] : '',
            'referrer_url' => isset($data['referrer']) ? $data['referrer'] : '',
            'load_time' => isset($data['load_time']) ? intval($data['load_time']) : 0,
            'country' => isset($data['country']) ? $data['country'] : '',
            'device_type' => isset($data['device_type']) ? $data['device_type'] : 'desktop',
            'created_at' => current_time('mysql'),
        ));

        $pv_id = $wpdb->insert_id;

        // Update session: increment pageview count, update exit_url, update ended_at
        $session_table = $wpdb->prefix . 'af_stats_sessions';
        $wpdb->query($wpdb->prepare(
            "UPDATE $session_table SET
                pageview_count = pageview_count + 1,
                exit_url = %s,
                ended_at = NOW(),
                duration = TIMESTAMPDIFF(SECOND, started_at, NOW()),
                is_bounce = IF(pageview_count + 1 > 1, 0, 1)
             WHERE id = %d",
            isset($data['url']) ? $data['url'] : '',
            $session_id
        ));

        // Update visitor total_pageviews
        $wpdb->query($wpdb->prepare(
            "UPDATE {$wpdb->prefix}af_stats_visitors SET total_pageviews = total_pageviews + 1, last_seen = NOW() WHERE id = %d",
            $visitor_id
        ));

        // Record lot analytics if this is a lot page and feature is enabled
        if (!empty($url_path) && strpos($url_path, '/lot-') !== false) {
            self::maybe_record_lot_view($url_path, $visitor_hash);
        }

        return $pv_id;
    }

    /**
     * Record a lot view in the analytics table if lot analytics is enabled
     */
    public static function maybe_record_lot_view($url_path, $visitor_hash) {
        if (get_option('bidspirit_settings') === false) return;

        $settings = get_option('bidspirit_settings', array());
        if (!is_array($settings)) $settings = array();
        $enabled = isset($settings['enable_lot_analytics']) ? $settings['enable_lot_analytics'] : 'off';
        if ($enabled !== 'on') return;

        // Extract lot slug from URL path (last segment)
        $segments = array_filter(explode('/', $url_path));
        $lot_slug = end($segments);
        if (empty($lot_slug)) return;

        global $wpdb;
        $post = $wpdb->get_row($wpdb->prepare(
            "SELECT ID FROM {$wpdb->posts} WHERE post_name = %s AND post_type = 'lot' AND post_status = 'publish' LIMIT 1",
            $lot_slug
        ));

        if (!$post) return;

        $term_id = 0;
        $meta_table = $wpdb->prefix . 'bp_meta_data';
        $term_row = $wpdb->get_var($wpdb->prepare(
            "SELECT termId FROM `{$meta_table}` WHERE postId = %d LIMIT 1",
            $post->ID
        ));
        if ($term_row) $term_id = intval($term_row);

        if (class_exists('\\auctionforge\\inc\\model\\LotAnalytics')) {
            \auctionforge\inc\model\LotAnalytics::recordView($post->ID, $term_id, $visitor_hash);
        }
    }

    /**
     * Update time-on-page for previous pageview
     */
    public static function update_time_on_page($prev_pv_id, $time_seconds) {
        global $wpdb;
        if ($prev_pv_id > 0 && $time_seconds > 0 && $time_seconds < 3600) {
            $wpdb->update(
                $wpdb->prefix . 'af_stats_pageviews',
                array('time_on_page' => intval($time_seconds)),
                array('id' => intval($prev_pv_id))
            );
        }
    }

    /**
     * Record a search
     */
    public static function record_search($session_id, $visitor_hash, $data) {
        global $wpdb;
        $wpdb->insert($wpdb->prefix . 'af_stats_searches', array(
            'session_id' => $session_id,
            'visitor_hash' => $visitor_hash,
            'search_query' => isset($data['query']) ? sanitize_text_field($data['query']) : '',
            'search_type' => isset($data['type']) ? sanitize_text_field($data['type']) : 'wordpress',
            'results_count' => isset($data['results']) ? intval($data['results']) : -1,
            'url' => isset($data['url']) ? $data['url'] : '',
            'created_at' => current_time('mysql'),
        ));
    }

    /**
     * Record an outbound click
     */
    public static function record_outbound($session_id, $visitor_hash, $data) {
        global $wpdb;
        $to_domain = '';
        if (!empty($data['to_url'])) {
            $parsed = parse_url($data['to_url']);
            $to_domain = isset($parsed['host']) ? $parsed['host'] : '';
        }
        $wpdb->insert($wpdb->prefix . 'af_stats_outbound', array(
            'session_id' => $session_id,
            'visitor_hash' => $visitor_hash,
            'from_url' => isset($data['from_url']) ? $data['from_url'] : '',
            'to_url' => isset($data['to_url']) ? $data['to_url'] : '',
            'to_domain' => $to_domain,
            'link_text' => isset($data['link_text']) ? sanitize_text_field($data['link_text']) : '',
            'created_at' => current_time('mysql'),
        ));
    }

    /**
     * Record a 404 error
     */
    public static function record_404($url, $referrer, $visitor_hash, $ip) {
        global $wpdb;
        $table = $wpdb->prefix . 'af_stats_errors';
        $url_path = parse_url($url, PHP_URL_PATH) ?: $url;
        $ua = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field($_SERVER['HTTP_USER_AGENT']) : '';

        $wpdb->query($wpdb->prepare(
            "INSERT INTO $table (url, url_path, referrer_url, visitor_hash, ip_address, user_agent, hit_count, first_seen, last_seen)
             VALUES (%s, %s, %s, %s, %s, %s, 1, NOW(), NOW())
             ON DUPLICATE KEY UPDATE hit_count = hit_count + 1, last_seen = NOW(), ip_address = VALUES(ip_address), user_agent = VALUES(user_agent)",
            $url, $url_path, $referrer, $visitor_hash, $ip, $ua
        ));
    }
}
