<?php
/**
 * Visitor Tracking System
 * LandSales Pro - Comprehensive visitor analytics
 */

class VisitorTracker {
    private $conn;
    private $site;
    private $visitorId;
    private $sessionId;
    private $pageviewId;

    // Known bot signatures
    private $botSignatures = [
        'googlebot', 'bingbot', 'slurp', 'duckduckbot', 'baiduspider',
        'yandexbot', 'sogou', 'exabot', 'facebot', 'facebookexternalhit',
        'ia_archiver', 'alexabot', 'mj12bot', 'ahrefsbot', 'semrushbot',
        'dotbot', 'rogerbot', 'seznambot', 'twitter', 'linkedinbot',
        'whatsapp', 'telegrambot', 'slackbot', 'discordbot', 'pinterestbot',
        'applebot', 'petalbot', 'bytespider', 'gptbot', 'claudebot',
        'anthropic', 'ccbot', 'dataforseo', 'blexbot', 'megaindex',
        'serpstatbot', 'seokicks', 'screaming frog', 'netcraft', 'uptimerobot',
        'pingdom', 'gtmetrix', 'webpagetest', 'lighthouse', 'pagespeed',
        'crawler', 'spider', 'bot', 'scraper', 'fetch', 'curl', 'wget',
        'python-requests', 'axios', 'node-fetch', 'headless', 'phantom'
    ];

    public function __construct($conn, $site = 'landsales') {
        $this->conn = $conn;
        $this->site = $site;
    }

    /**
     * Track a page view
     */
    public function track() {
        $ip = $this->getClientIP();
        $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
        $visitorHash = $this->generateVisitorHash($ip, $userAgent);

        // Get or create visitor
        $this->visitorId = $this->getOrCreateVisitor($visitorHash, $ip, $userAgent);

        // Get or create session
        $this->sessionId = $this->getOrCreateSession();

        // Track page view
        $this->pageviewId = $this->trackPageView();

        return [
            'visitor_id' => $this->visitorId,
            'session_id' => $this->sessionId,
            'pageview_id' => $this->pageviewId
        ];
    }

    /**
     * Track a custom event
     */
    public function trackEvent($type, $category = null, $action = null, $label = null, $value = null) {
        if (!$this->visitorId || !$this->sessionId) {
            return false;
        }

        $stmt = $this->conn->prepare("
            INSERT INTO visitor_events
            (visitor_id, session_id, pageview_id, site, event_type, event_category, event_action, event_label, event_value)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        ");

        $stmt->bind_param('iiissssss',
            $this->visitorId,
            $this->sessionId,
            $this->pageviewId,
            $this->site,
            $type,
            $category,
            $action,
            $label,
            $value
        );

        return $stmt->execute();
    }

    /**
     * Get or create visitor record
     */
    private function getOrCreateVisitor($hash, $ip, $userAgent) {
        // Check if visitor exists
        $stmt = $this->conn->prepare("SELECT id FROM visitors WHERE visitor_hash = ?");
        $stmt->bind_param('s', $hash);
        $stmt->execute();
        $result = $stmt->get_result();

        if ($row = $result->fetch_assoc()) {
            // Update existing visitor
            $updateStmt = $this->conn->prepare("
                UPDATE visitors SET
                    last_visit = NOW(),
                    total_visits = total_visits + 1
                WHERE id = ?
            ");
            $updateStmt->bind_param('i', $row['id']);
            $updateStmt->execute();

            return $row['id'];
        }

        // Parse user agent
        $deviceInfo = $this->parseUserAgent($userAgent);
        $isBot = $this->detectBot($userAgent);
        $botName = $isBot ? $this->getBotName($userAgent) : null;

        // Get geolocation
        $geo = $this->getGeolocation($ip);

        // Insert new visitor
        $stmt = $this->conn->prepare("
            INSERT INTO visitors
            (visitor_hash, ip_address, user_agent, device_type, browser, browser_version,
             os, os_version, is_bot, bot_name, country_code, country_name, region, city,
             latitude, longitude, timezone, isp, site)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ");

        $stmt->bind_param('ssssssssisssssddsss',
            $hash,
            $ip,
            $userAgent,
            $deviceInfo['device_type'],
            $deviceInfo['browser'],
            $deviceInfo['browser_version'],
            $deviceInfo['os'],
            $deviceInfo['os_version'],
            $isBot,
            $botName,
            $geo['country_code'],
            $geo['country_name'],
            $geo['region'],
            $geo['city'],
            $geo['latitude'],
            $geo['longitude'],
            $geo['timezone'],
            $geo['isp'],
            $this->site
        );

        $stmt->execute();
        return $this->conn->insert_id;
    }

    /**
     * Get or create session
     */
    private function getOrCreateSession() {
        // Session timeout: 30 minutes
        $sessionTimeout = 30 * 60;

        // Check for existing session cookie
        $sessionToken = $_COOKIE['vt_session'] ?? null;

        if ($sessionToken) {
            $stmt = $this->conn->prepare("
                SELECT id, started_at FROM visitor_sessions
                WHERE session_id = ? AND visitor_id = ?
                AND ended_at IS NULL
                AND started_at > DATE_SUB(NOW(), INTERVAL ? SECOND)
            ");
            $stmt->bind_param('sii', $sessionToken, $this->visitorId, $sessionTimeout);
            $stmt->execute();
            $result = $stmt->get_result();

            if ($row = $result->fetch_assoc()) {
                // Update pageviews count
                $updateStmt = $this->conn->prepare("
                    UPDATE visitor_sessions SET pageviews = pageviews + 1 WHERE id = ?
                ");
                $updateStmt->bind_param('i', $row['id']);
                $updateStmt->execute();

                return $row['id'];
            }
        }

        // Create new session
        $sessionToken = bin2hex(random_bytes(32));
        $referrer = $_SERVER['HTTP_REFERER'] ?? null;
        $referrerDomain = $referrer ? parse_url($referrer, PHP_URL_HOST) : null;
        $entryPage = $_SERVER['REQUEST_URI'] ?? '/';

        // Get UTM parameters
        $utmSource = $_GET['utm_source'] ?? null;
        $utmMedium = $_GET['utm_medium'] ?? null;
        $utmCampaign = $_GET['utm_campaign'] ?? null;
        $utmTerm = $_GET['utm_term'] ?? null;
        $utmContent = $_GET['utm_content'] ?? null;

        $stmt = $this->conn->prepare("
            INSERT INTO visitor_sessions
            (visitor_id, session_id, site, entry_page, referrer, referrer_domain,
             utm_source, utm_medium, utm_campaign, utm_term, utm_content, pageviews)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)
        ");

        $stmt->bind_param('issssssssss',
            $this->visitorId,
            $sessionToken,
            $this->site,
            $entryPage,
            $referrer,
            $referrerDomain,
            $utmSource,
            $utmMedium,
            $utmCampaign,
            $utmTerm,
            $utmContent
        );

        $stmt->execute();
        $sessionId = $this->conn->insert_id;

        // Set session cookie (30 min expiry)
        setcookie('vt_session', $sessionToken, time() + $sessionTimeout, '/', '', false, true);

        return $sessionId;
    }

    /**
     * Track page view
     */
    private function trackPageView() {
        $pageUrl = (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
        $pagePath = parse_url($pageUrl, PHP_URL_PATH);
        $queryString = $_SERVER['QUERY_STRING'] ?? null;
        $pageTitle = null; // Will be updated via JS

        $stmt = $this->conn->prepare("
            INSERT INTO visitor_pageviews
            (visitor_id, session_id, site, page_url, page_path, page_title, query_string)
            VALUES (?, ?, ?, ?, ?, ?, ?)
        ");

        $stmt->bind_param('iisssss',
            $this->visitorId,
            $this->sessionId,
            $this->site,
            $pageUrl,
            $pagePath,
            $pageTitle,
            $queryString
        );

        $stmt->execute();

        // Update visitor total pageviews
        $this->conn->query("UPDATE visitors SET total_pageviews = total_pageviews + 1 WHERE id = {$this->visitorId}");

        return $this->conn->insert_id;
    }

    /**
     * Get client IP address
     */
    private function getClientIP() {
        $headers = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'];

        foreach ($headers as $header) {
            if (!empty($_SERVER[$header])) {
                $ip = $_SERVER[$header];
                // Handle comma-separated IPs (X-Forwarded-For)
                if (strpos($ip, ',') !== false) {
                    $ip = trim(explode(',', $ip)[0]);
                }
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    return $ip;
                }
            }
        }

        return '0.0.0.0';
    }

    /**
     * Generate unique visitor hash
     */
    private function generateVisitorHash($ip, $userAgent) {
        return hash('sha256', $ip . '|' . $userAgent . '|' . date('Y-m'));
    }

    /**
     * Parse user agent string
     */
    private function parseUserAgent($userAgent) {
        $result = [
            'device_type' => 'unknown',
            'browser' => 'Unknown',
            'browser_version' => '',
            'os' => 'Unknown',
            'os_version' => ''
        ];

        if (empty($userAgent)) {
            return $result;
        }

        $ua = strtolower($userAgent);

        // Detect device type
        if ($this->detectBot($userAgent)) {
            $result['device_type'] = 'bot';
        } elseif (preg_match('/(tablet|ipad|playbook|silk)|(android(?!.*mobile))/i', $userAgent)) {
            $result['device_type'] = 'tablet';
        } elseif (preg_match('/(mobile|iphone|ipod|android|blackberry|opera mini|iemobile)/i', $userAgent)) {
            $result['device_type'] = 'mobile';
        } else {
            $result['device_type'] = 'desktop';
        }

        // Detect browser
        $browsers = [
            'Edge' => '/edg(?:e|a|ios)?\/(\d+)/i',
            'Chrome' => '/chrome\/(\d+)/i',
            'Firefox' => '/firefox\/(\d+)/i',
            'Safari' => '/version\/(\d+).*safari/i',
            'Opera' => '/(?:opera|opr)\/(\d+)/i',
            'IE' => '/(?:msie |trident.*rv:)(\d+)/i'
        ];

        foreach ($browsers as $browser => $pattern) {
            if (preg_match($pattern, $userAgent, $matches)) {
                $result['browser'] = $browser;
                $result['browser_version'] = $matches[1] ?? '';
                break;
            }
        }

        // Detect OS
        $osPatterns = [
            'Windows 11' => '/windows nt 10.*build.*(22|23)\d{3}/i',
            'Windows 10' => '/windows nt 10/i',
            'Windows 8.1' => '/windows nt 6\.3/i',
            'Windows 8' => '/windows nt 6\.2/i',
            'Windows 7' => '/windows nt 6\.1/i',
            'macOS' => '/mac os x (\d+[._]\d+)/i',
            'iOS' => '/(?:iphone|ipad|ipod).*os (\d+[._]\d+)/i',
            'Android' => '/android (\d+\.?\d*)/i',
            'Linux' => '/linux/i',
            'Ubuntu' => '/ubuntu/i'
        ];

        foreach ($osPatterns as $os => $pattern) {
            if (preg_match($pattern, $userAgent, $matches)) {
                $result['os'] = $os;
                if (isset($matches[1])) {
                    $result['os_version'] = str_replace('_', '.', $matches[1]);
                }
                break;
            }
        }

        return $result;
    }

    /**
     * Detect if visitor is a bot
     */
    private function detectBot($userAgent) {
        if (empty($userAgent)) {
            return true; // No user agent = likely bot
        }

        $ua = strtolower($userAgent);

        foreach ($this->botSignatures as $signature) {
            if (strpos($ua, $signature) !== false) {
                return true;
            }
        }

        // Additional bot detection heuristics
        if (strlen($userAgent) < 20) {
            return true;
        }

        return false;
    }

    /**
     * Get bot name
     */
    private function getBotName($userAgent) {
        $ua = strtolower($userAgent);

        $knownBots = [
            'googlebot' => 'Googlebot',
            'bingbot' => 'Bingbot',
            'slurp' => 'Yahoo Slurp',
            'duckduckbot' => 'DuckDuckBot',
            'baiduspider' => 'Baidu Spider',
            'yandexbot' => 'YandexBot',
            'facebookexternalhit' => 'Facebook Bot',
            'twitterbot' => 'Twitter Bot',
            'linkedinbot' => 'LinkedIn Bot',
            'whatsapp' => 'WhatsApp Bot',
            'telegrambot' => 'Telegram Bot',
            'ahrefsbot' => 'Ahrefs Bot',
            'semrushbot' => 'SEMrush Bot',
            'gptbot' => 'GPTBot',
            'claudebot' => 'Claude Bot',
            'uptimerobot' => 'Uptime Robot'
        ];

        foreach ($knownBots as $signature => $name) {
            if (strpos($ua, $signature) !== false) {
                return $name;
            }
        }

        return 'Unknown Bot';
    }

    /**
     * Get geolocation data from IP
     */
    private function getGeolocation($ip) {
        $default = [
            'country_code' => null,
            'country_name' => null,
            'region' => null,
            'city' => null,
            'latitude' => null,
            'longitude' => null,
            'timezone' => null,
            'isp' => null
        ];

        // Skip for local IPs
        if (in_array($ip, ['127.0.0.1', '::1', '0.0.0.0']) ||
            preg_match('/^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)/', $ip)) {
            $default['country_name'] = 'Local Network';
            return $default;
        }

        // Try ip-api.com (free, 45 requests per minute)
        $url = "http://ip-api.com/json/{$ip}?fields=status,country,countryCode,regionName,city,lat,lon,timezone,isp";

        $context = stream_context_create([
            'http' => [
                'timeout' => 3,
                'ignore_errors' => true
            ]
        ]);

        try {
            $response = @file_get_contents($url, false, $context);
            if ($response) {
                $data = json_decode($response, true);
                if ($data && $data['status'] === 'success') {
                    return [
                        'country_code' => $data['countryCode'] ?? null,
                        'country_name' => $data['country'] ?? null,
                        'region' => $data['regionName'] ?? null,
                        'city' => $data['city'] ?? null,
                        'latitude' => $data['lat'] ?? null,
                        'longitude' => $data['lon'] ?? null,
                        'timezone' => $data['timezone'] ?? null,
                        'isp' => $data['isp'] ?? null
                    ];
                }
            }
        } catch (Exception $e) {
            // Silently fail
        }

        return $default;
    }

    /**
     * Get visitor ID
     */
    public function getVisitorId() {
        return $this->visitorId;
    }

    /**
     * Get session ID
     */
    public function getSessionId() {
        return $this->sessionId;
    }

    /**
     * Get pageview ID
     */
    public function getPageviewId() {
        return $this->pageviewId;
    }
}

/**
 * Helper function to initialize tracking
 */
function trackVisitor($site = 'landsales') {
    global $conn;

    if (!isset($conn) || !$conn) {
        return null;
    }

    $tracker = new VisitorTracker($conn, $site);
    return $tracker->track();
}

/**
 * Get visitor statistics
 */
function getVisitorStats($site = null, $days = 30) {
    global $conn;

    $siteCondition = $site ? "AND site = '" . mysqli_real_escape_string($conn, $site) . "'" : "";
    $dateCondition = "AND first_visit >= DATE_SUB(NOW(), INTERVAL $days DAY)";

    // Total visitors
    $totalVisitors = db_fetch_one("
        SELECT COUNT(*) as count FROM visitors
        WHERE 1=1 $siteCondition $dateCondition
    ")['count'] ?? 0;

    // Human visitors
    $humanVisitors = db_fetch_one("
        SELECT COUNT(*) as count FROM visitors
        WHERE is_bot = 0 $siteCondition $dateCondition
    ")['count'] ?? 0;

    // Bot visitors
    $botVisitors = db_fetch_one("
        SELECT COUNT(*) as count FROM visitors
        WHERE is_bot = 1 $siteCondition $dateCondition
    ")['count'] ?? 0;

    // Total pageviews
    $totalPageviews = db_fetch_one("
        SELECT COUNT(*) as count FROM visitor_pageviews vp
        JOIN visitors v ON vp.visitor_id = v.id
        WHERE 1=1 " . ($site ? "AND vp.site = '" . mysqli_real_escape_string($conn, $site) . "'" : "") . "
        AND vp.viewed_at >= DATE_SUB(NOW(), INTERVAL $days DAY)
    ")['count'] ?? 0;

    // Today's visitors
    $todayVisitors = db_fetch_one("
        SELECT COUNT(*) as count FROM visitors
        WHERE DATE(first_visit) = CURDATE() AND is_bot = 0 $siteCondition
    ")['count'] ?? 0;

    // Top countries
    $topCountries = db_fetch_all("
        SELECT country_name, country_code, COUNT(*) as count
        FROM visitors
        WHERE country_name IS NOT NULL AND is_bot = 0 $siteCondition $dateCondition
        GROUP BY country_code, country_name
        ORDER BY count DESC
        LIMIT 10
    ");

    // Top pages
    $topPages = db_fetch_all("
        SELECT page_path, COUNT(*) as views
        FROM visitor_pageviews vp
        JOIN visitors v ON vp.visitor_id = v.id
        WHERE v.is_bot = 0 " . ($site ? "AND vp.site = '" . mysqli_real_escape_string($conn, $site) . "'" : "") . "
        AND vp.viewed_at >= DATE_SUB(NOW(), INTERVAL $days DAY)
        GROUP BY page_path
        ORDER BY views DESC
        LIMIT 10
    ");

    // Device breakdown
    $devices = db_fetch_all("
        SELECT device_type, COUNT(*) as count
        FROM visitors
        WHERE is_bot = 0 $siteCondition $dateCondition
        GROUP BY device_type
        ORDER BY count DESC
    ");

    // Browser breakdown
    $browsers = db_fetch_all("
        SELECT browser, COUNT(*) as count
        FROM visitors
        WHERE is_bot = 0 $siteCondition $dateCondition
        GROUP BY browser
        ORDER BY count DESC
        LIMIT 5
    ");

    // Daily visitors (last 30 days)
    $dailyVisitors = db_fetch_all("
        SELECT DATE(first_visit) as date,
               COUNT(*) as total,
               SUM(CASE WHEN is_bot = 0 THEN 1 ELSE 0 END) as humans,
               SUM(CASE WHEN is_bot = 1 THEN 1 ELSE 0 END) as bots
        FROM visitors
        WHERE first_visit >= DATE_SUB(NOW(), INTERVAL $days DAY) $siteCondition
        GROUP BY DATE(first_visit)
        ORDER BY date ASC
    ");

    return [
        'total_visitors' => $totalVisitors,
        'human_visitors' => $humanVisitors,
        'bot_visitors' => $botVisitors,
        'total_pageviews' => $totalPageviews,
        'today_visitors' => $todayVisitors,
        'top_countries' => $topCountries,
        'top_pages' => $topPages,
        'devices' => $devices,
        'browsers' => $browsers,
        'daily_visitors' => $dailyVisitors
    ];
}

/**
 * Get recent visitors
 */
function getRecentVisitors($site = null, $limit = 50, $offset = 0, $includesBots = true) {
    global $conn;

    $siteCondition = $site ? "AND site = '" . mysqli_real_escape_string($conn, $site) . "'" : "";
    $botCondition = $includesBots ? "" : "AND is_bot = 0";

    return db_fetch_all("
        SELECT v.*,
               (SELECT COUNT(*) FROM visitor_pageviews WHERE visitor_id = v.id) as pageviews,
               (SELECT page_path FROM visitor_pageviews WHERE visitor_id = v.id ORDER BY viewed_at DESC LIMIT 1) as last_page
        FROM visitors v
        WHERE 1=1 $siteCondition $botCondition
        ORDER BY last_visit DESC
        LIMIT $limit OFFSET $offset
    ");
}

/**
 * Get visitor details with full activity
 */
function getVisitorDetails($visitorId) {
    global $conn;

    $visitor = db_fetch_one("SELECT * FROM visitors WHERE id = ?", 'i', [$visitorId]);

    if (!$visitor) {
        return null;
    }

    // Get sessions
    $sessions = db_fetch_all("
        SELECT * FROM visitor_sessions
        WHERE visitor_id = ?
        ORDER BY started_at DESC
    ", 'i', [$visitorId]);

    // Get all pageviews
    $pageviews = db_fetch_all("
        SELECT * FROM visitor_pageviews
        WHERE visitor_id = ?
        ORDER BY viewed_at DESC
        LIMIT 100
    ", 'i', [$visitorId]);

    // Get events
    $events = db_fetch_all("
        SELECT * FROM visitor_events
        WHERE visitor_id = ?
        ORDER BY created_at DESC
        LIMIT 50
    ", 'i', [$visitorId]);

    return [
        'visitor' => $visitor,
        'sessions' => $sessions,
        'pageviews' => $pageviews,
        'events' => $events
    ];
}
