<?php
/**
 * TrackPro v5 — Intelligence Engine
 * Behavioral Profiling & Enhanced Identity Correlation
 */

class VisitorProfiler {
    private $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    /**
     * Profile a visitor based on all collected data across all sessions
     */
    public function profileVisitor(int $visitorId): array {
        // Gather all session data for this visitor
        $sessions = $this->getVisitorSessions($visitorId);
        if (empty($sessions)) return ['error' => 'No sessions'];

        $profile = [
            'visitor_id' => $visitorId,
            'data_points_used' => 0,
        ];

        // Run all analyzers
        $profile = array_merge($profile, $this->analyzeDeviceTier($sessions));
        $profile = array_merge($profile, $this->analyzeTechLevel($sessions));
        $profile = array_merge($profile, $this->analyzeIntent($sessions));
        $profile = array_merge($profile, $this->analyzeEngagement($sessions));
        $profile = array_merge($profile, $this->analyzePrivacy($sessions));
        $profile = array_merge($profile, $this->analyzeEcosystem($sessions));
        $profile = array_merge($profile, $this->analyzePatterns($sessions));
        $profile = array_merge($profile, $this->inferDemographics($sessions, $profile));
        $profile = array_merge($profile, $this->inferInterests($sessions, $profile));

        // Calculate overall confidence
        $profile['confidence_score'] = $this->calculateConfidence($sessions, $profile);

        // Store profile
        $this->storeProfile($profile);

        return $profile;
    }

    private function getVisitorSessions(int $visitorId): array {
        // Use column-safe query: only select columns we know exist from core schema
        // Additional v4 columns are fetched separately to avoid errors
        $sql = "SELECT s.id, s.session_uid, s.link_id, s.visitor_hash, s.is_bot, 
                s.duration_seconds, s.started_at, s.last_activity,
                tl.title as link_title, tl.link_uid
            FROM `visitor_sessions` vs
            JOIN `sessions` s ON vs.session_id = s.id
            LEFT JOIN `tracking_links` tl ON s.link_id = tl.id
            WHERE vs.visitor_id = ?
            ORDER BY s.started_at DESC
            LIMIT 100";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([$visitorId]);
        $sessions = $stmt->fetchAll(PDO::FETCH_ASSOC);

        // Enrich each session with data from related tables
        foreach ($sessions as &$s) {
            $sid = $s['id'];
            // Device data - select all available columns
            try { $r = $this->pdo->prepare("SELECT * FROM `device_data` WHERE `session_id`=?"); $r->execute([$sid]); $d = $r->fetch(PDO::FETCH_ASSOC); if ($d) $s = array_merge($s, $d); } catch (\Exception $e) {}
            // Browser data
            try { $r = $this->pdo->prepare("SELECT * FROM `browser_data` WHERE `session_id`=?"); $r->execute([$sid]); $d = $r->fetch(PDO::FETCH_ASSOC); if ($d) $s = array_merge($s, $d); } catch (\Exception $e) {}
            // Network data
            try { $r = $this->pdo->prepare("SELECT * FROM `network_data` WHERE `session_id`=?"); $r->execute([$sid]); $d = $r->fetch(PDO::FETCH_ASSOC); if ($d) $s = array_merge($s, $d); } catch (\Exception $e) {}
            // Location data
            try { $r = $this->pdo->prepare("SELECT * FROM `location_data` WHERE `session_id`=?"); $r->execute([$sid]); $d = $r->fetch(PDO::FETCH_ASSOC); if ($d) $s = array_merge($s, $d); } catch (\Exception $e) {}
            // Risk data
            try { $r = $this->pdo->prepare("SELECT * FROM `risk_data` WHERE `session_id`=?"); $r->execute([$sid]); $d = $r->fetch(PDO::FETCH_ASSOC); if ($d) $s = array_merge($s, $d); } catch (\Exception $e) {}
            // Fingerprint data
            try { $r = $this->pdo->prepare("SELECT * FROM `fingerprint_data` WHERE `session_id`=?"); $r->execute([$sid]); $d = $r->fetch(PDO::FETCH_ASSOC); if ($d) $s = array_merge($s, $d); } catch (\Exception $e) {}
        }
        unset($s);
        return $sessions;
    }

    /**
     * DEVICE TIER — Infer economic level from device signals
     */
    private function analyzeDeviceTier(array $sessions): array {
        $s = $sessions[0]; // Most recent session
        $score = 50; $signals = [];

        // RAM analysis
        $ram = floatval($s['device_memory'] ?? 0);
        if ($ram >= 16) { $score += 20; $signals[] = "RAM:{$ram}GB(premium)"; }
        elseif ($ram >= 8) { $score += 10; $signals[] = "RAM:{$ram}GB(mid-high)"; }
        elseif ($ram >= 4) { $signals[] = "RAM:{$ram}GB(mid)"; }
        elseif ($ram > 0) { $score -= 15; $signals[] = "RAM:{$ram}GB(budget)"; }

        // Screen resolution
        $sw = intval($s['screen_width'] ?? 0); $sh = intval($s['screen_height'] ?? 0);
        $pixels = $sw * $sh;
        if ($pixels >= 3686400) { $score += 15; $signals[] = "Screen:4K+"; } // 4K+
        elseif ($pixels >= 2073600) { $score += 10; $signals[] = "Screen:QHD"; } // QHD
        elseif ($pixels >= 1920*1080) { $score += 5; $signals[] = "Screen:FHD"; }
        elseif ($pixels > 0) { $score -= 5; $signals[] = "Screen:HD-"; }

        // Pixel ratio (Retina displays)
        $pr = floatval($s['pixel_ratio'] ?? 1);
        if ($pr >= 3) { $score += 10; $signals[] = "Retina:3x"; }
        elseif ($pr >= 2) { $score += 5; $signals[] = "Retina:2x"; }

        // GPU analysis
        $gpu = strtolower($s['gpu_renderer'] ?? '');
        if (preg_match('/rtx\s*(40[89]0|4070|3090|3080)/i', $gpu)) { $score += 25; $signals[] = "GPU:high-end"; }
        elseif (preg_match('/rtx|radeon\s*rx\s*(7[89]|69)/i', $gpu)) { $score += 15; $signals[] = "GPU:mid-high"; }
        elseif (preg_match('/apple|m[1-4]\s*(pro|max|ultra)/i', $gpu)) { $score += 20; $signals[] = "GPU:apple-silicon"; }
        elseif (preg_match('/mali-g7[2-9]|adreno\s*(7[3-9]|[89])/i', $gpu)) { $score += 10; $signals[] = "GPU:flagship-mobile"; }
        elseif (preg_match('/mali-g5|adreno\s*(6[0-4]|5)/i', $gpu)) { $score -= 10; $signals[] = "GPU:budget-mobile"; }

        // Color gamut & HDR
        if (($s['color_gamut'] ?? '') === 'p3' || ($s['color_gamut'] ?? '') === 'rec2020') { $score += 8; $signals[] = "WideGamut"; }
        if (!empty($s['hdr_support'])) { $score += 5; $signals[] = "HDR"; }

        // CPU threads
        $cpu = intval($s['hardware_concurrency'] ?? 0);
        if ($cpu >= 16) { $score += 12; $signals[] = "CPU:{$cpu}cores"; }
        elseif ($cpu >= 8) { $score += 5; $signals[] = "CPU:{$cpu}cores"; }
        elseif ($cpu > 0 && $cpu <= 2) { $score -= 10; $signals[] = "CPU:{$cpu}cores(low)"; }

        // Storage
        $storage = intval($s['storage_quota_mb'] ?? 0);
        if ($storage > 500000) { $score += 5; $signals[] = "Storage:500GB+"; }

        // Connection speed
        $bw = intval($s['bandwidth_kbps'] ?? 0);
        if ($bw > 50000) { $score += 8; $signals[] = "Speed:50Mbps+"; }
        elseif ($bw > 10000) { $score += 3; $signals[] = "Speed:10Mbps+"; }
        elseif ($bw > 0 && $bw < 1000) { $score -= 10; $signals[] = "Speed:slow"; }

        // NFC, Bluetooth (premium features)
        if (!empty($s['nfc_available'])) { $score += 3; $signals[] = "NFC"; }
        if (!empty($s['bluetooth_available'])) { $score += 2; $signals[] = "BT"; }

        // OS analysis
        $os = strtolower($s['os_name'] ?? '');
        if (strpos($os, 'ios') !== false || strpos($os, 'mac') !== false) { $score += 10; $signals[] = "Apple-OS"; }

        $score = max(0, min(100, $score));
        $tier = 'mid';
        if ($score >= 80) $tier = 'ultra';
        elseif ($score >= 60) $tier = 'premium';
        elseif ($score >= 35) $tier = 'mid';
        else $tier = 'budget';

        return [
            'device_tier' => $tier,
            'device_tier_score' => $score,
            'device_tier_signals' => json_encode($signals),
            'data_points_used' => count($signals),
        ];
    }

    /**
     * TECH LEVEL — How technically sophisticated is this person
     */
    private function analyzeTechLevel(array $sessions): array {
        $s = $sessions[0];
        $score = 30; $signals = [];

        // Ad blocker = tech aware
        if (!empty($s['ad_blocker_detected'])) { $score += 15; $signals[] = "adblocker"; }

        // VPN/Tor usage = tech savvy
        if (!empty($s['is_vpn'])) { $score += 15; $signals[] = "VPN"; }
        if (!empty($s['is_tor'])) { $score += 25; $signals[] = "Tor"; }

        // Incognito mode
        if (!empty($s['incognito_detected'])) { $score += 10; $signals[] = "incognito"; }

        // DevTools extensions
        $ext = strtolower($s['extensions_detected'] ?? '');
        if (strpos($ext, 'react devtools') !== false || strpos($ext, 'vue devtools') !== false || strpos($ext, 'redux') !== false) {
            $score += 20; $signals[] = "dev-tools-ext";
        }

        // Privacy extensions
        if (strpos($ext, 'ghostery') !== false || strpos($ext, 'privacy badger') !== false) {
            $score += 12; $signals[] = "privacy-ext";
        }

        // Password manager = moderate tech
        if (strpos($ext, '1password') !== false || strpos($ext, 'lastpass') !== false || strpos($ext, 'bitwarden') !== false || strpos($ext, 'dashlane') !== false) {
            $score += 8; $signals[] = "password-mgr";
        }

        // DNT flag or GPC
        if (!empty($s['dnt_flag'])) { $score += 5; $signals[] = "DNT"; }
        if (!empty($s['gpc_flag'])) { $score += 8; $signals[] = "GPC"; }

        // Dark mode (correlates with tech-savvy)
        if (!empty($s['dark_mode'])) { $score += 3; $signals[] = "dark-mode"; }

        // Browser choice
        $browser = strtolower($s['browser_name'] ?? '');
        if (strpos($browser, 'firefox') !== false) { $score += 8; $signals[] = "firefox"; }
        elseif (strpos($browser, 'brave') !== false) { $score += 12; $signals[] = "brave"; }
        elseif (strpos($browser, 'vivaldi') !== false || strpos($browser, 'opera') !== false) { $score += 5; $signals[] = "alt-browser"; }

        // Linux user
        $os = strtolower($s['os_name'] ?? '');
        if (strpos($os, 'linux') !== false && strpos($os, 'android') === false) { $score += 20; $signals[] = "linux"; }

        // Timezone mismatch (knows how to use VPN but forgot TZ)
        if (!empty($s['timezone_mismatch'])) { $score += 5; $signals[] = "tz-mismatch"; }

        // Multiple extensions
        $extCount = intval($s['extensions_count'] ?? 0);
        if ($extCount >= 5) { $score += 8; $signals[] = "many-extensions:{$extCount}"; }

        // WebAssembly support awareness (usually tech sites enable this)
        // Reduced motion = accessibility aware
        if (!empty($s['reduced_motion'])) { $score += 3; $signals[] = "a11y-motion"; }

        $score = max(0, min(100, $score));
        $level = 'basic';
        if ($score >= 80) $level = 'expert';
        elseif ($score >= 60) $level = 'advanced';
        elseif ($score >= 40) $level = 'intermediate';
        elseif ($score >= 20) $level = 'basic';
        else $level = 'novice';

        return [
            'tech_level' => $level,
            'tech_level_score' => $score,
            'tech_level_signals' => json_encode($signals),
        ];
    }

    /**
     * INTENT — What is this visitor trying to do
     */
    private function analyzeIntent(array $sessions): array {
        $score = 0; $signals = [];
        $sessionCount = count($sessions);
        $latestDuration = intval($sessions[0]['duration_seconds'] ?? 0);

        // Multiple visits = higher intent
        if ($sessionCount >= 5) { $score += 30; $signals[] = "repeat:{$sessionCount}visits"; }
        elseif ($sessionCount >= 3) { $score += 20; $signals[] = "returning:{$sessionCount}visits"; }
        elseif ($sessionCount >= 2) { $score += 10; $signals[] = "second-visit"; }

        // Duration analysis
        if ($latestDuration >= 120) { $score += 20; $signals[] = "long-session:{$latestDuration}s"; }
        elseif ($latestDuration >= 30) { $score += 10; $signals[] = "engaged:{$latestDuration}s"; }
        elseif ($latestDuration < 5) { $score -= 10; $signals[] = "bounce:{$latestDuration}s"; }

        // Multiple different links visited
        $uniqueLinks = count(array_unique(array_column($sessions, 'link_uid')));
        if ($uniqueLinks >= 3) { $score += 15; $signals[] = "multi-link:{$uniqueLinks}"; }

        // Behavioral data from latest session
        $beh = $this->getLatestBehavior($sessions[0]['id']);
        if ($beh) {
            $clicks = intval($beh['click_count'] ?? 0);
            $scroll = intval($beh['max_scroll'] ?? 0);
            $copies = intval($beh['copy_events'] ?? 0);

            if ($clicks > 5) { $score += 10; $signals[] = "clicks:{$clicks}"; }
            if ($scroll > 50) { $score += 10; $signals[] = "deep-scroll:{$scroll}%"; }
            if ($copies > 0) { $score += 15; $signals[] = "copied-content"; }
        }

        // GPS granted = higher trust/intent
        if (($sessions[0]['gps_source'] ?? '') === 'browser') { $score += 10; $signals[] = "gps-granted"; }

        // In-app browser context
        $iab = $sessions[0]['in_app_browser'] ?? 'none';
        if ($iab !== 'none' && $iab !== null) { $signals[] = "from:{$iab}"; }

        $score = max(0, min(100, $score));
        $intent = 'casual';
        if ($sessionCount >= 3 && $score >= 60) $intent = 'returning';
        elseif ($score >= 70) $intent = 'ready_to_act';
        elseif ($score >= 50) $intent = 'comparing';
        elseif ($score >= 25) $intent = 'researching';
        else $intent = 'casual';

        if (intval($sessions[0]['bot_likelihood'] ?? 0) > 60) $intent = 'automated';

        return [
            'intent' => $intent,
            'intent_score' => $score,
            'intent_signals' => json_encode($signals),
        ];
    }

    /**
     * ENGAGEMENT — How engaged is this visitor
     */
    private function analyzeEngagement(array $sessions): array {
        $totalDuration = 0; $totalInteractions = 0;
        foreach ($sessions as $s) {
            $totalDuration += intval($s['duration_seconds'] ?? 0);
            $beh = $this->getLatestBehavior($s['id']);
            if ($beh) {
                $totalInteractions += intval($beh['click_count'] ?? 0);
                $totalInteractions += intval($beh['keystroke_count'] ?? 0);
                $totalInteractions += intval($beh['touch_gestures'] ?? 0);
                $totalInteractions += intval($beh['copy_events'] ?? 0);
            }
        }

        $avgDuration = $totalDuration / max(1, count($sessions));
        $score = 0;
        if ($avgDuration >= 120) $score += 40;
        elseif ($avgDuration >= 30) $score += 20;
        elseif ($avgDuration >= 10) $score += 10;

        if ($totalInteractions >= 50) $score += 40;
        elseif ($totalInteractions >= 20) $score += 25;
        elseif ($totalInteractions >= 5) $score += 10;

        $score += min(20, count($sessions) * 4);
        $score = max(0, min(100, $score));

        $level = 'glance';
        if ($score >= 80) $level = 'power_user';
        elseif ($score >= 55) $level = 'engaged';
        elseif ($score >= 30) $level = 'reader';
        elseif ($score >= 10) $level = 'glance';
        else $level = 'bounce';

        return [
            'engagement_level' => $level,
            'engagement_score' => $score,
            'avg_session_duration' => (int)$avgDuration,
            'total_interactions' => $totalInteractions,
        ];
    }

    /**
     * PRIVACY — How privacy-conscious is this visitor
     */
    private function analyzePrivacy(array $sessions): array {
        $s = $sessions[0];
        $score = 0; $signals = [];

        if (!empty($s['is_vpn'])) { $score += 20; $signals[] = "VPN"; }
        if (!empty($s['is_tor'])) { $score += 30; $signals[] = "Tor"; }
        if (!empty($s['incognito_detected'])) { $score += 15; $signals[] = "Incognito"; }
        if (!empty($s['ad_blocker_detected'])) { $score += 10; $signals[] = "AdBlocker"; }
        if (!empty($s['dnt_flag'])) { $score += 5; $signals[] = "DNT"; }
        if (!empty($s['gpc_flag'])) { $score += 10; $signals[] = "GPC"; }

        $ext = strtolower($s['extensions_detected'] ?? '');
        if (strpos($ext, 'ghostery') !== false) { $score += 10; $signals[] = "Ghostery"; }
        if (strpos($ext, 'privacy badger') !== false) { $score += 10; $signals[] = "PrivacyBadger"; }
        if (strpos($ext, 'dark reader') !== false) { $score += 2; }

        // Permissions denied
        if (($s['perm_geolocation'] ?? '') === 'denied') { $score += 5; $signals[] = "geo-denied"; }
        if (($s['perm_notifications'] ?? '') === 'denied') { $score += 3; $signals[] = "notif-denied"; }
        if (($s['perm_camera'] ?? '') === 'denied') { $score += 3; $signals[] = "cam-denied"; }

        // GPS not granted despite being asked
        if (($s['gps_source'] ?? 'none') === 'none') { $score += 3; }

        $browser = strtolower($s['browser_name'] ?? '');
        if (strpos($browser, 'brave') !== false) { $score += 15; $signals[] = "Brave"; }
        if (strpos($browser, 'firefox') !== false) { $score += 5; $signals[] = "Firefox"; }

        $score = max(0, min(100, $score));
        $level = 'none';
        if ($score >= 70) $level = 'paranoid';
        elseif ($score >= 50) $level = 'high';
        elseif ($score >= 25) $level = 'moderate';
        elseif ($score >= 10) $level = 'low';

        return [
            'privacy_consciousness' => $level,
            'privacy_score' => $score,
            'privacy_signals' => json_encode($signals),
        ];
    }

    /**
     * ECOSYSTEM — Which tech ecosystem does this person live in
     */
    private function analyzeEcosystem(array $sessions): array {
        $s = $sessions[0]; $signals = [];
        $scores = ['google' => 0, 'apple' => 0, 'microsoft' => 0, 'meta' => 0, 'other' => 0];

        $os = strtolower($s['os_name'] ?? '');
        if (strpos($os, 'ios') !== false || strpos($os, 'mac') !== false) { $scores['apple'] += 30; $signals[] = "OS:Apple"; }
        elseif (strpos($os, 'android') !== false) { $scores['google'] += 20; $signals[] = "OS:Android"; }
        elseif (strpos($os, 'windows') !== false) { $scores['microsoft'] += 20; $signals[] = "OS:Windows"; }
        elseif (strpos($os, 'chrome') !== false) { $scores['google'] += 30; $signals[] = "OS:ChromeOS"; }
        else { $scores['other'] += 20; $signals[] = "OS:Other"; }

        $browser = strtolower($s['browser_name'] ?? '');
        if (strpos($browser, 'chrome') !== false) { $scores['google'] += 10; }
        elseif (strpos($browser, 'safari') !== false) { $scores['apple'] += 15; }
        elseif (strpos($browser, 'edge') !== false) { $scores['microsoft'] += 15; }

        // Social platform detection
        $socials = $this->pdo->prepare("SELECT `platform`,`logged_in`,`confidence` FROM `social_detection` WHERE `session_id`=? AND (`logged_in`=1 OR `confidence` IN ('high','definite'))");
        $socials->execute([$s['id']]);
        foreach ($socials->fetchAll() as $soc) {
            $p = $soc['platform'];
            if (in_array($p, ['google', 'gmail', 'youtube', 'google_drive', 'google_maps'])) { $scores['google'] += 8; $signals[] = "logged:{$p}"; }
            elseif (in_array($p, ['facebook', 'instagram', 'messenger', 'whatsapp', 'threads'])) { $scores['meta'] += 8; $signals[] = "logged:{$p}"; }
            elseif (in_array($p, ['microsoft', 'linkedin'])) { $scores['microsoft'] += 8; $signals[] = "logged:{$p}"; }
        }

        // Determine primary
        arsort($scores);
        $primary = array_key_first($scores);
        $top = array_slice($scores, 0, 2, true);
        $vals = array_values($top);
        if (count($vals) >= 2 && $vals[0] - $vals[1] < 10) $primary = 'mixed';

        return [
            'primary_ecosystem' => $primary,
            'ecosystem_signals' => json_encode($signals),
        ];
    }

    /**
     * PATTERNS — Visit patterns and timing
     */
    private function analyzePatterns(array $sessions): array {
        $count = count($sessions);
        $pattern = 'one_time';
        if ($count >= 10) $pattern = 'obsessive';
        elseif ($count >= 5) $pattern = 'frequent';
        elseif ($count >= 3) $pattern = 'regular';
        elseif ($count >= 2) $pattern = 'occasional';

        // Active hours
        $hourBins = array_fill(0, 24, 0);
        foreach ($sessions as $s) {
            if (!empty($s['started_at'])) {
                $hour = intval(date('G', strtotime($s['started_at'])));
                $hourBins[$hour]++;
            }
        }

        return [
            'visit_pattern' => $pattern,
            'active_hours' => json_encode($hourBins),
        ];
    }

    /**
     * DEMOGRAPHICS — Statistical inference of age, profession
     */
    private function inferDemographics(array $sessions, array $profile): array {
        $s = $sessions[0];
        $signals = [];

        // Age range inference
        $ageScore = 35; // default mid-range
        $os = strtolower($s['os_name'] ?? '');
        $browser = strtolower($s['browser_name'] ?? '');
        $darkMode = !empty($s['dark_mode']);

        // Young indicators
        if (in_array($s['in_app_browser'] ?? '', ['tiktok', 'snapchat', 'instagram'])) { $ageScore -= 10; $signals[] = "young:social-app"; }
        if ($darkMode) { $ageScore -= 3; }

        // Older indicators
        if ($browser === 'safari' && strpos($os, 'mac') !== false) { $ageScore += 5; }
        if (intval($s['screen_width'] ?? 0) > 1920) { $ageScore += 5; } // larger screens = desktop = older

        // Tech level correlation
        $tl = $profile['tech_level'] ?? 'basic';
        if ($tl === 'expert') { $ageScore -= 5; } // skews younger
        if ($tl === 'novice') { $ageScore += 10; } // skews older

        $ageRange = '25-34';
        if ($ageScore <= 20) $ageRange = '13-17';
        elseif ($ageScore <= 28) $ageRange = '18-24';
        elseif ($ageScore <= 38) $ageRange = '25-34';
        elseif ($ageScore <= 48) $ageRange = '35-44';
        elseif ($ageScore <= 58) $ageRange = '45-54';
        else $ageRange = '55+';

        // Profession inference
        $profession = 'general';
        $ext = strtolower($s['extensions_detected'] ?? '');
        if (strpos($ext, 'react devtools') !== false || strpos($ext, 'vue devtools') !== false || strpos($ext, 'redux') !== false) {
            $profession = 'software developer';
        } elseif ($tl === 'expert' && (strpos($os, 'linux') !== false)) {
            $profession = 'IT/engineering';
        } elseif (($profile['device_tier'] ?? '') === 'ultra' && $tl === 'advanced') {
            $profession = 'tech professional';
        } elseif (($profile['device_tier'] ?? '') === 'premium' && in_array($s['in_app_browser'] ?? '', ['linkedin'])) {
            $profession = 'business professional';
        } elseif (($profile['device_tier'] ?? '') === 'budget' && $ageScore <= 25) {
            $profession = 'student';
        }

        return [
            'likely_age_range' => $ageRange,
            'likely_profession' => $profession,
        ];
    }

    /**
     * INTERESTS — Infer from social platforms, behavior, device
     */
    private function inferInterests(array $sessions, array $profile): array {
        $interests = [];
        $s = $sessions[0];

        // From social platforms
        $socials = $this->pdo->prepare("SELECT `platform`,`logged_in`,`confidence` FROM `social_detection` WHERE `session_id`=? AND (`logged_in`=1 OR `confidence` IN ('high','definite'))");
        $socials->execute([$s['id']]);
        foreach ($socials->fetchAll() as $soc) {
            $p = $soc['platform'];
            $map = [
                'github' => 'coding', 'gitlab' => 'coding', 'stackoverflow' => 'coding',
                'linkedin' => 'professional networking', 'medium' => 'reading/writing',
                'spotify' => 'music', 'twitch' => 'gaming/streaming', 'discord' => 'gaming/community',
                'reddit' => 'community/discussion', 'pinterest' => 'design/lifestyle',
                'tiktok' => 'short-form video', 'youtube' => 'video content',
                'netflix' => 'movies/tv', 'disney_plus' => 'movies/tv', 'prime_video' => 'movies/tv',
                'notion' => 'productivity', 'dropbox' => 'cloud storage',
                'amazon' => 'online shopping', 'ebay' => 'online shopping', 'aliexpress' => 'online shopping',
                'instagram' => 'photography/social', 'facebook' => 'social networking',
                'twitter' => 'news/opinions', 'chatgpt' => 'AI/technology', 'claude_ai' => 'AI/technology',
            ];
            if (isset($map[$p])) $interests[] = $map[$p];
        }

        // From device
        if (!empty($s['gamepad_count'])) $interests[] = 'gaming';
        if (($profile['device_tier'] ?? '') === 'ultra') $interests[] = 'technology enthusiast';
        if (($profile['tech_level'] ?? '') === 'expert') $interests[] = 'technology';
        if (($profile['privacy_consciousness'] ?? '') === 'paranoid') $interests[] = 'privacy/security';

        $interests = array_unique($interests);
        return ['interest_tags' => json_encode(array_values($interests))];
    }

    private function calculateConfidence(array $sessions, array $profile): int {
        $score = 20; // base
        $score += min(20, count($sessions) * 5); // more sessions = more data
        $score += min(15, intval($profile['data_points_used'] ?? 0));
        if (!empty($profile['device_tier_signals'])) $score += 10;
        if (!empty($profile['tech_level_signals'])) $score += 10;
        if (intval($sessions[0]['duration_seconds'] ?? 0) > 10) $score += 10;
        if (($sessions[0]['gps_source'] ?? '') === 'browser') $score += 15;
        return min(100, $score);
    }

    private function getLatestBehavior(int $sessionId): ?array {
        $stmt = $this->pdo->prepare("SELECT `event_data` FROM `behavioral_data` WHERE `session_id`=? ORDER BY `id` DESC LIMIT 1");
        $stmt->execute([$sessionId]);
        $row = $stmt->fetch();
        if ($row && !empty($row['event_data'])) {
            return json_decode($row['event_data'], true) ?: [];
        }
        return null;
    }

    private function storeProfile(array $p): void {
        $fields = ['visitor_id','device_tier','device_tier_score','device_tier_signals','tech_level','tech_level_score','tech_level_signals','intent','intent_score','intent_signals','engagement_level','engagement_score','visit_pattern','active_hours','avg_session_duration','total_interactions','privacy_consciousness','privacy_score','privacy_signals','primary_ecosystem','ecosystem_signals','interest_tags','likely_age_range','likely_profession','confidence_score','data_points_used'];
        $cols = []; $vals = []; $dups = []; $params = [];
        foreach ($fields as $f) {
            if (isset($p[$f])) {
                $cols[] = "`{$f}`";
                $vals[] = "?";
                $dups[] = "`{$f}`=VALUES(`{$f}`)";
                $params[] = $p[$f];
            }
        }
        $cols[] = '`last_profiled`'; $vals[] = 'NOW()'; $dups[] = '`last_profiled`=NOW()';
        $sql = "INSERT INTO `visitor_profiles` (" . implode(',', $cols) . ") VALUES (" . implode(',', $vals) . ") ON DUPLICATE KEY UPDATE " . implode(',', $dups);
        $this->pdo->prepare($sql)->execute($params);
    }
}

class IdentityCorrelator {
    private $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    /**
     * Run correlation across all visitors to find same-person clusters
     */
    public function correlateAll(): array {
        $stats = ['clusters_found' => 0, 'visitors_linked' => 0, 'matches' => []];
        
        // Get all visitors with their latest session data
        $viRows = $this->pdo->query("SELECT vi.* FROM `visitor_identities` vi ORDER BY vi.id")->fetchAll(PDO::FETCH_ASSOC);
        $visitors = [];
        foreach ($viRows as $vi) {
            // Get latest session for this visitor
            $sStmt = $this->pdo->prepare("SELECT s.id FROM `visitor_sessions` vs JOIN `sessions` s ON vs.session_id=s.id WHERE vs.visitor_id=? ORDER BY s.started_at DESC LIMIT 1");
            $sStmt->execute([$vi['id']]);
            $sid = $sStmt->fetchColumn();
            if (!$sid) continue;
            
            // Fetch data from each table
            $v = $vi;
            $tables = ['fingerprint_data','device_data','browser_data','location_data'];
            foreach ($tables as $t) {
                try { $r = $this->pdo->prepare("SELECT * FROM `{$t}` WHERE `session_id`=?"); $r->execute([$sid]); $d = $r->fetch(PDO::FETCH_ASSOC); if ($d) $v = array_merge($v, $d); } catch (\Exception $e) {}
            }
            $visitors[] = $v;
        }

        // Update signatures for each visitor
        foreach ($visitors as &$v) {
            $v['screen_sig'] = ($v['screen_width'] ?? '') . 'x' . ($v['screen_height'] ?? '') . '@' . ($v['pixel_ratio'] ?? '');
            $v['gpu_sig'] = strtolower(substr($v['gpu_renderer'] ?? '', 0, 64));
            $v['hw_sig'] = ($v['hardware_concurrency'] ?? '') . '|' . ($v['device_memory'] ?? '');
            $v['tz_sig'] = $v['timezone'] ?? '';
            $v['lang_sig'] = $v['languages_list'] ?? '';

            // Store signatures
            try {
                $this->pdo->prepare("UPDATE `visitor_identities` SET `screen_signature`=?, `gpu_signature`=?, `timezone_signature`=?, `language_signature`=?, `fingerprint_stable`=? WHERE `id`=?")
                    ->execute([$v['screen_sig'], $v['gpu_sig'], $v['tz_sig'], $v['lang_sig'], $v['combined_fingerprint'] ?? null, $v['id']]);
            } catch (\Exception $e) {}
        }
        unset($v);

        // Compare every pair (O(n^2) but fine for <10K visitors)
        $n = count($visitors);
        $clusters = [];
        $visitorCluster = [];

        for ($i = 0; $i < $n; $i++) {
            for ($j = $i + 1; $j < $n; $j++) {
                $match = $this->compareVisitors($visitors[$i], $visitors[$j]);
                if ($match['confidence'] >= 60) {
                    $vid1 = $visitors[$i]['id']; $vid2 = $visitors[$j]['id'];

                    // Assign to cluster
                    $cid = $visitorCluster[$vid1] ?? $visitorCluster[$vid2] ?? 'cluster_' . substr(md5($vid1 . $vid2), 0, 12);
                    $visitorCluster[$vid1] = $cid;
                    $visitorCluster[$vid2] = $cid;

                    // Store in identity graph
                    try {
                        $this->pdo->prepare("INSERT INTO `identity_graph` (`cluster_id`,`visitor_id`,`match_type`,`match_confidence`,`matched_with_visitor_id`,`match_evidence`) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE `match_confidence`=VALUES(`match_confidence`),`match_evidence`=VALUES(`match_evidence`)")
                            ->execute([$cid, $vid1, $match['type'], $match['confidence'], $vid2, json_encode($match['evidence'])]);
                        $this->pdo->prepare("INSERT INTO `identity_graph` (`cluster_id`,`visitor_id`,`match_type`,`match_confidence`,`matched_with_visitor_id`,`match_evidence`) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE `match_confidence`=VALUES(`match_confidence`)")
                            ->execute([$cid, $vid2, $match['type'], $match['confidence'], $vid1, json_encode($match['evidence'])]);
                    } catch (\Exception $e) {}

                    // Update cluster_id on visitors
                    $this->pdo->prepare("UPDATE `visitor_identities` SET `cluster_id`=? WHERE `id` IN (?,?)")->execute([$cid, $vid1, $vid2]);

                    $stats['matches'][] = ['v1' => $vid1, 'v2' => $vid2, 'confidence' => $match['confidence'], 'type' => $match['type']];
                    $stats['visitors_linked'] += 2;
                }
            }
        }

        $stats['clusters_found'] = count(array_unique(array_values($visitorCluster)));
        return $stats;
    }

    private function compareVisitors(array $a, array $b): array {
        $confidence = 0; $evidence = []; $type = 'fingerprint';

        // Exact fingerprint match = very strong
        if (!empty($a['combined_fingerprint']) && $a['combined_fingerprint'] === $b['combined_fingerprint']) {
            $confidence += 50; $evidence[] = 'exact_fingerprint';
        }

        // Canvas hash match
        if (!empty($a['canvas_hash']) && $a['canvas_hash'] === $b['canvas_hash'] && $a['canvas_hash'] !== '00000000') {
            $confidence += 15; $evidence[] = 'canvas_match';
        }

        // WebGL hash match
        if (!empty($a['webgl_hash']) && $a['webgl_hash'] === $b['webgl_hash'] && $a['webgl_hash'] !== '00000000') {
            $confidence += 10; $evidence[] = 'webgl_match';
        }

        // Audio hash match
        if (!empty($a['audio_hash']) && $a['audio_hash'] === $b['audio_hash'] && $a['audio_hash'] !== '00000000') {
            $confidence += 10; $evidence[] = 'audio_match';
        }

        // Font hash match
        if (!empty($a['font_hash']) && $a['font_hash'] === $b['font_hash'] && $a['font_hash'] !== '00000000') {
            $confidence += 8; $evidence[] = 'font_match';
        }

        // Screen signature match
        if (!empty($a['screen_sig']) && $a['screen_sig'] === $b['screen_sig'] && $a['screen_sig'] !== 'x@') {
            $confidence += 8; $evidence[] = 'screen_match';
        }

        // GPU match
        if (!empty($a['gpu_sig']) && $a['gpu_sig'] === $b['gpu_sig'] && strlen($a['gpu_sig']) > 5) {
            $confidence += 10; $evidence[] = 'gpu_match';
        }

        // Hardware signature (CPU + RAM)
        if (!empty($a['hw_sig']) && $a['hw_sig'] === $b['hw_sig'] && $a['hw_sig'] !== '|') {
            $confidence += 5; $evidence[] = 'hardware_match';
        }

        // Timezone + language combo
        if (!empty($a['tz_sig']) && $a['tz_sig'] === $b['tz_sig']) {
            $confidence += 3; $evidence[] = 'timezone_match';
        }
        if (!empty($a['lang_sig']) && $a['lang_sig'] === $b['lang_sig'] && strlen($a['lang_sig']) > 3) {
            $confidence += 3; $evidence[] = 'language_match';
        }

        // Same IP = moderate signal (could be shared network)
        if (!empty($a['ip_address']) && $a['ip_address'] === $b['ip_address']) {
            $confidence += 5; $evidence[] = 'same_ip'; $type = 'ip_pattern';
        }

        // Determine match type
        if (in_array('exact_fingerprint', $evidence)) $type = 'fingerprint';
        elseif (count(array_intersect($evidence, ['canvas_match','webgl_match','audio_match'])) >= 2) $type = 'cross_device';
        elseif (in_array('same_ip', $evidence)) $type = 'ip_pattern';
        else $type = 'behavioral';

        return ['confidence' => min(100, $confidence), 'evidence' => $evidence, 'type' => $type];
    }
}
