<?php
/**
 * AuctionForge User Tracking
 * Tracks user IP addresses and device fingerprints on registration and login.
 * Cloudflare-aware: uses CF-Connecting-IP header when available.
 * Deduplicates: same user + same IP + same device = update last_seen + increment count.
 */

if (!defined('ABSPATH')) {
    die('You shouldnt be here');
}

function auctionforge_create_ip_log_table() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'auctionforge_user_tracking';
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE IF NOT EXISTS $table_name (
        id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
        email varchar(255) NOT NULL DEFAULT '',
        first_name varchar(255) DEFAULT '',
        last_name varchar(255) DEFAULT '',
        ip_address varchar(45) NOT NULL DEFAULT '',
        device_id varchar(64) NOT NULL DEFAULT '',
        device_fingerprint varchar(64) NOT NULL DEFAULT '',
        gpu_vendor varchar(100) DEFAULT '',
        gpu_renderer varchar(255) DEFAULT '',
        canvas_hash varchar(64) DEFAULT '',
        audio_hash varchar(64) DEFAULT '',
        screen_res varchar(50) DEFAULT '',
        hardware_cores int unsigned DEFAULT 0,
        math_hash varchar(64) DEFAULT '',
        webrtc_ips varchar(255) DEFAULT '',
        media_devices varchar(100) DEFAULT '',
        voices_hash varchar(64) DEFAULT '',
        battery_info varchar(50) DEFAULT '',
        connection_type varchar(50) DEFAULT '',
        color_profile varchar(50) DEFAULT '',
        accessibility varchar(100) DEFAULT '',
        form_fill_ms int unsigned DEFAULT 0,
        user_agent text DEFAULT '',
        first_seen datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
        last_seen datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
        event_count int unsigned NOT NULL DEFAULT 1,
        last_event_type varchar(20) NOT NULL DEFAULT 'register',
        PRIMARY KEY (id),
        UNIQUE KEY idx_email_ip_device (email, ip_address, device_id),
        KEY idx_email (email),
        KEY idx_ip_address (ip_address),
        KEY idx_device_id (device_id),
        KEY idx_device_fingerprint (device_fingerprint),
        KEY idx_last_seen (last_seen)
    ) $charset_collate;";

    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    dbDelta($sql);

    // Force column migration for existing tables (dbDelta is unreliable for ALTER)
    auctionforge_maybe_upgrade_tracking_table();
}

/**
 * Auto-migrate tracking table schema on version change.
 * Runs once per plugin version — checks for missing columns and adds them.
 * This guarantees sites upgraded from older versions get the full schema.
 */
function auctionforge_maybe_upgrade_tracking_table() {
    $current_version = defined('AUCTIONFORGE_VERSION') ? AUCTIONFORGE_VERSION : '0.0.0';
    $db_version = get_option('auctionforge_tracking_db_version', '0.0.0');

    if (version_compare($db_version, $current_version, '>=')) {
        return; // Already migrated for this version
    }

    global $wpdb;
    $table = $wpdb->prefix . 'auctionforge_user_tracking';

    // Check table exists at all
    if ($wpdb->get_var("SHOW TABLES LIKE '$table'") !== $table) {
        return;
    }

    $existing_cols = $wpdb->get_col("SHOW COLUMNS FROM $table", 0);

    // Define every column that should exist with its ADD COLUMN definition
    $required_columns = array(
        'gpu_vendor'      => "varchar(100) DEFAULT '' AFTER device_fingerprint",
        'gpu_renderer'    => "varchar(255) DEFAULT '' AFTER gpu_vendor",
        'canvas_hash'     => "varchar(64)  DEFAULT '' AFTER gpu_renderer",
        'audio_hash'      => "varchar(64)  DEFAULT '' AFTER canvas_hash",
        'screen_res'      => "varchar(50)  DEFAULT '' AFTER audio_hash",
        'hardware_cores'  => "int unsigned DEFAULT 0  AFTER screen_res",
        'math_hash'       => "varchar(64)  DEFAULT '' AFTER hardware_cores",
        'webrtc_ips'      => "varchar(255) DEFAULT '' AFTER math_hash",
        'media_devices'   => "varchar(100) DEFAULT '' AFTER webrtc_ips",
        'voices_hash'     => "varchar(64)  DEFAULT '' AFTER media_devices",
        'battery_info'    => "varchar(50)  DEFAULT '' AFTER voices_hash",
        'connection_type' => "varchar(50)  DEFAULT '' AFTER battery_info",
        'color_profile'   => "varchar(50)  DEFAULT '' AFTER connection_type",
        'accessibility'   => "varchar(100) DEFAULT '' AFTER color_profile",
        'form_fill_ms'    => "int unsigned DEFAULT 0  AFTER accessibility",
    );

    foreach ($required_columns as $col => $definition) {
        if (!in_array($col, $existing_cols)) {
            $wpdb->query("ALTER TABLE $table ADD COLUMN $col $definition");
        }
    }

    update_option('auctionforge_tracking_db_version', $current_version);
}

// Run the schema check on admin_init so it fires once after any plugin update
add_action('admin_init', 'auctionforge_maybe_upgrade_tracking_table');

function auctionforge_get_client_ip() {
    if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
        return sanitize_text_field($_SERVER['HTTP_CF_CONNECTING_IP']);
    }
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        return sanitize_text_field(trim($ips[0]));
    }
    if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
        return sanitize_text_field($_SERVER['HTTP_X_REAL_IP']);
    }
    return sanitize_text_field(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0');
}

/**
 * AJAX handler: log a tracking event (registration or login)
 * Deduplicates by (email, ip_address, device_id)
 */
function auctionforge_ajax_log_ip() {
    check_ajax_referer('auctionforge_ip_log', 'nonce');

    global $wpdb;
    $table_name = $wpdb->prefix . 'auctionforge_user_tracking';

    $ip = auctionforge_get_client_ip();
    $first_name = sanitize_text_field(isset($_POST['first_name']) ? $_POST['first_name'] : '');
    $last_name = sanitize_text_field(isset($_POST['last_name']) ? $_POST['last_name'] : '');
    $email = sanitize_email(isset($_POST['email']) ? $_POST['email'] : '');
    $event_type = sanitize_text_field(isset($_POST['event_type']) ? $_POST['event_type'] : 'register');
    $device_id = sanitize_text_field(isset($_POST['device_id']) ? $_POST['device_id'] : '');
    $device_fingerprint = sanitize_text_field(isset($_POST['device_fingerprint']) ? $_POST['device_fingerprint'] : '');
    
    // Advanced Fingerprint Metrics — Tier 1
    $gpu_vendor = sanitize_text_field(isset($_POST['gpu_vendor']) ? $_POST['gpu_vendor'] : '');
    $gpu_renderer = sanitize_text_field(isset($_POST['gpu_renderer']) ? $_POST['gpu_renderer'] : '');
    $canvas_hash = sanitize_text_field(isset($_POST['canvas_hash']) ? $_POST['canvas_hash'] : '');
    $audio_hash = sanitize_text_field(isset($_POST['audio_hash']) ? $_POST['audio_hash'] : '');
    $screen_res = sanitize_text_field(isset($_POST['screen_res']) ? $_POST['screen_res'] : '');
    $hardware_cores = intval(isset($_POST['hardware_cores']) ? $_POST['hardware_cores'] : 0);
    
    // Advanced Fingerprint Metrics — Tier 2
    $math_hash = sanitize_text_field(isset($_POST['math_hash']) ? $_POST['math_hash'] : '');
    $webrtc_ips = sanitize_text_field(isset($_POST['webrtc_ips']) ? $_POST['webrtc_ips'] : '');
    $media_devices = sanitize_text_field(isset($_POST['media_devices']) ? $_POST['media_devices'] : '');
    $voices_hash = sanitize_text_field(isset($_POST['voices_hash']) ? $_POST['voices_hash'] : '');
    $battery_info = sanitize_text_field(isset($_POST['battery_info']) ? $_POST['battery_info'] : '');
    $connection_type = sanitize_text_field(isset($_POST['connection_type']) ? $_POST['connection_type'] : '');
    $color_profile = sanitize_text_field(isset($_POST['color_profile']) ? $_POST['color_profile'] : '');
    $accessibility = sanitize_text_field(isset($_POST['accessibility']) ? $_POST['accessibility'] : '');
    $form_fill_ms = intval(isset($_POST['form_fill_ms']) ? $_POST['form_fill_ms'] : 0);
    
    $user_agent = sanitize_text_field(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '');

    if (!in_array($event_type, array('register', 'login', 'bid', 'terms_agreement', 'refund_agreement', 'chargeback_waiver'))) {
        $event_type = 'register';
    }

    if (empty($email)) {
        wp_send_json_error('No email provided');
        return;
    }

    $now = current_time('mysql');

    // INSERT or UPDATE on duplicate (same email + ip + device)
    $sql = $wpdb->prepare(
        "INSERT INTO $table_name (email, first_name, last_name, ip_address, device_id, device_fingerprint, gpu_vendor, gpu_renderer, canvas_hash, audio_hash, screen_res, hardware_cores, math_hash, webrtc_ips, media_devices, voices_hash, battery_info, connection_type, color_profile, accessibility, form_fill_ms, user_agent, first_seen, last_seen, event_count, last_event_type)
         VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s, %s, %s, 1, %s)
         ON DUPLICATE KEY UPDATE
            first_name = VALUES(first_name),
            last_name = VALUES(last_name),
            device_fingerprint = VALUES(device_fingerprint),
            gpu_vendor = VALUES(gpu_vendor),
            gpu_renderer = VALUES(gpu_renderer),
            canvas_hash = VALUES(canvas_hash),
            audio_hash = VALUES(audio_hash),
            screen_res = VALUES(screen_res),
            hardware_cores = VALUES(hardware_cores),
            math_hash = VALUES(math_hash),
            webrtc_ips = VALUES(webrtc_ips),
            media_devices = VALUES(media_devices),
            voices_hash = VALUES(voices_hash),
            battery_info = VALUES(battery_info),
            connection_type = VALUES(connection_type),
            color_profile = VALUES(color_profile),
            accessibility = VALUES(accessibility),
            form_fill_ms = VALUES(form_fill_ms),
            user_agent = VALUES(user_agent),
            last_seen = VALUES(last_seen),
            event_count = event_count + 1,
            last_event_type = VALUES(last_event_type)",
        $email, $first_name, $last_name, $ip, $device_id, $device_fingerprint, $gpu_vendor, $gpu_renderer, $canvas_hash, $audio_hash, $screen_res, $hardware_cores, $math_hash, $webrtc_ips, $media_devices, $voices_hash, $battery_info, $connection_type, $color_profile, $accessibility, $form_fill_ms, $user_agent, $now, $now, $event_type
    );

    $wpdb->query($sql);

    // Identity stitch: pair the anonymous af_vid (localStorage) with this
    // bidder's email so subsequent pageviews/searches/favourites can be
    // resolved to a person downstream. Pure observer of the existing
    // post-login tracking AJAX — runs after the auth event has already
    // landed and the original INSERT above is committed. Wrapped in a
    // try/catch so a missing class or DB hiccup never affects the
    // tracking response or, by extension, the bidder's session.
    if (!empty($_POST['visitor_hash']) && class_exists('AF_Stats_Visitor_Email_Map')) {
        try {
            $vh = sanitize_text_field((string) $_POST['visitor_hash']);
            // af_vid is a 32-char hex string. Anything else is junk we don't
            // want sneaking into the map.
            if (preg_match('/^[a-f0-9]{8,64}$/', $vh)) {
                AF_Stats_Visitor_Email_Map::record($vh, $email, $ip);
            }
        } catch (\Throwable $t) {
            // swallow — never block the tracking AJAX or the login flow
        }
    }

    wp_send_json_success(array('logged' => true));
}

/**
 * AJAX handler: search tracking logs (admin only)
 */
function auctionforge_ajax_search_ip_logs() {
    check_ajax_referer('auctionforge_admin', 'nonce');
    if (!current_user_can('manage_options')) {
        wp_send_json_error('Unauthorized');
        return;
    }

    global $wpdb;
    $table_name = $wpdb->prefix . 'auctionforge_user_tracking';

    $search = sanitize_text_field(isset($_POST['search']) ? $_POST['search'] : '');
    $per_page = intval(isset($_POST['per_page']) ? $_POST['per_page'] : 50);
    $page = intval(isset($_POST['page']) ? $_POST['page'] : 1);
    $offset = ($page - 1) * $per_page;

    $where = array();
    $params = array();

    if (!empty($search)) {
        $like = '%' . $wpdb->esc_like($search) . '%';
        $where[] = "(ip_address LIKE %s OR first_name LIKE %s OR last_name LIKE %s OR email LIKE %s OR device_id LIKE %s OR device_fingerprint LIKE %s)";
        $params[] = $like;
        $params[] = $like;
        $params[] = $like;
        $params[] = $like;
        $params[] = $like;
        $params[] = $like;
    }

    $event_filter = sanitize_text_field(isset($_POST['event_type']) ? $_POST['event_type'] : '');
    if (!empty($event_filter) && in_array($event_filter, array('register', 'login', 'bid'))) {
        $where[] = "last_event_type = %s";
        $params[] = $event_filter;
    }

    $where_sql = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';

    $count_sql = "SELECT COUNT(*) FROM $table_name $where_sql";
    if (!empty($params)) {
        $total = $wpdb->get_var($wpdb->prepare($count_sql, $params));
    } else {
        $total = $wpdb->get_var($count_sql);
    }

    $query = "SELECT * FROM $table_name $where_sql ORDER BY last_seen DESC LIMIT %d OFFSET %d";
    $all_params = array_merge($params, array($per_page, $offset));
    $results = $wpdb->get_results($wpdb->prepare($query, $all_params), ARRAY_A);

    // Find shared devices/IPs — flag entries where device_id or device_fingerprint matches other emails
    $flagged = array();
    foreach ($results as $row) {
        if (!empty($row['device_id'])) {
            $shared_device = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(DISTINCT email) FROM $table_name WHERE device_id = %s",
                $row['device_id']
            ));
            if ($shared_device > 1) {
                $flagged[$row['id']]['shared_device'] = intval($shared_device);
            }
        }
        if (!empty($row['device_fingerprint'])) {
            $shared_fp = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(DISTINCT email) FROM $table_name WHERE device_fingerprint = %s",
                $row['device_fingerprint']
            ));
            if ($shared_fp > 1) {
                $flagged[$row['id']]['shared_fingerprint'] = intval($shared_fp);
            }
        }
    }

    wp_send_json_success(array(
        'results'  => $results,
        'flagged'  => $flagged,
        'total'    => intval($total),
        'page'     => $page,
        'per_page' => $per_page,
        'pages'    => ceil(intval($total) / max($per_page, 1)),
    ));
}

/**
 * AJAX handler: look up all accounts linked to a device or IP
 */
function auctionforge_ajax_lookup_linked() {
    check_ajax_referer('auctionforge_admin', 'nonce');
    if (!current_user_can('manage_options')) {
        wp_send_json_error('Unauthorized');
        return;
    }

    global $wpdb;
    $table_name = $wpdb->prefix . 'auctionforge_user_tracking';

    $type = sanitize_text_field(isset($_POST['type']) ? $_POST['type'] : '');
    $value = sanitize_text_field(isset($_POST['value']) ? $_POST['value'] : '');

    if (empty($value)) {
        wp_send_json_error('No value provided');
        return;
    }

    $allowed = array('device_id', 'device_fingerprint', 'ip_address');
    if (!in_array($type, $allowed)) {
        wp_send_json_error('Invalid lookup type');
        return;
    }

    $results = $wpdb->get_results($wpdb->prepare(
        "SELECT * FROM $table_name WHERE $type = %s ORDER BY last_seen DESC",
        $value
    ), ARRAY_A);

    wp_send_json_success(array('results' => $results));
}

/**
 * AJAX handler: export as CSV (admin only)
 */
function auctionforge_ajax_export_ip_logs() {
    check_ajax_referer('auctionforge_admin', 'nonce');
    if (!current_user_can('manage_options')) {
        wp_die('Unauthorized');
    }

    global $wpdb;
    $table_name = $wpdb->prefix . 'auctionforge_user_tracking';

    $search = sanitize_text_field(isset($_GET['search']) ? $_GET['search'] : '');

    $where = array();
    $params = array();

    if (!empty($search)) {
        $like = '%' . $wpdb->esc_like($search) . '%';
        $where[] = "(ip_address LIKE %s OR first_name LIKE %s OR last_name LIKE %s OR email LIKE %s OR device_id LIKE %s OR device_fingerprint LIKE %s)";
        $params = array($like, $like, $like, $like, $like, $like);
    }

    $where_sql = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
    $query = "SELECT * FROM $table_name $where_sql ORDER BY last_seen DESC";

    if (!empty($params)) {
        $results = $wpdb->get_results($wpdb->prepare($query, $params), ARRAY_A);
    } else {
        $results = $wpdb->get_results($query, ARRAY_A);
    }

    header('Content-Type: text/csv');
    header('Content-Disposition: attachment; filename="user-tracking-' . date('Y-m-d') . '.csv"');

    $output = fopen('php://output', 'w');
    fputcsv($output, array('ID', 'Email', 'First Name', 'Last Name', 'IP Address', 'Device ID', 'Fingerprint', 'GPU Vendor', 'GPU Renderer', 'Canvas Hash', 'Audio Hash', 'Screen Res', 'Cores', 'Math Hash', 'WebRTC IPs', 'Media Devices', 'Voices Hash', 'Battery', 'Connection', 'Color Profile', 'Accessibility', 'Form Fill (ms)', 'User Agent', 'First Seen', 'Last Seen', 'Event Count', 'Last Event'));

    foreach ($results as $row) {
        fputcsv($output, array(
            $row['id'], $row['email'], $row['first_name'], $row['last_name'],
            $row['ip_address'], $row['device_id'], $row['device_fingerprint'],
            $row['gpu_vendor'], $row['gpu_renderer'], $row['canvas_hash'], 
            $row['audio_hash'], $row['screen_res'], $row['hardware_cores'],
            isset($row['math_hash']) ? $row['math_hash'] : '',
            isset($row['webrtc_ips']) ? $row['webrtc_ips'] : '',
            isset($row['media_devices']) ? $row['media_devices'] : '',
            isset($row['voices_hash']) ? $row['voices_hash'] : '',
            isset($row['battery_info']) ? $row['battery_info'] : '',
            isset($row['connection_type']) ? $row['connection_type'] : '',
            isset($row['color_profile']) ? $row['color_profile'] : '',
            isset($row['accessibility']) ? $row['accessibility'] : '',
            isset($row['form_fill_ms']) ? $row['form_fill_ms'] : 0,
            $row['user_agent'], $row['first_seen'], $row['last_seen'],
            $row['event_count'], $row['last_event_type'],
        ));
    }

    fclose($output);
    exit;
}

function auctionforge_register_ip_log_page() {
    add_submenu_page(
        'edit.php?post_type=lot',
        __('User Tracking', 'auctionforge'),
        __('User Tracking', 'auctionforge'),
        'manage_options',
        'auctionforge-ip-log',
        'auctionforge_render_ip_log_page'
    );
}

function auctionforge_render_ip_log_page() {
    require_once plugin_dir_path(dirname(__FILE__)) . 'admin/view/ip-log.php';
}
