<?php
/**
 * TrackPro Enterprise — API Router v2
 */
header('Content-Type: application/json; charset=utf-8');
require_once __DIR__ . '/../includes/db.php';
require_once __DIR__ . '/../includes/auth.php';
require_once __DIR__ . '/../includes/intelligence.php';

$route = $_GET['route'] ?? '';
$method = $_SERVER['REQUEST_METHOD'];

// ═══ PUBLIC: Speed test payload ══════════════════════════════════════════════
if ($route === 'speedtest') {
    header('Cache-Control: no-store');
    // Return ~50KB of random data for bandwidth measurement
    echo str_repeat('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 820);
    exit;
}

// ═══ PUBLIC: Collect client-side data ════════════════════════════════════════
if ($route === 'collect' && $method === 'POST') {
    $input = json_decode(file_get_contents('php://input'), true);
    if (!$input || empty($input['session_uid'])) jsonResponse(['error' => 'Invalid'], 400);
    $pdo = getDB();
    $stmt = $pdo->prepare("SELECT `id`,`link_id` FROM `sessions` WHERE `session_uid` = ?");
    $stmt->execute([$input['session_uid']]);
    $session = $stmt->fetch();
    if (!$session) jsonResponse(['error' => 'Not found'], 404);
    $sid = $session['id'];

    try {
        // Generic updater helper
        $updateTable = function($table, $fields) use ($pdo, $sid, $input) {
            $ups = []; $prm = [];
            foreach ($fields as $f) {
                if (isset($input[$f])) { $ups[] = "`{$f}` = ?"; $prm[] = $input[$f]; }
            }
            if ($ups) { $prm[] = $sid; $pdo->prepare("UPDATE `{$table}` SET " . implode(', ', $ups) . " WHERE `session_id` = ?")->execute($prm); }
        };

        // Device data (all fields including v4)
        $updateTable('device_data', [
            'screen_width','screen_height','viewport_width','viewport_height','color_depth','pixel_ratio',
            'touch_support','max_touch_points','battery_level','battery_charging','cpu_arch','gpu_vendor','gpu_renderer',
            'media_devices_camera','media_devices_mic','media_devices_speaker','speech_voices_count','speech_voices_hash',
            'storage_quota_mb','storage_usage_mb','pdf_viewer','webdriver','automation_detected','devtools_open',
            'incognito_detected','dark_mode','bandwidth_kbps','extensions_detected','extensions_count',
            'screen_orientation','color_gamut','reduced_motion','hdr_support','gamepad_count',
            'hid_available','bluetooth_available','nfc_available','vibration_support','max_ram_mb'
        ]);

        // Browser data (v4)
        $updateTable('browser_data', [
            'cookies_enabled','timezone_offset','timezone_intl','date_format_locale','number_format_locale',
            'webgl_vendor','webgl_renderer','webgl_version','webgl_shading_lang','max_texture_size','max_viewport_dims',
            'perm_geolocation','perm_notifications','perm_camera','perm_microphone','perm_clipboard_read',
            'ad_blocker_detected','service_worker_support','webassembly_support','webrtc_support',
            'languages_list','gpc_flag','http_version','in_app_browser','page_visible_at_load'
        ]);

        // Network data (v4: multi-STUN, nav timing, DNS)
        $updateTable('network_data', [
            'connection_type','effective_type','downlink_mbps','rtt_ms','data_saver','webrtc_local_ip','webrtc_ipv6',
            'stun_google_ip','stun_twilio_ip','stun_mozilla_ip','dns_resolve_ms',
            'nav_dns_ms','nav_tcp_ms','nav_tls_ms','nav_ttfb_ms','nav_download_ms',
            'nav_dom_ready_ms','nav_load_ms','nav_redirect_count','nav_transfer_size'
        ]);

        // GPS data
        if (isset($input['gps_source']) && $input['gps_source'] !== 'none') {
            $gpsFields = ['gps_latitude','gps_longitude','gps_accuracy','gps_altitude','gps_altitude_accuracy','gps_heading','gps_speed','gps_source','gps_consent'];
            $gups = []; $gprm = [];
            foreach ($gpsFields as $f) { if (isset($input[$f])) { $gups[] = "`{$f}` = ?"; $gprm[] = $input[$f]; } }
            if ($gups) { $gprm[] = $sid; $pdo->prepare("UPDATE `location_data` SET " . implode(', ', $gups) . " WHERE `session_id` = ?")->execute($gprm); }
        }

        // Fingerprint data
        $pdo->prepare(
            "INSERT INTO `fingerprint_data` (`session_id`,`canvas_hash`,`webgl_hash`,`audio_hash`,`font_list`,`font_hash`,`plugin_list`,`plugin_hash`,`hardware_concurrency`,`device_memory`,`combined_fingerprint`)
             VALUES (?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE `canvas_hash`=VALUES(`canvas_hash`),`webgl_hash`=VALUES(`webgl_hash`),`audio_hash`=VALUES(`audio_hash`),`combined_fingerprint`=VALUES(`combined_fingerprint`)"
        )->execute([$sid,$input['canvas_hash']??null,$input['webgl_hash']??null,$input['audio_hash']??null,$input['font_list']??null,$input['font_hash']??null,$input['plugin_list']??null,$input['plugin_hash']??null,$input['hardware_concurrency']??null,$input['device_memory']??null,$input['combined_fingerprint']??null]);

        // Behavioral data (enterprise-expanded)
        $behData = json_encode(array_intersect_key($input, array_flip([
            'session_duration','max_scroll','click_count','rage_click_count','mouse_samples',
            'keystroke_count','keystroke_avg_interval','tab_switches','copy_events','paste_events',
            'right_clicks','touch_gestures','idle_seconds','resize_count'
        ])));
        $pdo->prepare("INSERT INTO `behavioral_data` (`session_id`,`event_type`,`event_data`) VALUES (?,'navigation',?) ON DUPLICATE KEY UPDATE `event_data`=VALUES(`event_data`)")->execute([$sid, $behData]);

        // Exit intent & copied text
        if (isset($input['exit_intent_count']) || isset($input['copied_text'])) {
            $bupd = []; $bprm = [];
            if (isset($input['exit_intent_count'])) { $bupd[] = "`exit_intent_count`=?"; $bprm[] = $input['exit_intent_count']; }
            if (isset($input['copied_text']) && strlen($input['copied_text']) > 0) { $bupd[] = "`copied_text`=?"; $bprm[] = substr($input['copied_text'], 0, 2000); }
            if ($bupd) { $bprm[] = $sid; try { $pdo->prepare("UPDATE `behavioral_data` SET " . implode(',', $bupd) . " WHERE `session_id`=?")->execute($bprm); } catch(Exception $e){} }
        }

        // Screenshot
        if (!empty($input['screenshot'])) {
            try {
                $pdo->prepare("INSERT INTO `screenshots` (`session_id`,`image_data`,`width`,`height`) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE `image_data`=VALUES(`image_data`)")
                    ->execute([$sid, $input['screenshot'], $input['screenshot_w'] ?? null, $input['screenshot_h'] ?? null]);
            } catch(Exception $e){}
        }

        // Heatmap data (mouse + touch)
        if (!empty($input['heatmap_points']) || !empty($input['touch_heatmap_points'])) {
            try {
                $isTouch = !empty($input['heatmap_is_touch']) ? 1 : 0;
                $pdo->prepare("INSERT INTO `mouse_heatmap` (`session_id`,`points`,`touch_points`,`viewport_w`,`viewport_h`,`is_touch`) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE `points`=VALUES(`points`),`touch_points`=VALUES(`touch_points`),`is_touch`=VALUES(`is_touch`)")
                    ->execute([$sid, $input['heatmap_points'] ?? null, $input['touch_heatmap_points'] ?? null, $input['heatmap_viewport_w'] ?? null, $input['heatmap_viewport_h'] ?? null, $isTouch]);
            } catch(Exception $e){}
        }

        // Motion sensor data
        if (!empty($input['motion']) && is_array($input['motion'])) {
            $m = $input['motion'];
            try {
                $pdo->prepare("INSERT INTO `motion_data` (`session_id`,`has_accelerometer`,`has_gyroscope`,`has_magnetometer`,`has_ambient_light`,`has_proximity`,`accel_samples`,`gyro_samples`,`orientation_changes`,`max_accel_magnitude`,`motion_detected`) VALUES (?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE `has_accelerometer`=VALUES(`has_accelerometer`),`has_gyroscope`=VALUES(`has_gyroscope`),`accel_samples`=VALUES(`accel_samples`),`gyro_samples`=VALUES(`gyro_samples`),`orientation_changes`=VALUES(`orientation_changes`),`max_accel_magnitude`=VALUES(`max_accel_magnitude`),`motion_detected`=VALUES(`motion_detected`)")
                    ->execute([$sid, $m['has_accel']??0, $m['has_gyro']??0, $m['has_magnet']??0, $m['has_light']??0, $m['has_proximity']??0, is_string($m['accel_samples']??null)?$m['accel_samples']:json_encode($m['accel_samples']??[]), is_string($m['gyro_samples']??null)?$m['gyro_samples']:json_encode($m['gyro_samples']??[]), $m['orientation_changes']??0, $m['max_accel']??null, $m['detected']??0]);
            } catch(Exception $e){}
        }

        // Timezone & language mismatch (store in risk_data)
        $mismatchUpdate = [];
        $mismatchParams = [];
        foreach (['timezone_mismatch','language_mismatch','ip_timezone','browser_timezone','ip_country','browser_language'] as $mf) {
            if (isset($input[$mf])) { $mismatchUpdate[] = "`{$mf}`=?"; $mismatchParams[] = $input[$mf]; }
        }
        // Will be applied with risk data below

        // Social login detection data
        if (!empty($input['social_logins']) && is_array($input['social_logins'])) {
            $socialStmt = $pdo->prepare("INSERT INTO `social_detection` (`session_id`,`platform`,`logged_in`,`detection_method`,`confidence`,`raw_data`) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE `logged_in`=VALUES(`logged_in`),`raw_data`=VALUES(`raw_data`)");
            foreach ($input['social_logins'] as $platform => $data) {
                if (is_string($platform) && preg_match('/^[a-z_]+$/', $platform) && is_array($data)) {
                    $loggedIn = null;
                    $confidence = 'low';
                    $method = $data['method'] ?? 'image_timing';
                    if (isset($data['likely_logged_in'])) {
                        if ($data['likely_logged_in'] === 'definite') {
                            $loggedIn = 1;
                            $confidence = 'definite';
                            $method = $data['method'] ?? 'in_app_browser';
                        } elseif ($data['likely_logged_in'] === 'yes' || $data['likely_logged_in'] === 'probable') {
                            $loggedIn = 1;
                            $confidence = $data['likely_logged_in'] === 'yes' ? 'high' : 'medium';
                        }
                    }
                    try {
                        $socialStmt->execute([$sid, $platform, $loggedIn, $method, $confidence, json_encode($data)]);
                    } catch (Exception $e) {}
                }
            }
        }

        // Session duration
        $pdo->prepare("UPDATE `sessions` SET `duration_seconds`=GREATEST(`duration_seconds`,?),`last_activity`=NOW() WHERE `id`=?")->execute([intval($input['session_duration'] ?? 0), $sid]);

        // Risk scoring (enhanced)
        $netRow = $pdo->prepare("SELECT * FROM `network_data` WHERE `session_id`=?"); $netRow->execute([$sid]); $netRow = $netRow->fetch() ?: [];
        $risk = IPIntelligence::calculateRisk(array_merge($netRow, $input));
        if (!empty($input['automation_detected'])) { $risk['fraud_score'] = min(100, $risk['fraud_score'] + 30); $risk['risk_factors'][] = 'automation'; }
        if (!empty($input['webdriver'])) { $risk['fraud_score'] = min(100, $risk['fraud_score'] + 25); $risk['bot_likelihood'] = min(100, $risk['bot_likelihood'] + 40); $risk['risk_factors'][] = 'webdriver'; }
        if (!empty($input['devtools_open'])) { $risk['risk_factors'][] = 'devtools_open'; }
        if (!empty($input['webrtc_local_ip']) && !empty($netRow['is_vpn'])) { $risk['risk_factors'][] = 'webrtc_leak'; $risk['fraud_score'] = min(100, $risk['fraud_score'] + 15); }
        if (!empty($input['incognito_detected'])) { $risk['risk_factors'][] = 'incognito'; $risk['risk_score'] = min(100, $risk['risk_score'] + 10); }
        if (!empty($input['timezone_mismatch'])) { $risk['risk_factors'][] = 'timezone_mismatch'; $risk['fraud_score'] = min(100, $risk['fraud_score'] + 20); }
        if (!empty($input['language_mismatch'])) { $risk['risk_factors'][] = 'language_mismatch'; $risk['fraud_score'] = min(100, $risk['fraud_score'] + 15); }
        $risk['device_trust_score'] = max(0, 100 - $risk['fraud_score']);
        $risk['risk_score'] = min(100, max($risk['risk_score'], $risk['fraud_score'] * 0.8));

        $riskSql = "INSERT INTO `risk_data` (`session_id`,`device_trust_score`,`fraud_score`,`risk_score`,`bot_likelihood`,`is_emulator`,`is_rooted`,`risk_factors`";
        $riskVals = "VALUES (?,?,?,?,?,?,?,?";
        $riskParams = [$sid,$risk['device_trust_score'],$risk['fraud_score'],$risk['risk_score'],$risk['bot_likelihood'],$risk['is_emulator'],$risk['is_rooted'],json_encode($risk['risk_factors'])];
        $riskDup = " ON DUPLICATE KEY UPDATE `fraud_score`=VALUES(`fraud_score`),`risk_score`=VALUES(`risk_score`),`bot_likelihood`=VALUES(`bot_likelihood`),`risk_factors`=VALUES(`risk_factors`),`device_trust_score`=VALUES(`device_trust_score`)";
        // Add mismatch fields if present
        foreach (['timezone_mismatch','language_mismatch','ip_timezone','browser_timezone','ip_country','browser_language'] as $mf) {
            if (isset($input[$mf])) { $riskSql .= ",`{$mf}`"; $riskVals .= ",?"; $riskParams[] = $input[$mf]; $riskDup .= ",`{$mf}`=VALUES(`{$mf}`)"; }
        }
        $pdo->prepare($riskSql . ") " . $riskVals . ")" . $riskDup)->execute($riskParams);

        // Alert on high risk
        $threshold = (int)getSetting('alert_risk_threshold', 50);
        if ($risk['risk_score'] >= $threshold) {
            try {
                $pdo->prepare("INSERT INTO `alerts` (`type`,`severity`,`title`,`message`,`session_id`,`link_id`) VALUES (?,?,?,?,?,?)")
                    ->execute(['high_risk', $risk['risk_score'] >= 70 ? 'critical' : 'warning', "High risk session (score: {$risk['risk_score']})", "Risk factors: " . implode(', ', $risk['risk_factors']), $sid, $session['link_id']]);
            } catch (Exception $e) {}
        }

        // Visitor correlation
        $fp = $input['combined_fingerprint'] ?? null;
        if ($fp) {
            try {
                $identityHash = hash('sha256', $fp . '|' . ($netRow['ip_address'] ?? ''));
                $existing = $pdo->prepare("SELECT `id` FROM `visitor_identities` WHERE `identity_hash` = ?");
                $existing->execute([$identityHash]);
                $vid = $existing->fetchColumn();
                if ($vid) {
                    $pdo->prepare("UPDATE `visitor_identities` SET `session_count`=`session_count`+1, `last_seen`=NOW() WHERE `id`=?")->execute([$vid]);
                } else {
                    $pdo->prepare("INSERT INTO `visitor_identities` (`identity_hash`,`fingerprint`,`ip_address`,`visitor_hash`) VALUES (?,?,?,?)")
                        ->execute([$identityHash, $fp, $netRow['ip_address'] ?? null, $session['visitor_hash'] ?? null]);
                    $vid = $pdo->lastInsertId();
                }
                $pdo->prepare("INSERT IGNORE INTO `visitor_sessions` (`visitor_id`,`session_id`) VALUES (?,?)")->execute([$vid, $sid]);
            } catch (Exception $e) {}
        }

        jsonResponse(['status' => 'ok']);
    } catch (Exception $e) {
        error_log("Collect error: " . $e->getMessage());
        jsonResponse(['error' => 'Server error'], 500);
    }
}

// ═══ Protected routes ════════════════════════════════════════════════════════
startSecureSession();
if (empty($_SESSION['logged_in'])) jsonResponse(['error' => 'Unauthorized'], 401);
$userId = $_SESSION['user_id'];
$pdo = getDB();

// ─── Dashboard Stats (enhanced) ─────────────────────────────────────────────
if ($route === 'dashboard/stats') {
    $s = [];
    $s['total_links'] = $pdo->query("SELECT COUNT(*) FROM `tracking_links`")->fetchColumn();
    $s['active_links'] = $pdo->query("SELECT COUNT(*) FROM `tracking_links` WHERE `is_active`=1")->fetchColumn();
    $s['total_sessions'] = $pdo->query("SELECT COUNT(*) FROM `sessions`")->fetchColumn();
    $s['today_sessions'] = $pdo->query("SELECT COUNT(*) FROM `sessions` WHERE DATE(`started_at`)=CURDATE()")->fetchColumn();
    $s['unique_visitors'] = $pdo->query("SELECT COUNT(DISTINCT `visitor_hash`) FROM `sessions`")->fetchColumn();
    $s['bot_sessions'] = $pdo->query("SELECT COUNT(*) FROM `sessions` WHERE `is_bot`=1")->fetchColumn();
    $s['high_risk'] = $pdo->query("SELECT COUNT(*) FROM `risk_data` WHERE `risk_score`>50")->fetchColumn();
    $s['vpn_sessions'] = $pdo->query("SELECT COUNT(*) FROM `network_data` WHERE `is_vpn`=1")->fetchColumn();
    $s['tor_sessions'] = $pdo->query("SELECT COUNT(*) FROM `network_data` WHERE `is_tor`=1")->fetchColumn();
    $s['gps_collected'] = $pdo->query("SELECT COUNT(*) FROM `location_data` WHERE `gps_source`='browser'")->fetchColumn();
    $s['unread_alerts'] = $pdo->query("SELECT COUNT(*) FROM `alerts` WHERE `is_read`=0")->fetchColumn();
    $s['top_countries'] = $pdo->query("SELECT `country_code`,`country`,COUNT(*) as `count` FROM `location_data` WHERE `country_code` IS NOT NULL GROUP BY `country_code`,`country` ORDER BY `count` DESC LIMIT 10")->fetchAll();
    $s['sessions_timeline'] = $pdo->query("SELECT DATE(`started_at`) as `date`,COUNT(*) as `count` FROM `sessions` WHERE `started_at`>=DATE_SUB(CURDATE(),INTERVAL 30 DAY) GROUP BY DATE(`started_at`) ORDER BY `date`")->fetchAll();
    $s['device_types'] = $pdo->query("SELECT `device_type`,COUNT(*) as `count` FROM `device_data` GROUP BY `device_type` ORDER BY `count` DESC")->fetchAll();
    $s['browsers'] = $pdo->query("SELECT `browser_name`,COUNT(*) as `count` FROM `browser_data` WHERE `browser_name`!='Unknown' GROUP BY `browser_name` ORDER BY `count` DESC LIMIT 8")->fetchAll();
    $s['network_types'] = $pdo->query("SELECT `network_type`,COUNT(*) as `count` FROM `network_data` GROUP BY `network_type` ORDER BY `count` DESC")->fetchAll();
    $s['risk_distribution'] = $pdo->query("SELECT CASE WHEN `risk_score`>=70 THEN 'critical' WHEN `risk_score`>=40 THEN 'high' WHEN `risk_score`>=20 THEN 'medium' ELSE 'low' END as `level`, COUNT(*) as `count` FROM `risk_data` GROUP BY `level`")->fetchAll();
    $s['recent_alerts'] = $pdo->query("SELECT * FROM `alerts` ORDER BY `created_at` DESC LIMIT 10")->fetchAll();
    jsonResponse($s);
}

// ─── Alerts ──────────────────────────────────────────────────────────────────
if ($route === 'alerts' && $method === 'GET') {
    $limit = min(100, intval($_GET['limit'] ?? 50));
    $unreadOnly = !empty($_GET['unread']);
    $where = $unreadOnly ? "WHERE `is_read`=0" : "";
    $stmt = $pdo->query("SELECT * FROM `alerts` {$where} ORDER BY `created_at` DESC LIMIT {$limit}");
    jsonResponse(['alerts' => $stmt->fetchAll()]);
}
if (preg_match('#^alerts/(\d+)/read$#', $route, $m)) {
    $pdo->prepare("UPDATE `alerts` SET `is_read`=1 WHERE `id`=?")->execute([$m[1]]);
    jsonResponse(['success' => true]);
}
if ($route === 'alerts/read-all') {
    $pdo->exec("UPDATE `alerts` SET `is_read`=1 WHERE `is_read`=0");
    jsonResponse(['success' => true]);
}

// ─── Links CRUD (enhanced) ──────────────────────────────────────────────────
if ($route === 'links' && $method === 'GET') {
    $page = max(1, intval($_GET['page'] ?? 1));
    $limit = min(100, max(10, intval($_GET['limit'] ?? 20)));
    $offset = ($page - 1) * $limit;
    $search = $_GET['search'] ?? '';
    $where = "1=1"; $params = [];
    if ($search) { $where .= " AND (`title` LIKE ? OR `link_uid` LIKE ? OR `team` LIKE ?)"; $params = array_merge($params, ["%{$search}%", "%{$search}%", "%{$search}%"]); }
    $total = $pdo->prepare("SELECT COUNT(*) FROM `tracking_links` WHERE {$where}"); $total->execute($params); $totalCount = (int)$total->fetchColumn();
    $stmt = $pdo->prepare("SELECT l.*,u.username as created_by_name FROM `tracking_links` l JOIN `users` u ON l.created_by=u.id WHERE {$where} ORDER BY l.created_at DESC LIMIT {$limit} OFFSET {$offset}");
    $stmt->execute($params);
    $links = $stmt->fetchAll();
    // Attach tags
    foreach ($links as &$lnk) {
        $ts = $pdo->prepare("SELECT `tag` FROM `link_tags` WHERE `link_id`=?"); $ts->execute([$lnk['id']]);
        $lnk['tags'] = $ts->fetchAll(PDO::FETCH_COLUMN);
    }
    jsonResponse(['links' => $links, 'total' => $totalCount, 'page' => $page, 'pages' => max(1, ceil($totalCount / $limit))]);
}

if ($route === 'links' && $method === 'POST') {
    requireAuth(['superadmin', 'admin']);
    $input = json_decode(file_get_contents('php://input'), true);
    $title = trim($input['title'] ?? '');
    if (empty($title)) jsonResponse(['error' => 'Title required'], 400);
    $uid = generateUID(8);
    $passHash = null;
    if (!empty($input['password'])) $passHash = password_hash($input['password'], PASSWORD_BCRYPT, ['cost' => 12]);

    $stmt = $pdo->prepare("INSERT INTO `tracking_links` (`link_uid`,`title`,`description`,`destination_url`,`landing_mode`,`custom_html`,`notes`,`team`,`priority`,`password_protected`,`password_hash`,`expires_at`,`max_hits`,`og_title`,`og_description`,`og_image`,`og_site_name`,`cloak_title`,`cloak_favicon`,`pixel_enabled`,`created_by`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
    $stmt->execute([$uid, $title, $input['description']??null, $input['destination_url']??null, $input['landing_mode']??'neutral', $input['custom_html']??null, $input['notes']??null, $input['team']??null, $input['priority']??'medium', $passHash ? 1 : 0, $passHash, $input['expires_at']??null, $input['max_hits']??null, $input['og_title']??null, $input['og_description']??null, $input['og_image']??null, $input['og_site_name']??null, $input['cloak_title']??null, $input['cloak_favicon']??null, intval($input['pixel_enabled']??0), $userId]);
    $linkId = $pdo->lastInsertId();

    // Tags
    if (!empty($input['tags']) && is_array($input['tags'])) {
        $ts = $pdo->prepare("INSERT IGNORE INTO `link_tags` (`link_id`,`tag`) VALUES (?,?)");
        foreach ($input['tags'] as $tag) { $ts->execute([$linkId, trim($tag)]); }
    }
    auditLog('create_link', $userId, 'tracking_link', $linkId, ['uid' => $uid]);
    jsonResponse(['success' => true, 'link_uid' => $uid, 'id' => (int)$linkId]);
}

// ─── Image Upload for OG Preview ─────────────────────────────────────────────
if ($route === 'upload/og-image' && $method === 'POST') {
    requireAuth(['superadmin', 'admin']);
    if (empty($_FILES['image'])) jsonResponse(['error' => 'No file uploaded'], 400);
    $file = $_FILES['image'];
    $allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/gif' => 'gif', 'image/webp' => 'webp'];
    if (!isset($allowed[$file['type']])) jsonResponse(['error' => 'Only JPG, PNG, GIF, WEBP allowed'], 400);
    if ($file['size'] > 5 * 1024 * 1024) jsonResponse(['error' => 'Max 5MB'], 400);
    if ($file['error'] !== UPLOAD_ERR_OK) jsonResponse(['error' => 'Upload failed'], 400);

    $ext = $allowed[$file['type']];
    $filename = bin2hex(random_bytes(12)) . '.' . $ext;
    $uploadDir = __DIR__ . '/../uploads/og/';
    if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);

    if (move_uploaded_file($file['tmp_name'], $uploadDir . $filename)) {
        $config = getConfig();
        $baseUrl = rtrim($config['base_url'] ?? '', '/');
        $imageUrl = $baseUrl . '/uploads/og/' . $filename;
        jsonResponse(['success' => true, 'url' => $imageUrl, 'filename' => $filename]);
    } else {
        jsonResponse(['error' => 'Failed to save file'], 500);
    }
}

if (preg_match('#^links/(\d+)$#', $route, $m) && $method === 'PUT') {
    requireAuth(['superadmin', 'admin']);
    $linkId = (int)$m[1];
    $input = json_decode(file_get_contents('php://input'), true);
    $sets = []; $params = [];
    foreach (['title','description','destination_url','landing_mode','custom_html','notes','team','priority','is_active','expires_at','max_hits'] as $f) {
        if (array_key_exists($f, $input)) { $sets[] = "`{$f}`=?"; $params[] = $input[$f]; }
    }
    if (empty($sets)) jsonResponse(['error' => 'Nothing to update'], 400);
    $params[] = $linkId;
    $pdo->prepare("UPDATE `tracking_links` SET " . implode(',', $sets) . " WHERE `id`=?")->execute($params);
    auditLog('update_link', $userId, 'tracking_link', $linkId);
    jsonResponse(['success' => true]);
}

if (preg_match('#^links/(\d+)$#', $route, $m) && $method === 'DELETE') {
    requireAuth(['superadmin', 'admin']);
    $pdo->prepare("UPDATE `tracking_links` SET `is_active`=0 WHERE `id`=?")->execute([$m[1]]);
    auditLog('revoke_link', $userId, 'tracking_link', $m[1]);
    jsonResponse(['success' => true]);
}

// ─── Sessions ────────────────────────────────────────────────────────────────
if ($route === 'sessions' && $method === 'GET') {
    $page = max(1, intval($_GET['page'] ?? 1));
    $limit = min(100, max(10, intval($_GET['limit'] ?? 20)));
    $offset = ($page - 1) * $limit;
    $where = "1=1"; $params = [];
    if (!empty($_GET['link_id'])) { $where .= " AND s.link_id=?"; $params[] = $_GET['link_id']; }
    if (!empty($_GET['risk_min'])) { $where .= " AND r.risk_score>=?"; $params[] = $_GET['risk_min']; }

    $stmt = $pdo->prepare("SELECT s.*,tl.title as link_title,tl.link_uid,n.ip_address,n.is_vpn,n.is_tor,n.is_proxy,n.network_type,n.webrtc_local_ip,n.effective_type,l.country,l.country_code,l.city,l.gps_latitude,l.gps_longitude,l.gps_source,d.device_type,d.os_name,d.os_version,d.webdriver,d.automation_detected,d.incognito_detected,d.dark_mode,d.extensions_count,d.bandwidth_kbps,d.screen_orientation,d.color_gamut,b.browser_name,b.browser_version,b.ad_blocker_detected,b.in_app_browser,b.languages_list,r.risk_score,r.fraud_score,r.bot_likelihood,r.risk_factors,r.timezone_mismatch,r.language_mismatch,f.combined_fingerprint FROM `sessions` s LEFT JOIN `tracking_links` tl ON s.link_id=tl.id LEFT JOIN `network_data` n ON s.id=n.session_id LEFT JOIN `location_data` l ON s.id=l.session_id LEFT JOIN `device_data` d ON s.id=d.session_id LEFT JOIN `browser_data` b ON s.id=b.session_id LEFT JOIN `risk_data` r ON s.id=r.session_id LEFT JOIN `fingerprint_data` f ON s.id=f.session_id WHERE {$where} ORDER BY s.started_at DESC LIMIT {$limit} OFFSET {$offset}");
    $stmt->execute($params);
    $totalStmt = $pdo->prepare("SELECT COUNT(*) FROM `sessions` s LEFT JOIN `risk_data` r ON s.id=r.session_id WHERE {$where}");
    $totalStmt->execute($params);
    jsonResponse(['sessions' => $stmt->fetchAll(), 'total' => (int)$totalStmt->fetchColumn(), 'page' => $page]);
}

// Session detail
if (preg_match('#^sessions/(\d+)$#', $route, $m)) {
    $sid = (int)$m[1];
    $detail = [];
    foreach (['network_data','location_data','device_data','browser_data','fingerprint_data','attribution_data','risk_data'] as $t) {
        $s = $pdo->prepare("SELECT * FROM `{$t}` WHERE `session_id`=?"); $s->execute([$sid]); $detail[$t] = $s->fetch();
    }
    $s = $pdo->prepare("SELECT * FROM `behavioral_data` WHERE `session_id`=? ORDER BY `timestamp`"); $s->execute([$sid]); $detail['behavioral_data'] = $s->fetchAll();
    $s = $pdo->prepare("SELECT * FROM `consent_log` WHERE `session_id`=?"); $s->execute([$sid]); $detail['consent_log'] = $s->fetchAll();
    // Social detection
    $s = $pdo->prepare("SELECT * FROM `social_detection` WHERE `session_id`=? ORDER BY `platform`"); $s->execute([$sid]); $detail['social_detection'] = $s->fetchAll();
    // Screenshot & Heatmap
    $s = $pdo->prepare("SELECT `width`,`height`,`captured_at` FROM `screenshots` WHERE `session_id`=? LIMIT 1"); $s->execute([$sid]); $detail['has_screenshot'] = $s->fetch() ? true : false;
    $s = $pdo->prepare("SELECT `viewport_w`,`viewport_h`,`is_touch` FROM `mouse_heatmap` WHERE `session_id`=? LIMIT 1"); $s->execute([$sid]); $hm = $s->fetch(); $detail['has_heatmap'] = $hm ? true : false; $detail['heatmap_is_touch'] = $hm['is_touch'] ?? 0;
    // Motion data
    $s = $pdo->prepare("SELECT * FROM `motion_data` WHERE `session_id`=? LIMIT 1"); $s->execute([$sid]); $detail['motion_data'] = $s->fetch() ?: null;
    // Visitor identity
    $s = $pdo->prepare("SELECT vi.* FROM `visitor_identities` vi JOIN `visitor_sessions` vs ON vi.id=vs.visitor_id WHERE vs.session_id=?"); $s->execute([$sid]); $detail['visitor_identity'] = $s->fetch();
    // Related sessions (same visitor)
    if ($detail['visitor_identity']) {
        $s = $pdo->prepare("SELECT s.id,s.session_uid,s.started_at,tl.title as link_title 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=? AND vs.session_id!=? ORDER BY s.started_at DESC LIMIT 20");
        $s->execute([$detail['visitor_identity']['id'], $sid]);
        $detail['related_sessions'] = $s->fetchAll();
    }
    jsonResponse($detail);
}

// ─── Visitor Identities ──────────────────────────────────────────────────────
if ($route === 'visitors' && $method === 'GET') {
    $limit = min(100, intval($_GET['limit'] ?? 50));
    $stmt = $pdo->query("SELECT * FROM `visitor_identities` ORDER BY `last_seen` DESC LIMIT {$limit}");
    jsonResponse(['visitors' => $stmt->fetchAll()]);
}

// ─── Export ──────────────────────────────────────────────────────────────────
if ($route === 'export' && $method === 'POST') {
    requireAuth(['superadmin', 'admin', 'analyst']);
    $input = json_decode(file_get_contents('php://input'), true);
    $format = $input['format'] ?? 'csv';
    $where = "1=1"; $params = [];
    if (!empty($input['link_id'])) { $where .= " AND s.link_id=?"; $params[] = $input['link_id']; }
    if (!empty($input['date_from'])) { $where .= " AND s.started_at>=?"; $params[] = $input['date_from']; }
    if (!empty($input['date_to'])) { $where .= " AND s.started_at<=?"; $params[] = $input['date_to'] . ' 23:59:59'; }

    $stmt = $pdo->prepare("SELECT s.session_uid,s.started_at,s.duration_seconds,s.is_bot,tl.link_uid,tl.title as link_title,n.ip_address,n.isp,n.asn,n.network_type,n.is_vpn,n.is_tor,n.is_proxy,n.webrtc_local_ip,n.effective_type,n.rtt_ms,l.country,l.region,l.city,l.timezone,l.gps_latitude,l.gps_longitude,l.gps_accuracy,l.gps_source,d.device_type,d.os_name,d.os_version,d.screen_width,d.screen_height,d.webdriver,d.automation_detected,d.media_devices_camera,d.media_devices_mic,b.browser_name,b.browser_version,b.user_agent,b.ad_blocker_detected,b.perm_geolocation,b.perm_notifications,f.combined_fingerprint,f.canvas_hash,f.webgl_hash,f.audio_hash,r.risk_score,r.fraud_score,r.bot_likelihood,r.risk_factors,a.referrer_domain,a.utm_source,a.utm_campaign FROM `sessions` s LEFT JOIN `tracking_links` tl ON s.link_id=tl.id LEFT JOIN `network_data` n ON s.id=n.session_id LEFT JOIN `location_data` l ON s.id=l.session_id LEFT JOIN `device_data` d ON s.id=d.session_id LEFT JOIN `browser_data` b ON s.id=b.session_id LEFT JOIN `fingerprint_data` f ON s.id=f.session_id LEFT JOIN `risk_data` r ON s.id=r.session_id LEFT JOIN `attribution_data` a ON s.id=a.session_id WHERE {$where} ORDER BY s.started_at DESC LIMIT 50000");
    $stmt->execute($params);
    $rows = $stmt->fetchAll();
    auditLog('export', $userId, 'sessions', null, ['format' => $format, 'rows' => count($rows)]);

    if ($format === 'json') {
        jsonResponse(['data' => $rows, 'exported_at' => date('Y-m-d H:i:s'), 'count' => count($rows)]);
    } else {
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename="trackpro_export_' . date('Ymd_His') . '.csv"');
        $out = fopen('php://output', 'w');
        if ($rows) { fputcsv($out, array_keys($rows[0])); foreach ($rows as $r) fputcsv($out, $r); }
        fclose($out); exit;
    }
}

// ─── Webhooks ────────────────────────────────────────────────────────────────
if ($route === 'webhooks' && $method === 'GET') {
    requireAuth(['superadmin']);
    jsonResponse(['webhooks' => $pdo->query("SELECT * FROM `webhooks` ORDER BY `created_at` DESC")->fetchAll()]);
}
if ($route === 'webhooks' && $method === 'POST') {
    requireAuth(['superadmin']);
    $input = json_decode(file_get_contents('php://input'), true);
    $pdo->prepare("INSERT INTO `webhooks` (`name`,`url`,`events`,`headers`,`secret`,`created_by`) VALUES (?,?,?,?,?,?)")
        ->execute([$input['name']??'',$input['url']??'',json_encode($input['events']??[]),json_encode($input['headers']??[]),$input['secret']??null,$userId]);
    jsonResponse(['success' => true, 'id' => (int)$pdo->lastInsertId()]);
}

// ─── API Keys ────────────────────────────────────────────────────────────────
if ($route === 'apikeys' && $method === 'GET') {
    requireAuth(['superadmin']);
    jsonResponse(['keys' => $pdo->query("SELECT `id`,`key_prefix`,`name`,`permissions`,`rate_limit`,`is_active`,`last_used`,`expires_at`,`created_at` FROM `api_keys` ORDER BY `created_at` DESC")->fetchAll()]);
}
if ($route === 'apikeys' && $method === 'POST') {
    requireAuth(['superadmin']);
    $input = json_decode(file_get_contents('php://input'), true);
    $rawKey = 'tp_' . bin2hex(random_bytes(24));
    $prefix = substr($rawKey, 0, 7);
    $hash = hash('sha256', $rawKey);
    $pdo->prepare("INSERT INTO `api_keys` (`key_hash`,`key_prefix`,`name`,`permissions`,`rate_limit`,`expires_at`,`created_by`) VALUES (?,?,?,?,?,?,?)")
        ->execute([$hash,$prefix,$input['name']??'API Key',json_encode($input['permissions']??['read']),$input['rate_limit']??1000,$input['expires_at']??null,$userId]);
    jsonResponse(['success' => true, 'api_key' => $rawKey, 'note' => 'Save this key — it cannot be shown again']);
}

// ─── Users ───────────────────────────────────────────────────────────────────
if ($route === 'users' && $method === 'GET') { requireAuth(['superadmin']); jsonResponse(['users' => $pdo->query("SELECT `id`,`username`,`email`,`role`,`is_active`,`last_login`,`created_at` FROM `users` ORDER BY `created_at`")->fetchAll()]); }
if ($route === 'users' && $method === 'POST') {
    requireAuth(['superadmin']);
    $input = json_decode(file_get_contents('php://input'), true);
    if (empty($input['username']) || empty($input['email']) || empty($input['password'])) jsonResponse(['error' => 'All fields required'], 400);
    try {
        $pdo->prepare("INSERT INTO `users` (`username`,`email`,`password_hash`,`role`) VALUES (?,?,?,?)")
            ->execute([$input['username'], $input['email'], password_hash($input['password'], PASSWORD_BCRYPT, ['cost' => 12]), $input['role'] ?? 'viewer']);
        auditLog('create_user', $userId, 'user', $pdo->lastInsertId());
        jsonResponse(['success' => true, 'id' => (int)$pdo->lastInsertId()]);
    } catch (PDOException $e) { jsonResponse(['error' => 'Username or email exists'], 409); }
}

// ─── Settings ────────────────────────────────────────────────────────────────
if ($route === 'settings' && $method === 'GET') { requireAuth(['superadmin']); $stmt = $pdo->query("SELECT * FROM `settings`"); $s = []; foreach ($stmt->fetchAll() as $r) $s[$r['key_name']] = $r['value']; jsonResponse($s); }
if ($route === 'settings' && $method === 'POST') {
    requireAuth(['superadmin']);
    $input = json_decode(file_get_contents('php://input'), true);
    foreach ($input as $k => $v) $pdo->prepare("INSERT INTO `settings` (`key_name`,`value`) VALUES (?,?) ON DUPLICATE KEY UPDATE `value`=?")->execute([$k,$v,$v]);
    auditLog('update_settings', $userId);
    jsonResponse(['success' => true]);
}

// ─── Audit Log ───────────────────────────────────────────────────────────────
if ($route === 'audit' && $method === 'GET') {
    requireAuth(['superadmin']);
    $limit = min(200, intval($_GET['limit'] ?? 100));
    jsonResponse(['logs' => $pdo->query("SELECT a.*,u.username FROM `audit_log` a LEFT JOIN `users` u ON a.user_id=u.id ORDER BY a.created_at DESC LIMIT {$limit}")->fetchAll()]);
}

// ─── Live Session Feed ───────────────────────────────────────────────────────
if ($route === 'live' && $method === 'GET') {
    $since = $_GET['since'] ?? date('Y-m-d H:i:s', strtotime('-60 seconds'));
    $stmt = $pdo->prepare("SELECT s.id,s.session_uid,s.started_at,s.is_bot,tl.title as link_title,tl.link_uid,n.ip_address,n.is_vpn,n.is_tor,l.country,l.country_code,l.city,l.gps_latitude,l.gps_longitude,d.device_type,d.os_name,b.browser_name,r.risk_score FROM `sessions` s LEFT JOIN `tracking_links` tl ON s.link_id=tl.id LEFT JOIN `network_data` n ON s.id=n.session_id LEFT JOIN `location_data` l ON s.id=l.session_id LEFT JOIN `device_data` d ON s.id=d.session_id LEFT JOIN `browser_data` b ON s.id=b.session_id LEFT JOIN `risk_data` r ON s.id=r.session_id WHERE s.started_at>=? ORDER BY s.started_at DESC LIMIT 50");
    $stmt->execute([$since]);
    $active = $pdo->query("SELECT COUNT(*) FROM `sessions` WHERE `last_activity`>=DATE_SUB(NOW(),INTERVAL 5 MINUTE)")->fetchColumn();
    jsonResponse(['sessions' => $stmt->fetchAll(), 'active_now' => (int)$active, 'server_time' => date('Y-m-d H:i:s')]);
}

// ─── Screenshot ──────────────────────────────────────────────────────────────
if (preg_match('#^sessions/(\d+)/screenshot$#', $route, $m)) {
    $row = $pdo->prepare("SELECT `image_data`,`width`,`height`,`captured_at` FROM `screenshots` WHERE `session_id`=? ORDER BY `captured_at` DESC LIMIT 1");
    $row->execute([$m[1]]);
    $screenshot = $row->fetch();
    jsonResponse($screenshot ?: ['error' => 'No screenshot']);
}

// ─── Heatmap Data ────────────────────────────────────────────────────────────
if (preg_match('#^sessions/(\d+)/heatmap$#', $route, $m)) {
    $row = $pdo->prepare("SELECT `points`,`viewport_w`,`viewport_h` FROM `mouse_heatmap` WHERE `session_id`=? ORDER BY `created_at` DESC LIMIT 1");
    $row->execute([$m[1]]);
    $hm = $row->fetch();
    if ($hm) { $hm['points'] = json_decode($hm['points']); }
    jsonResponse($hm ?: ['error' => 'No heatmap data']);
}

// ─── Pixel Tracking Stats ────────────────────────────────────────────────────
if (preg_match('#^links/(\d+)/pixels$#', $route, $m)) {
    $stmt = $pdo->prepare("SELECT `ip_address`,`user_agent`,`referer`,`opened_at` FROM `pixel_opens` WHERE `link_id`=? ORDER BY `opened_at` DESC LIMIT 200");
    $stmt->execute([$m[1]]);
    $total = $pdo->prepare("SELECT COUNT(*) FROM `pixel_opens` WHERE `link_id`=?"); $total->execute([$m[1]]);
    jsonResponse(['opens' => $stmt->fetchAll(), 'total' => (int)$total->fetchColumn()]);
}

// ─── Link Comparison ─────────────────────────────────────────────────────────
if ($route === 'compare' && $method === 'GET') {
    $ids = array_filter(array_map('intval', explode(',', $_GET['ids'] ?? '')));
    if (count($ids) < 2) jsonResponse(['error' => 'Need at least 2 link IDs'], 400);
    $results = [];
    foreach ($ids as $lid) {
        $link = $pdo->prepare("SELECT `id`,`link_uid`,`title`,`hit_count`,`created_at` FROM `tracking_links` WHERE `id`=?"); $link->execute([$lid]); $link = $link->fetch();
        if (!$link) continue;
        $stats = [];
        $stats['link'] = $link;
        $stats['unique_visitors'] = $pdo->prepare("SELECT COUNT(DISTINCT `visitor_hash`) FROM `sessions` WHERE `link_id`=?"); $stats['unique_visitors']->execute([$lid]); $stats['unique_visitors'] = (int)$stats['unique_visitors']->fetchColumn();
        $stats['avg_duration'] = $pdo->prepare("SELECT AVG(`duration_seconds`) FROM `sessions` WHERE `link_id`=?"); $stats['avg_duration']->execute([$lid]); $stats['avg_duration'] = round((float)$stats['avg_duration']->fetchColumn(), 1);
        $stats['bot_pct'] = $pdo->prepare("SELECT ROUND(SUM(`is_bot`)*100/COUNT(*),1) FROM `sessions` WHERE `link_id`=?"); $stats['bot_pct']->execute([$lid]); $stats['bot_pct'] = (float)$stats['bot_pct']->fetchColumn();
        $stats['vpn_pct'] = $pdo->prepare("SELECT ROUND(SUM(n.`is_vpn`)*100/COUNT(*),1) FROM `sessions` s JOIN `network_data` n ON s.id=n.session_id WHERE s.`link_id`=?"); $stats['vpn_pct']->execute([$lid]); $stats['vpn_pct'] = (float)$stats['vpn_pct']->fetchColumn();
        $stats['avg_risk'] = $pdo->prepare("SELECT AVG(r.`risk_score`) FROM `sessions` s JOIN `risk_data` r ON s.id=r.session_id WHERE s.`link_id`=?"); $stats['avg_risk']->execute([$lid]); $stats['avg_risk'] = round((float)$stats['avg_risk']->fetchColumn(), 1);
        $stats['top_countries'] = $pdo->prepare("SELECT l.`country`,l.`country_code`,COUNT(*) as c FROM `sessions` s JOIN `location_data` l ON s.id=l.session_id WHERE s.`link_id`=? GROUP BY l.`country_code`,l.`country` ORDER BY c DESC LIMIT 5"); $stats['top_countries']->execute([$lid]); $stats['top_countries'] = $stats['top_countries']->fetchAll();
        $stats['devices'] = $pdo->prepare("SELECT d.`device_type`,COUNT(*) as c FROM `sessions` s JOIN `device_data` d ON s.id=d.session_id WHERE s.`link_id`=? GROUP BY d.`device_type` ORDER BY c DESC"); $stats['devices']->execute([$lid]); $stats['devices'] = $stats['devices']->fetchAll();
        $stats['timeline'] = $pdo->prepare("SELECT DATE(s.`started_at`) as dt,COUNT(*) as c FROM `sessions` s WHERE s.`link_id`=? AND s.`started_at`>=DATE_SUB(CURDATE(),INTERVAL 30 DAY) GROUP BY dt ORDER BY dt"); $stats['timeline']->execute([$lid]); $stats['timeline'] = $stats['timeline']->fetchAll();
        $stats['pixel_opens'] = $pdo->prepare("SELECT COUNT(*) FROM `pixel_opens` WHERE `link_id`=?"); $stats['pixel_opens']->execute([$lid]); $stats['pixel_opens'] = (int)$stats['pixel_opens']->fetchColumn();
        $results[] = $stats;
    }
    jsonResponse(['comparisons' => $results]);
}

// ─── QR Code Generator ──────────────────────────────────────────────────────
if (preg_match('#^links/(\d+)/qr$#', $route, $m)) {
    $link = $pdo->prepare("SELECT `link_uid` FROM `tracking_links` WHERE `id`=?"); $link->execute([$m[1]]); $link = $link->fetch();
    if (!$link) jsonResponse(['error' => 'Link not found'], 404);
    $config = getConfig();
    $trackUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . rtrim($config['base_url'] ?? '', '/') . '/t/' . $link['link_uid'];
    // Generate QR using Google Charts API (simple, no library needed)
    $size = intval($_GET['size'] ?? 300);
    $qrUrl = 'https://chart.googleapis.com/chart?chs=' . $size . 'x' . $size . '&cht=qr&chl=' . urlencode($trackUrl) . '&choe=UTF-8';
    jsonResponse(['qr_url' => $qrUrl, 'track_url' => $trackUrl, 'size' => $size]);
}

// ─── Geo Map Data ────────────────────────────────────────────────────────────
if ($route === 'geomap' && $method === 'GET') {
    $linkId = $_GET['link_id'] ?? null;
    $where = "l.`latitude` IS NOT NULL AND l.`latitude` != 0"; $params = [];
    if ($linkId) { $where .= " AND s.`link_id`=?"; $params[] = $linkId; }
    $stmt = $pdo->prepare("SELECT l.`latitude`,l.`longitude`,l.`gps_latitude`,l.`gps_longitude`,l.`country`,l.`city`,n.`ip_address`,n.`is_vpn`,n.`is_tor`,r.`risk_score`,d.`device_type`,s.`started_at`,tl.`title` as link_title FROM `sessions` s LEFT JOIN `location_data` l ON s.id=l.session_id LEFT JOIN `network_data` n ON s.id=n.session_id LEFT JOIN `risk_data` r ON s.id=r.session_id LEFT JOIN `device_data` d ON s.id=d.session_id LEFT JOIN `tracking_links` tl ON s.link_id=tl.id WHERE {$where} ORDER BY s.`started_at` DESC LIMIT 500");
    $stmt->execute($params);
    jsonResponse(['pins' => $stmt->fetchAll()]);
}

// ─── Funnel Analysis ─────────────────────────────────────────────────────────
if (preg_match('#^links/(\d+)/funnel$#', $route, $m)) {
    $lid = (int)$m[1];
    $link = $pdo->prepare("SELECT * FROM `tracking_links` WHERE `id`=?"); $link->execute([$lid]); $link = $link->fetch();
    if (!$link) jsonResponse(['error' => 'Not found'], 404);
    $total = $pdo->prepare("SELECT COUNT(*) FROM `sessions` WHERE `link_id`=?"); $total->execute([$lid]); $total = (int)$total->fetchColumn();
    $bots = $pdo->prepare("SELECT COUNT(*) FROM `sessions` WHERE `link_id`=? AND `is_bot`=1"); $bots->execute([$lid]); $bots = (int)$bots->fetchColumn();
    $humans = $total - $bots;
    $stayed5s = $pdo->prepare("SELECT COUNT(*) FROM `sessions` WHERE `link_id`=? AND `duration_seconds`>=5"); $stayed5s->execute([$lid]); $stayed5s = (int)$stayed5s->fetchColumn();
    $stayed30s = $pdo->prepare("SELECT COUNT(*) FROM `sessions` WHERE `link_id`=? AND `duration_seconds`>=30"); $stayed30s->execute([$lid]); $stayed30s = (int)$stayed30s->fetchColumn();
    $gpsGiven = $pdo->prepare("SELECT COUNT(*) FROM `sessions` s JOIN `location_data` l ON s.id=l.session_id WHERE s.`link_id`=? AND l.`gps_source`='browser'"); $gpsGiven->execute([$lid]); $gpsGiven = (int)$gpsGiven->fetchColumn();
    $pixelOpens = $pdo->prepare("SELECT COUNT(*) FROM `pixel_opens` WHERE `link_id`=?"); $pixelOpens->execute([$lid]); $pixelOpens = (int)$pixelOpens->fetchColumn();
    jsonResponse(['link_title'=>$link['title'],'total_hits'=>$total,'bots'=>$bots,'humans'=>$humans,'stayed_5s'=>$stayed5s,'stayed_30s'=>$stayed30s,'gps_granted'=>$gpsGiven,'pixel_opens'=>$pixelOpens,'landing_mode'=>$link['landing_mode']]);
}

// ─── Intelligence Engine: Visitor Profile ────────────────────────────────────
if (preg_match('#^visitors/(\d+)/profile$#', $route, $m)) {
    require_once __DIR__ . '/../includes/intelligence_engine.php';
    $vid = (int)$m[1];
    $profiler = new VisitorProfiler($pdo);
    $profile = $profiler->profileVisitor($vid);
    jsonResponse($profile);
}

// ─── Intelligence Engine: Profile All Visitors ──────────────────────────────
if ($route === 'intelligence/profile-all' && $method === 'POST') {
    require_once __DIR__ . '/../includes/intelligence_engine.php';
    $profiler = new VisitorProfiler($pdo);
    $visitors = $pdo->query("SELECT `id` FROM `visitor_identities` ORDER BY `last_seen` DESC LIMIT 500")->fetchAll();
    $results = ['profiled' => 0, 'errors' => 0, 'error_details' => []];
    foreach ($visitors as $v) {
        try {
            $p = $profiler->profileVisitor($v['id']);
            if (isset($p['error'])) { $results['errors']++; $results['error_details'][] = "v{$v['id']}: {$p['error']}"; }
            else $results['profiled']++;
        } catch (Exception $e) {
            $results['errors']++;
            $results['error_details'][] = "v{$v['id']}: " . $e->getMessage();
        }
    }
    jsonResponse($results);
}

// ─── Intelligence Engine: Run Identity Correlation ──────────────────────────
if ($route === 'intelligence/correlate' && $method === 'POST') {
    require_once __DIR__ . '/../includes/intelligence_engine.php';
    $correlator = new IdentityCorrelator($pdo);
    $result = $correlator->correlateAll();
    jsonResponse($result);
}

// ─── Intelligence Engine: Get Visitor Intelligence ──────────────────────────
if (preg_match('#^visitors/(\d+)/intelligence$#', $route, $m)) {
    $vid = (int)$m[1];
    // Get profile
    $profile = $pdo->prepare("SELECT * FROM `visitor_profiles` WHERE `visitor_id`=?"); $profile->execute([$vid]); $profile = $profile->fetch() ?: null;
    // Get identity
    $identity = $pdo->prepare("SELECT * FROM `visitor_identities` WHERE `id`=?"); $identity->execute([$vid]); $identity = $identity->fetch() ?: null;
    // Get cluster members
    $cluster = [];
    if ($identity && !empty($identity['cluster_id'])) {
        $cStmt = $pdo->prepare("SELECT ig.*, vi.fingerprint, vi.ip_address, vi.session_count, vi.first_seen, vi.last_seen FROM `identity_graph` ig JOIN `visitor_identities` vi ON ig.matched_with_visitor_id = vi.id WHERE ig.visitor_id = ? ORDER BY ig.match_confidence DESC");
        $cStmt->execute([$vid]);
        $cluster = $cStmt->fetchAll();
    }
    // Get all sessions
    $sessStmt = $pdo->prepare("SELECT s.id, s.started_at, s.duration_seconds, tl.title as link_title, tl.link_uid, n.ip_address, l.country_code, l.city, d.device_type, d.os_name, b.browser_name, r.risk_score FROM `visitor_sessions` vs JOIN `sessions` s ON vs.session_id=s.id LEFT JOIN `tracking_links` tl ON s.link_id=tl.id LEFT JOIN `network_data` n ON s.id=n.session_id LEFT JOIN `location_data` l ON s.id=l.session_id LEFT JOIN `device_data` d ON s.id=d.session_id LEFT JOIN `browser_data` b ON s.id=b.session_id LEFT JOIN `risk_data` r ON s.id=r.session_id WHERE vs.visitor_id=? ORDER BY s.started_at DESC");
    $sessStmt->execute([$vid]);
    jsonResponse([
        'profile' => $profile,
        'identity' => $identity,
        'cluster' => $cluster,
        'sessions' => $sessStmt->fetchAll(),
    ]);
}

// ─── Intelligence Engine: Dashboard Overview ────────────────────────────────
if ($route === 'intelligence/overview') {
    $total = (int)$pdo->query("SELECT COUNT(*) FROM `visitor_identities`")->fetchColumn();
    $profiled = (int)$pdo->query("SELECT COUNT(*) FROM `visitor_profiles`")->fetchColumn();
    $clustered = (int)$pdo->query("SELECT COUNT(DISTINCT `cluster_id`) FROM `visitor_identities` WHERE `cluster_id` IS NOT NULL")->fetchColumn();

    // Device tier distribution
    $tiers = $pdo->query("SELECT `device_tier`, COUNT(*) as cnt FROM `visitor_profiles` GROUP BY `device_tier` ORDER BY cnt DESC")->fetchAll();
    // Tech level distribution
    $techLevels = $pdo->query("SELECT `tech_level`, COUNT(*) as cnt FROM `visitor_profiles` GROUP BY `tech_level` ORDER BY cnt DESC")->fetchAll();
    // Intent distribution
    $intents = $pdo->query("SELECT `intent`, COUNT(*) as cnt FROM `visitor_profiles` GROUP BY `intent` ORDER BY cnt DESC")->fetchAll();
    // Privacy distribution
    $privacy = $pdo->query("SELECT `privacy_consciousness`, COUNT(*) as cnt FROM `visitor_profiles` GROUP BY `privacy_consciousness` ORDER BY cnt DESC")->fetchAll();
    // Ecosystem distribution
    $ecosystems = $pdo->query("SELECT `primary_ecosystem`, COUNT(*) as cnt FROM `visitor_profiles` GROUP BY `primary_ecosystem` ORDER BY cnt DESC")->fetchAll();
    // Engagement distribution
    $engagement = $pdo->query("SELECT `engagement_level`, COUNT(*) as cnt FROM `visitor_profiles` GROUP BY `engagement_level` ORDER BY cnt DESC")->fetchAll();
    // Recent profiles
    $recent = $pdo->query("SELECT vp.*, vi.ip_address, vi.session_count, vi.first_seen, vi.last_seen FROM `visitor_profiles` vp JOIN `visitor_identities` vi ON vp.visitor_id=vi.id ORDER BY vp.last_profiled DESC LIMIT 20")->fetchAll();

    jsonResponse([
        'total_visitors' => $total,
        'profiled' => $profiled,
        'clusters' => $clustered,
        'tiers' => $tiers,
        'tech_levels' => $techLevels,
        'intents' => $intents,
        'privacy' => $privacy,
        'ecosystems' => $ecosystems,
        'engagement' => $engagement,
        'recent_profiles' => $recent,
    ]);
}

jsonResponse(['error' => 'Not found'], 404);
