<?php
// ============================================================
//  Metrics tracker — include at the top of every public page
// ============================================================
//
//  Usage:
//    require_once __DIR__ . '/includes/metrics.php';
//    metrics_track_pageview();                    // page view
//    metrics_track_listing_view($listing_id);     // listing detail view
//    metrics_track_search($q, $cat, $tier, $n);   // directory search
//
//  Design notes:
//  - IP addresses are hashed with a secret salt before storage.
//    We can still group per visitor, but can't recover the IP.
//  - Country lookup is cached forever per IP hash.
//  - Bot traffic is skipped.
//  - Admin/member portal pages are never tracked (noise).
//  - Failures are silent — metrics never break the site.
//
// ============================================================

if (!defined('METRICS_SALT')) {
    define('METRICS_SALT', 'buylocal-metrics-salt-change-me');
}

function metrics_enabled(): bool {
    // Skip admin / member portal / cron / crawlers
    $path = $_SERVER['PHP_SELF'] ?? '';
    if (strpos($path, '/admin/') !== false)  return false;
    if (strpos($path, '/member/') !== false) return false;
    if (strpos($path, '/cron/') !== false)   return false;
    if (strpos($path, 'payfast-itn')  !== false) return false;
    if (strpos($path, '-submit.php')  !== false) return false;
    return true;
}

function metrics_hash_ip(string $ip): string {
    return hash('sha256', $ip . '|' . METRICS_SALT);
}

function metrics_detect_device(string $ua): string {
    $ua = strtolower($ua);
    // Common bots
    $bots = ['bot','crawler','spider','slurp','baidu','yandex','facebookexternalhit',
             'twitterbot','telegram','whatsapp','slack','linkedinbot','googlebot',
             'bingbot','headless','lighthouse','curl','wget','http-client'];
    foreach ($bots as $b) { if (strpos($ua, $b) !== false) return 'bot'; }
    if ($ua === '') return 'unknown';
    // Tablet
    if (preg_match('/ipad|tablet|kindle|playbook|silk/', $ua)) return 'tablet';
    // Mobile
    if (preg_match('/mobile|iphone|android.*mobile|phone|ipod|opera mini|blackberry/', $ua)) return 'mobile';
    return 'desktop';
}

function metrics_client_ip(): string {
    $candidates = [
        'HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR',
        'HTTP_X_REAL_IP', 'REMOTE_ADDR',
    ];
    foreach ($candidates as $k) {
        if (!empty($_SERVER[$k])) {
            $ip = trim(explode(',', $_SERVER[$k])[0]);
            if (filter_var($ip, FILTER_VALIDATE_IP)) return $ip;
        }
    }
    return '0.0.0.0';
}

function metrics_country_for(string $ip, string $ip_hash): ?string {
    // Private / local IPs — skip lookup
    if ($ip === '0.0.0.0' || $ip === '127.0.0.1') return null;
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
        return null;
    }

    // Check cache first
    try {
        $cached = db_row('SELECT country FROM metrics_ip_cache WHERE ip_hash = :h', ['h' => $ip_hash]);
        if ($cached !== null) return $cached['country']; // may be null — stored lookup-failure
    } catch (Throwable $e) { return null; }

    // Lookup via free ipapi.co (no API key, ~1000/day)
    $ctx = stream_context_create([
        'http' => [ 'timeout' => 2, 'ignore_errors' => true,
                    'header' => "User-Agent: BuyLocalLowveld/1.0\r\n" ],
    ]);
    $country = null;
    $json = @file_get_contents("https://ipapi.co/{$ip}/country_name/", false, $ctx);
    if ($json !== false) {
        $json = trim($json);
        if ($json && strlen($json) < 80 && strpos($json, 'error') === false) {
            $country = $json;
        }
    }

    // Cache result (even if null — stops repeat failed lookups)
    try {
        db_exec(
            'INSERT INTO metrics_ip_cache (ip_hash, country) VALUES (:h, :c)
               ON DUPLICATE KEY UPDATE country = :c, looked_at = NOW()',
            ['h' => $ip_hash, 'c' => $country]
        );
    } catch (Throwable $e) { /* ignore */ }

    return $country;
}

// ------------------------------------------------------------
// Public API
// ------------------------------------------------------------

function metrics_track_pageview(?int $listing_id = null): void {
    if (!metrics_enabled()) return;
    try {
        $ip      = metrics_client_ip();
        $ip_hash = metrics_hash_ip($ip);
        $ua      = $_SERVER['HTTP_USER_AGENT'] ?? '';
        $device  = metrics_detect_device($ua);
        if ($device === 'bot') return;  // Don't record bots

        // Throttle per-IP per-page: one view per 30 seconds (stops reloads inflating numbers)
        $page = basename($_SERVER['PHP_SELF'] ?? 'unknown');
        $path = substr($_SERVER['REQUEST_URI'] ?? $page, 0, 255);

        $recent = db_value(
            "SELECT id FROM metrics_pageviews
              WHERE ip_hash=:h AND page=:p AND viewed_at >= DATE_SUB(NOW(), INTERVAL 30 SECOND)
              LIMIT 1",
            ['h' => $ip_hash, 'p' => $page]
        );
        if ($recent) return;

        $country = metrics_country_for($ip, $ip_hash);
        $referrer = substr($_SERVER['HTTP_REFERER'] ?? '', 0, 255) ?: null;

        db_insert('metrics_pageviews', [
            'page'        => $page,
            'path'        => $path,
            'listing_id'  => $listing_id,
            'ip_hash'     => $ip_hash,
            'country'     => $country,
            'device_type' => $device,
            'referrer'    => $referrer,
        ]);
    } catch (Throwable $e) { /* swallow — metrics never break the page */ }
}

function metrics_track_listing_view(int $listing_id): void {
    metrics_track_pageview($listing_id);
}

function metrics_track_search(?string $query, ?string $category = null,
                              ?string $tier = null, int $result_count = 0): void {
    if (!metrics_enabled()) return;
    try {
        $query    = $query    ? substr(trim($query), 0, 255) : null;
        $category = $category ? substr(trim($category), 0, 80) : null;
        $tier     = $tier     ? substr(trim($tier), 0, 40) : null;

        // Don't log empty searches (no query, no filter)
        if (!$query && !$category && !$tier) return;

        $ip_hash = metrics_hash_ip(metrics_client_ip());

        db_insert('metrics_searches', [
            'query_text'    => $query,
            'category_slug' => $category,
            'tier_filter'   => $tier,
            'result_count'  => $result_count,
            'ip_hash'       => $ip_hash,
        ]);
    } catch (Throwable $e) { /* ignore */ }
}